diff --git a/.github/workflows/bytecode-compilers.yml b/.github/workflows/bytecode-compilers.yml new file mode 100644 index 00000000..73ef5435 --- /dev/null +++ b/.github/workflows/bytecode-compilers.yml @@ -0,0 +1,376 @@ +name: Build bytecode compilers + +# Builds the per-host bytecode compiler CLIs for every engine that supports +# ahead-of-time bytecode, by cloning each engine's UPSTREAM repo (not the +# android-runtime vendored sources) and building the CLI there. +# +# Output: one artifact per (engine, host), named `bytecode-compiler--`, +# each containing `//` so the downloads merge straight into +# tools/bytecode-compiler/bin/. +# +# The final `bundle` job merges them all into a single `bytecode-compiler-bin` +# artifact laid out exactly like tools/bytecode-compiler/bin/ — download it, unzip, +# and drop its contents into tools/bytecode-compiler/bin/ as-is. +# +# NOTE ON COMPATIBILITY: the produced bytecode must be loadable by the engine +# library the runtime ships. Hermes/PrimJS runtimes link prebuilt .so's and +# QuickJS/QuickJS-NG compile from vendored source, so the refs below must be kept +# in step with what the runtime actually bundles. Pin these to exact commits once +# validated instead of moving branches. +on: + workflow_dispatch: + inputs: + hermes_ref: + description: "facebook/hermes ref" + default: "10a40b5f1de81d007cba6afe567c4ba7a0b45f5d" + quickjs_ref: + # MUST match the quickjs submodule pin (BC_VERSION must agree with the + # runtime). See .gitmodules / scripts/vendor-engines-as-submodules.sh. + description: "bellard/quickjs ref (match the submodule pin)" + default: "04be246001599f5995fa2f2d8c91a0f198d3f34c" + quickjs_ng_ref: + # MUST match the quickjs_ng submodule pin (BC_VERSION must agree with the + # runtime, or JS_ReadObject rejects the blob). See .gitmodules / + # scripts/vendor-engines-as-submodules.sh. + description: "quickjs-ng/quickjs ref (match the submodule pin)" + default: "d950d55e950dd7994a96c669c31efa967b8b79f3" + primjs_ref: + description: "lynx-family/primjs ref" + default: "9c1fe150179b86f0ae5c6fbe670af7d76eca9df6" + +env: + HERMES_REPO: https://github.com/facebook/hermes + QUICKJS_REPO: https://github.com/bellard/quickjs + QUICKJS_NG_REPO: https://github.com/quickjs-ng/quickjs + PRIMJS_REPO: https://github.com/lynx-family/primjs + +jobs: + # --------------------------------------------------------------------------- + # Hermes — `hermesc` already emits a loadable HBC blob; no shim needed. + # --------------------------------------------------------------------------- + hermes: + name: hermes (${{ matrix.host }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - { host: linux-x64, runner: ubuntu-24.04 } + - { host: linux-arm64, runner: ubuntu-24.04-arm } + - { host: darwin-x64, runner: macos-15-intel } + - { host: darwin-arm64, runner: macos-14 } + - { host: win32-x64, runner: windows-2022 } + # win32-arm64 dropped: hermes' Boost.Context ARM64 assembler + CMake's + # ASM_ARMASM linker support fail on windows-11-arm. The win32-x64 + # hermesc runs on ARM Windows via x64 emulation (HBC output is portable). + steps: + - name: Install toolchain (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y cmake ninja-build build-essential python3 + - name: Install toolchain (macOS) + if: runner.os == 'macOS' + run: brew install cmake ninja + - name: Install toolchain (Windows) + if: runner.os == 'Windows' + run: choco install ninja -y + + - name: Clone Hermes + shell: bash + # Fetch by ref so an exact commit SHA works (git clone --branch only + # accepts branch/tag names). GitHub allows fetching arbitrary SHAs. + run: | + set -euxo pipefail + ref="${{ github.event.inputs.hermes_ref || 'master' }}" + git init hermes + git -C hermes remote add origin "$HERMES_REPO" + git -C hermes fetch --depth 1 origin "$ref" + git -C hermes -c advice.detachedHead=false checkout FETCH_HEAD + + - name: Build hermesc (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + set -euxo pipefail + cmake -S hermes -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DHERMES_BUILD_APPLE_FRAMEWORK=OFF + cmake --build build --target hermesc + + - name: Build hermesc (Windows) + if: runner.os == 'Windows' + shell: bash + run: | + set -euxo pipefail + # Use the default (Visual Studio) generator; Ninja+MSVC also works if the + # VS dev environment is on PATH. + cmake -S hermes -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build --target hermesc --config Release + + - name: Stage binary + shell: bash + run: | + set -euxo pipefail + mkdir -p "out/hermes/${{ matrix.host }}" + bin="$(find build -type f \( -name hermesc -o -name hermesc.exe \) | head -n1)" + test -n "$bin" + cp "$bin" "out/hermes/${{ matrix.host }}/" + + - uses: actions/upload-artifact@v4 + with: + name: bytecode-compiler-hermes-${{ matrix.host }} + # Root the artifact at `out/` so it carries the full + # `hermes//` path and merges straight into bin/. + path: out + if-no-files-found: error + + # --------------------------------------------------------------------------- + # QuickJS (bellard) — build via its own Makefile (libquickjs.a, which injects + # -D_GNU_SOURCE -DCONFIG_VERSION), then link our blob-emitting shim. + # win32-x64 is CROSS-compiled on Linux via bellard's CONFIG_WIN32 (mingw-w64); + # win32-arm64 is not possible (mingw-w64 targets x86 only). + # --------------------------------------------------------------------------- + quickjs: + name: quickjs (${{ matrix.host }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - { host: linux-x64, runner: ubuntu-24.04 } + - { host: linux-arm64, runner: ubuntu-24.04-arm } + - { host: darwin-x64, runner: macos-15-intel } + - { host: darwin-arm64, runner: macos-14 } + - { host: win32-x64, runner: ubuntu-24.04 } # mingw cross-compile + steps: + - uses: actions/checkout@v4 + - name: Install toolchain (Linux) + if: runner.os == 'Linux' + run: | + set -euxo pipefail + sudo apt-get update + sudo apt-get install -y build-essential + # mingw-w64 for the win32-x64 cross build. + if [ "${{ matrix.host }}" = "win32-x64" ]; then sudo apt-get install -y mingw-w64; fi + # macOS: make + clang ship with the Xcode command-line tools. + + - name: Clone QuickJS + shell: bash + run: | + set -euxo pipefail + ref="${{ github.event.inputs.quickjs_ref || 'master' }}" + git init quickjs + git -C quickjs remote add origin "$QUICKJS_REPO" + git -C quickjs fetch --depth 1 origin "$ref" + git -C quickjs -c advice.detachedHead=false checkout FETCH_HEAD + + - name: Build lib + shim + shell: bash + env: + SHIM: ${{ github.workspace }}/tools/bytecode-compiler/native/qjs-compile.c + run: | + set -euxo pipefail + cd quickjs + jobs="$(getconf _NPROCESSORS_ONLN)" + out="../out/quickjs/${{ matrix.host }}"; mkdir -p "$out" + if [ "${{ matrix.host }}" = "win32-x64" ]; then + # CONFIG_WIN32=y selects the x86_64-w64-mingw32- cross toolchain. + make -j"$jobs" CONFIG_WIN32=y libquickjs.a + # bellard's win32 LIBS are "-lm -lpthread" (winpthreads provides the + # pthread_* and clock_gettime symbols quickjs.c uses). + x86_64-w64-mingw32-gcc -O2 -DNSBC_MAGIC='"NSBCQJS"' -I. \ + -o "$out/nsbc-quickjs.exe" "$SHIM" libquickjs.a -lm -lpthread + # Cross-built Windows binary — not runnable on the Linux host. + else + # Use bellard's own Makefile so CONFIG_VERSION / _GNU_SOURCE and the + # correct source set come from upstream. + make -j"$jobs" libquickjs.a + cc -O2 -DNSBC_MAGIC='"NSBCQJS"' -I. -o "$out/nsbc-quickjs" "$SHIM" libquickjs.a -lm + "$out/nsbc-quickjs" || true # usage line (exit 2) proves it links & runs + fi + + - uses: actions/upload-artifact@v4 + with: + name: bytecode-compiler-quickjs-${{ matrix.host }} + # Root the artifact at `out/` so it carries the full + # `quickjs//` path and merges straight into bin/. + path: out + if-no-files-found: error + + # --------------------------------------------------------------------------- + # QuickJS-NG — CMake build of libqjs, then our shim (same JS_* API). + # --------------------------------------------------------------------------- + quickjs-ng: + name: quickjs-ng (${{ matrix.host }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - { host: linux-x64, runner: ubuntu-24.04 } + - { host: linux-arm64, runner: ubuntu-24.04-arm } + - { host: darwin-x64, runner: macos-15-intel } + - { host: darwin-arm64, runner: macos-14 } + - { host: win32-x64, runner: windows-2022 } + # - { host: win32-arm64, runner: windows-11-arm } + steps: + - uses: actions/checkout@v4 + - name: Install toolchain (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y cmake ninja-build build-essential + - name: Install toolchain (macOS) + if: runner.os == 'macOS' + run: brew install cmake ninja + - name: Install toolchain (Windows) + if: runner.os == 'Windows' + run: choco install ninja llvm -y + + - name: Clone QuickJS-NG + shell: bash + run: | + set -euxo pipefail + ref="${{ github.event.inputs.quickjs_ng_ref || 'master' }}" + git init quickjs-ng + git -C quickjs-ng remote add origin "$QUICKJS_NG_REPO" + git -C quickjs-ng fetch --depth 1 origin "$ref" + git -C quickjs-ng -c advice.detachedHead=false checkout FETCH_HEAD + + - name: Build lib + shim + shell: bash + env: + SHIM: ${{ github.workspace }}/tools/bytecode-compiler/native/qjs-compile.c + run: | + set -euxo pipefail + # Build the shim as a CMake target so it uses the SAME toolchain + CRT + # as the engine lib. Linking a separately-compiled (clang) shim against + # the MSVC-built qjs.lib fails with __imp_*/CRT-mismatch link errors on + # Windows. The 'qjs' target propagates its public include dirs and libm. + cp "$SHIM" quickjs-ng/nsbc-quickjs-ng.c + cat >> quickjs-ng/CMakeLists.txt <<'CMAKE' + + add_executable(nsbc-quickjs-ng nsbc-quickjs-ng.c) + target_link_libraries(nsbc-quickjs-ng PRIVATE qjs) + target_compile_definitions(nsbc-quickjs-ng PRIVATE NSBC_MAGIC=\"NSBCNGS\") + CMAKE + cmake -S quickjs-ng -B quickjs-ng/build -DCMAKE_BUILD_TYPE=Release + cmake --build quickjs-ng/build --config Release --target nsbc-quickjs-ng + out="out/quickjs-ng/${{ matrix.host }}"; mkdir -p "$out" + bin="$(find quickjs-ng/build -type f \( -name 'nsbc-quickjs-ng' -o -name 'nsbc-quickjs-ng.exe' \) | head -n1)" + test -n "$bin"; cp "$bin" "$out/" + [ "${{ runner.os }}" = "Windows" ] || "$out/nsbc-quickjs-ng" || true + + - uses: actions/upload-artifact@v4 + with: + name: bytecode-compiler-quickjs-ng-${{ matrix.host }} + # Root the artifact at `out/` so it carries the full + # `quickjs-ng//` path and merges straight into bin/. + path: out + if-no-files-found: error + + # --------------------------------------------------------------------------- + # PrimJS — build via its own toolchain: `hab sync` fetches gn + ninja + a + # bundled clang + sysroot, then GN/ninja build the engine. We add our LEPUS_* + # shim as a sibling GN executable (reusing the qjs-cli engine deps). + # + # Unix only, and NOT linux-arm64: PrimJS's envsetup.sh aborts on Windows, and + # its DEPS only provides a linux sysroot for x86_64. So: linux-x64 + macOS. + # + # Validated locally on darwin-arm64 (hab sync -> gn gen -> ninja nsbc-primjs + # produces a working CLI). Linux uses the same flow. + # --------------------------------------------------------------------------- + primjs: + name: primjs (${{ matrix.host }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - { host: linux-x64, runner: ubuntu-24.04 } + - { host: darwin-x64, runner: macos-15-intel } + - { host: darwin-arm64, runner: macos-14 } + steps: + - uses: actions/checkout@v4 + - name: Install prerequisites (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y python3 curl + + - name: Clone PrimJS + shell: bash + run: | + set -euxo pipefail + ref="${{ github.event.inputs.primjs_ref || 'develop' }}" + git init primjs + git -C primjs remote add origin "$PRIMJS_REPO" + git -C primjs fetch --depth 1 origin "$ref" + git -C primjs -c advice.detachedHead=false checkout FETCH_HEAD + + - name: Add shim as a GN target + shell: bash + env: + SHIM: ${{ github.workspace }}/tools/bytecode-compiler/native/primjs-compile.c + run: | + set -euxo pipefail + # Build as C++ (.cc); the LEPUS_* API is extern "C" so linkage is fine. + cp "$SHIM" primjs/tools/qjs-cli/nsbc-primjs.cc + # Add the target to the ROOT BUILD.gn (always loaded, so it's always + # generated) linking ONLY the core engine (//src:quickjs_lib). We avoid + # qjs_wasm_binding — its WASM/JSC code doesn't build under the bundled + # toolchain's -Werror, and our shim doesn't need it. (Validated locally.) + cat >> primjs/BUILD.gn <<'GN' + + executable("nsbc-primjs") { + cflags = [ "-Wno-c99-designator" ] + sources = [ "//tools/qjs-cli/nsbc-primjs.cc" ] + include_dirs = [ "//include" ] + deps = [ "//src:quickjs_lib" ] + } + GN + + - name: Build (hab sync + gn + ninja) + shell: bash + working-directory: primjs + run: | + set -euxo pipefail + # hab (in-repo) fetches gn, ninja, the bundled clang toolchain + sysroot. + source tools/envsetup.sh + hab sync . + gn gen out/Default --args="is_debug=false" + ninja -C out/Default nsbc-primjs + out="../out/primjs/${{ matrix.host }}"; mkdir -p "$out" + cp out/Default/nsbc-primjs "$out/nsbc-primjs" + "$out/nsbc-primjs" || true # usage line (exit 2) proves it links & runs + + - uses: actions/upload-artifact@v4 + with: + name: bytecode-compiler-primjs-${{ matrix.host }} + # Root the artifact at `out/` so it carries the full + # `primjs//` path and merges straight into bin/. + path: out + if-no-files-found: error + + # --------------------------------------------------------------------------- + # Bundle — merge every per-(engine, host) artifact into a single artifact laid + # out exactly like tools/bytecode-compiler/bin/, ready to download-and-paste. + # --------------------------------------------------------------------------- + bundle: + name: bundle bin/ + runs-on: ubuntu-24.04 + needs: [hermes, quickjs, quickjs-ng, primjs] + # Run even if some engine/host builds failed, so a partial bin/ is still + # published rather than nothing. + if: always() + steps: + - name: Download all compiler artifacts + uses: actions/download-artifact@v4 + with: + # Every per-job artifact already carries an `//` + # path; merge-multiple overlays them into one tree = the bin/ layout. + pattern: bytecode-compiler-* + path: bin + merge-multiple: true + + - name: Show merged layout + run: find bin -type f | sort + + - uses: actions/upload-artifact@v4 + with: + name: bytecode-compiler-bin + path: bin + if-no-files-found: error diff --git a/.github/workflows/npm_trusted_release.yml b/.github/workflows/npm_trusted_release.yml index f3164a10..6d83b594 100644 --- a/.github/workflows/npm_trusted_release.yml +++ b/.github/workflows/npm_trusted_release.yml @@ -77,7 +77,7 @@ jobs: set -euo pipefail case "$ENGINE" in all) - echo 'engines=["v8","hermes","jsc","quickjs","quickjs-ng","shermes","primjs"]' >> "$GITHUB_OUTPUT" + echo 'engines=["v8","hermes","jsc","quickjs","quickjs-ng","primjs"]' >> "$GITHUB_OUTPUT" ;; v8|hermes|jsc|quickjs|quickjs-ng|shermes|primjs) printf 'engines=["%s"]\n' "$ENGINE" >> "$GITHUB_OUTPUT" @@ -123,6 +123,8 @@ jobs: distribution: "temurin" - name: Install root dependencies run: npm install + - name: Set up engine submodules, patches, and jsparser + run: npm run setup - name: Resolve engine target id: target shell: bash @@ -200,9 +202,6 @@ jobs: echo "NPM_VERSION=$NPM_VERSION" >> "$GITHUB_OUTPUT" echo "NPM_TAG=$NPM_TAG" >> "$GITHUB_OUTPUT" echo "Resolved $PACKAGE_NAME@$NPM_VERSION (tag: $NPM_TAG)" - - name: Install jsparser dependencies - working-directory: ${{ env.JS_PARSER_DIR }} - run: npm ci - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build (-Pengine=${{ steps.target.outputs.GRADLE_ENGINE }}) diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f75532ef --- /dev/null +++ b/.gitmodules @@ -0,0 +1,8 @@ +[submodule "test-app/runtime/src/main/cpp/napi/quickjs/source"] + path = test-app/runtime/src/main/cpp/napi/quickjs/source + url = https://github.com/bellard/quickjs + ignore = dirty +[submodule "test-app/runtime/src/main/cpp/napi/quickjs/source_ng"] + path = test-app/runtime/src/main/cpp/napi/quickjs/source_ng + url = https://github.com/quickjs-ng/quickjs + ignore = dirty diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 9089549d..28f7bee1 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,25 +1,43 @@ { - "configurations": [ - { - "name": "Android", - "includePath": [ - "${workspaceFolder}/**", - "/Users/ammarahmed/Library/Android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/darwin-x86_64/**", - "${workspaceFolder}/test-app/runtime/src/main/cpp/jsc/include" - ], - "defines": [], - "compilerPath": "/Users/ammarahmed/Library/Android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang-17", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "clang-x86", - "browse": { - "path": [ - "/Users/ammarahmed/Library/Android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/darwin-x86_64", - "${workspaceFolder}/test-app/runtime/src/main/cpp/jsc/include" - ], - "limitSymbolsToIncludedHeaders": false - } - } - ], - "version": 4 -} \ No newline at end of file + "configurations": [ + { + "name": "Android", + "compilerPath": "/Users/ammarahmed/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang++", + "compilerArgs": ["-std=c++20"], + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "linux-clang-arm64", + "defines": [ + "USE_HOST_OBJECT=true", + "NAPI_EXPERIMENTAL=true", + "__BIONIC__=1", + "__V8__", + "__V8_13__" + ], + "includePath": [ + "/Users/ammarahmed/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/c++/v1", + "/Users/ammarahmed/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include", + "/Users/ammarahmed/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/aarch64-linux-android", + "/Users/ammarahmed/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/18/include", + "${workspaceFolder}/test-app/runtime/src/main/cpp", + "${workspaceFolder}/test-app/runtime/src/main/cpp/zip/include", + "${workspaceFolder}/test-app/runtime/src/main/cpp/runtime/**", + "${workspaceFolder}/test-app/runtime/src/main/cpp/modules/**", + "${workspaceFolder}/test-app/runtime/src/main/cpp/napi/common", + "${workspaceFolder}/test-app/runtime/src/main/cpp/napi/v8", + "${workspaceFolder}/test-app/runtime/src/main/cpp/napi/v8/v8_inspector", + "${workspaceFolder}/test-app/runtime/src/main/cpp/napi/v8-13", + "${workspaceFolder}/test-app/runtime/src/main/cpp/napi/v8-13/include", + "${workspaceFolder}/test-app/runtime/src/main/cpp/napi/v8-13/v8_inspector" + ], + "browse": { + "path": [ + "${workspaceFolder}/test-app/runtime/src/main/cpp", + "/Users/ammarahmed/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include" + ], + "limitSymbolsToIncludedHeaders": true + } + } + ], + "version": 4 +} diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 00000000..9aa3b4e5 --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,73 @@ +# V8 → NAPI runtime migration ledger + +Tracks the port of substantive commits from the old V8-based runtime +(`nativescript/android-runtime`, branch `master`) into this NAPI-based runtime, +starting at `2bab8f5` (`fix(URL): allow undefined 2nd args`, #1826) through the +old runtime's HEAD (129 commits total). + +Plan: `~/.claude/plans/we-want-to-migrate-modular-prism.md` + +**Status:** the full range `2bab8f5..HEAD` has been triaged. ~60 `build(deps)`/CI/`chore`/`release` +commits are out of scope (fork tooling). Of the substantive commits: **23 ported/partial** +(all native/Java build-verified), several **already-present**, and the rest **deferred** with +rationale (each deferred row says why + what's needed to complete it). Deferred clusters: +**ESM** (`052cb21` + its dependents `7782720f`/`288491f`/`5ceb3d4`/`92c2654`/`45ed1f6`), +**timers** (`bfd7650`), **URLSearchParams spec** (`89893ae`), **workers→C++** (`a84d3c7`), +**inspector** (`55da2da`/`4b5ab0a`), **NDK r27d** (`0387a8d`). The **@CriticalNative** pair +(`085bc4f`+`3c956cf`) and **DexFactory** pair (`c9d41e6`+`fce8e29`) are now **ported** (compile-verified; +they carry device/version/release-only runtime risk — validate on real devices). Verification here is +compile/link + config-eval; on-device behavior is noted per-row where relevant. + +**Disposition:** `ported` · `already-present` · `partial` · `skipped` · `deferred` + +| # | old hash | subject | disposition | new commit | notes | +|---|----------|---------|-------------|------------|-------| +| 1 | 2bab8f5 | fix(URL): allow undefined 2nd args (#1826) | ported | ef20f1d | `URL::New` (modules/url/URL.cpp): gate base-URL branch on `argv[1]` not being undefined/null via `napi_util::is_of_type`; ported expanded `testURLImpl.js` verbatim (JS is engine-agnostic). Verified: native recompile (V8-13, all ABIs) exit 0. | +| 2 | 94ddb15 | fix: `exit(0)` causes ANR due to destroyed mutex (#1820) | ported | b46586b | Old fix was in `MetadataNode::BuildMetadata`; in napi runtime that error path moved to `MetadataBuilder.cpp:55` (identical direct-boot/locked-screen comment). Changed `exit(0)` → `_Exit(0)`. Verified: native recompile exit 0. | +| — | f290ed2 | perf: optimizations around generating JS classes from Metadata (#1824) | already-present | — | Large metadata-subsystem perf refactor (13 files). Confirmed by user: already implemented in the napi runtime's rewritten metadata code. Skipped. | +| 3 | a983931 | fix: gradle error when compileSdk or targetSdk is provided (#1825) | ported | 6f01665 | `test-app/app/build.gradle`: add `as int` cast to provided `compileSdk`/`targetSdk` in `computeCompileSdkVersion`/`computeTargetSdkVersion` (props arrive as String from `-P`). Verified: `:app:help -PcompileSdk=35 -PtargetSdk=35` config eval exit 0. | +| — | e293636 | fix: inner type not set when companion object defined as function (#1831) | already-present | — | New `MetadataNode::SetInnerTypes` (metadata/MetadataNode.cpp:1726-1733) already has the `napi_has_own_property` + `if(!hasOwnProperty)` guard before defining the inner-type accessor — napi port of the same fix. Skipped. | +| 5 | b31fc5f + 3633aed + 83f611b + 3513ce7 + 45fb275 | Ada v3 + URLPattern (#1830), Ada 3.1.1/3.1.3/3.2.7/3.3.0 (#1832/#1835/#1884) | ported | this commit | **Consolidated the whole Ada v3 chain** (Ada is a vendored single-file lib, so intermediate bumps collapse to the final): replaced `modules/url/ada/ada.{h,cpp}` 2.9.0→3.3.0 (verified no API breaks in URL/URLSearchParams). **Ported URLPattern** V8→napi: new `modules/url/URLPattern.{cpp,h}` with a `napi_regex_provider` (RegExp via global ctor + `.exec`, move-only `NapiRegex` ref wrapper, thread-local env for `create_instance`); wired `URLPattern::Init` into `NSRuntimeModules`. Registered spec-correct `hasRegExpGroups` (old C++ had typo `hasRegexpGroups`). Ported `testURLPattern.js` + registered in mainpage.js. Skipped the commit's gradle/CMake/Runtime-V8 bits (superseded / engine-specific). Verified: native build all ABIs exit 0. | +| 6 | bec401c | feat: NDK 27 and Support for Java 21 (#1819) | partial | this commit | **Ported (Java-21 source compat):** `NanoWSD.java` byte casts (`header |= (byte)…`); `NativeScriptAbstractMap` `SimpleEntry`/`SimpleImmutableEntry` gain `` bounds; generator `build.gradle` ×2 `'17'`→`JavaVersion.VERSION_17`; add `compileOptions VERSION_17` to runtime module. **Already-present:** `-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON` (runtime/build.gradle:173), armeabi/x86 android_support block already removed. **Skipped (superseded/N-A to fork):** CI workflows, `gradlew`/wrapper (fork on gradle 8.14.3 > 8.9), root `package.json` version_info, dts-generator submodule bump. Verified: `:runtime:compileDebugJavaWithJavac` under **JDK 21** exit 0. | +| — | c9a2a86 | feat: update Gradle 8.14.3 and Android build tools 8.12.1 (#1834) | already-present | — | Fork already on gradle wrapper 8.14.3 and `NS_DEFAULT_ANDROID_BUILD_TOOLS_VERSION=8.12.1`. Skipped. | +| — | e98367c | feat: performance api (#1838) | already-present | — | `runtime/performance/Performance.h` registers `global.performance`/`performance.now()` (installed at Runtime.cpp:313). Skipped. | +| 7 | 87f7f9c | feat: update libzip to 1.11.4 (#1845) | ported | this commit | Vendored binary dep. Replaced `cpp/zip/include/{zip.h,zipconf.h}` and the 4 prebuilt `libs/common//libzip.a` with upstream 1.11.4 (fork's zipconf was mislabeled "0.11"). Sole consumer `AssetExtractor.cpp` uses only stable API (zip_open/fread/stat_index/…). Verified: native compile+link all ABIs exit 0. **Caveat:** binary swap verified to link only — on-device asset extraction should be smoke-tested. | +| — | 0387a8d | feat: update NDK to latest LTS (r27d) (#1846) | deferred | — | Portable change is `defaultNdkVersion` 27.1.12297006 → **27.3.13750724** in runtime/build.gradle. Deferred: r27d is NOT installed in this env (only 27.0–27.2), and AGP's `ndkVersion defaultNdkVersion` would fail the build (blocking verification of later commits). **Action for user:** `sdkmanager "ndk;27.3.13750724"`, then bump `defaultNdkVersion`. CI/package.json NDK bumps skipped (fork-specific/superseded). | +| 8 | 78589fd | feat: kotlin 2.2.x support (#1848) | ported | this commit | `gradle.properties`: `ns_default_kotlin_version` + `ns_default_kotlinx_metadata_jvm_version` 2.0.0 → 2.2.20. `KotlinClassDescriptor.kt`: kotlinx-metadata 2.2.x renamed the API — `metaClass.enumEntries` → `metaClass.kmEnumEntries.map { it.name }` (`getEnumEntriesAsFields` already takes `Collection`). Verified: metadata-generator `compileKotlin` under 2.2.20 exit 0 + `:app:buildMetadata` runs the generator successfully. | +| 9 | f033061 | feat: queueMicrotask support (#1868) | ported | this commit | Registered global `queueMicrotask` in `Runtime::Init` (after Performance). The V8 original used `isolate->EnqueueMicrotask`; napi has no equivalent, so implemented engine-agnostically via `Promise.resolve().then(cb)` — preserves microtask ordering with promises (`['qm1','p','qm2']` case) and runs before timers. Validates the arg is a function (TypeError otherwise). Ported `testQueueMicrotask.js` + mainpage.js. Verified: native compile exit 0. **Caveat:** ordering/timing asserts not run on-device here. | +| — | 052cb21 | feat: ES modules (ESM) support (#1836) | deferred | — | **Large (26 files).** Analyzed in depth. Upstream ESM is **100% V8-native** (`v8::Module`, `ScriptCompiler::CompileModule`, `InstantiateModule`, `Evaluate`, `GetModuleNamespace`, isolate `SetHostInitializeImportMetaObjectCallback` / `SetHostImportModuleDynamicallyCallback`; ~650-line ModuleInternalCallbacks.cpp). **No napi ESM API exists**, so options are: (a) V8-only native via the napi↔v8 bridge (works only on V8 engine); (b) **engine-agnostic load-time ESM→CJS transpilation** (recommended if pursued: es-module-lexer + source rewrite + import.meta/dynamic-import polyfills through existing CommonJS path; live-binding snapshot caveat). Portable non-native parts (all engines): `Module.java` `.mjs` resolution (.js→.mjs order, index.mjs), `AppConfig.logScriptLoading` + `Runtime.getLogScriptLoadingEnabled`, SBG `generateUniqueFileIdentifier` (MD5, collision-safe for .mjs) + `isJsFile` .mjs, jsparser `sourceType:'module'` for .mjs. **Deferred by user pending design decision.** Related `7782720f` (http esm realms + HMR) also depends on this. | +| 10 | 5cb66ee | fix: prevent crash when jweak points to null (#1881) | ported | this commit | Engine-agnostic JNI/cache fix, applied to the same files under new paths. `jni/JEnv.h`: add `isSameObject`. `jni/LRUCache.h`: add optional `cacheValidCallback` (checked on lookup → evict stale entries via new `evictKey`) + member. `objectmanager/ObjectManager`: add `ValidateWeakGlobalRefCallback` (`!isSameObject(obj, NULL)`) and pass it to the weak-ref `m_cache`. Skipped upstream's cosmetic robin_hood/emplace swap (new LRUCache uses `std::unordered_map`). Verified: native compile all ABIs exit 0. | +| — | 7782720f | feat: http loaded es module realms + HMR DX enrichments (#1883) | deferred | — | Builds directly on the deferred ESM feature (modifies the V8 `ModuleInternalCallbacks`, adds HMRSupport + DevFlags on top of ESM realms, `testESModules.mjs`). Deferred together with [[052cb21]] ESM. | +| — | 45fb275 | feat: Ada 3.3.0 (#1884) | already-present | — | Covered by the consolidated Ada v3 upgrade in commit `8109ff9` (fork now on Ada 3.3.0). | +| 11 | c05e283 | fix: improve reThrowToJava exception handling and runtime retrieval (#1886) | ported | this commit | Made `ObjectManager::GetClassName` (both overloads) + its `JAVA_LANG_CLASS`/`GET_NAME_METHOD_ID` JNI ids **static** (added out-of-class defs), so `NativeScriptException` can resolve a Java class name during rethrow **without** `Runtime::GetRuntime(env)->GetObjectManager()` (avoids a throw-within-rethrow when the env/runtime is unavailable). Updated the 3 class-name-only sites in NativeScriptException; left `GetJavaExceptionFromEnv` (needs the instance). Skipped upstream's clang-format churn of ObjectManager. Runtime-retrieval robustness (`.at`→`.find`) already present in napi `GetRuntime`. Verified: native compile+link all ABIs exit 0. | +| 12 | 3ecd707 | fix: proguard builds (#1887) | ported | this commit | Comment out `buildMetadata.finalizedBy(currentTask)` for `minify*WithR8` (app/build.gradle:1181); add `consumerProguardFiles 'consumer-rules.pro'` to runtime defaultConfig; new `runtime/consumer-rules.pro` (keep `com.tns.*`, `com.tns.gen.**`, `com.tns.internal.**` + RuntimeVisibleAnnotations). Verified: gradle config eval exit 0. **Caveat:** full effect only observable in a release R8/minify build. | +| — | 288491f | fix: http realm cache key with query params (#1896) | deferred | — | Touches only `HMRSupport.cpp` + `ModuleInternalCallbacks.cpp` — both part of the deferred ESM / http-module-realms work ([[052cb21]], [[7782720f]]). Deferred with them. | +| 13 | 3e61cef | fix: URLSearchParams.forEach() crash and spec compliance (#1895) | partial | this commit | New `ForEach` already had spec arg order `(value, key, searchParams)`, thisArg, and stop-on-throw. Only remaining bug: iterated with `get_keys()`+`get(key)` (returns first value for duplicate keys) → switched to `get_entries()` structured-binding loop (`?a=1&a=2` now yields both). Ported the 4 forEach unit tests. Verified: native compile all ABIs exit 0. | +| — | 5ceb3d4 | feat: remote module security (#1899) | deferred | — | Secures the http-loaded module realms; C++ lands in `DevFlags.cpp/h` + `HMRSupport.cpp` + `ModuleInternalCallbacks.cpp` — none of which exist in the fork (they come from the deferred [[7782720f]] / [[052cb21]]). Java `AppConfig`/`Runtime` additions only make sense with that feature present. Deferred with ESM. | +| 14 | e924542 | feat: improved error logging for NativeScript exceptions (#1908) | partial | this commit | **Ported:** the engine-agnostic `std::set_terminate(LogAndAbortUncaught)` handler in `Runtime::Init(JavaVM)` — logs an uncaught native exception (message via new `NativeScriptException::what()`) before `_Exit`, instead of a bare abort. **Skipped:** the 726-line V8-based `NativeScriptException.cpp/h` logging rewrite — the fork's napi NativeScriptException already builds messages/stacks its own way (`GetErrorMessage`/`GetErrorStackTrace`/`GetFullMessage`/`PrintErrorMessage`) and the old logic is V8-stack-specific; not a clean map. Verified: native compile all ABIs exit 0. | +| 15 | 1fd144f | fix: multithreadedJS should use concurrent java maps (#1920) | ported | this commit | Broadened `Runtime.java` instance-map fields (`strongInstances`, `weakInstances`, `strongJavaObjectToID`, `weakJavaObjectToID`, `loadedJavaScriptExtends`) from concrete types to the `Map<>` interface (only Map methods are used; `NativeScriptHashMap` implements Map), and in the `Runtime(config, dynamicConfig)` ctor swap them to `ConcurrentHashMap`/`Collections.synchronizedMap(...)` when `getEnableMultithreadedJavascript()`. Ported `ConcurrentAccessTest.java` + `testConcurrentAccess.js` + mainpage require. Verified: `:runtime:` + `:app:compileDebugJavaWithJavac` exit 0. **Caveat:** concurrency behavior is device-only. | +| 22 | 085bc4f + 3c956cf | feat: @CriticalNative/@FastNative on safe methods (#1921) + optimized native method registration for Android 8-11 (#1942) | ported | this commit | Combined end-state (`3c956cf` supersedes `085bc4f`). **Java** (`Runtime.java`): `SUPPORTS_OPTIMIZED_NATIVE = SDK_INT>=26`; each hot native now has an annotated variant + a `*Legacy` variant + a dispatcher — `@CriticalNative generateNewObjectIdCritical`/`getCurrentRuntimeIdCritical`/`getPointerSizeCritical` (static, no-arg/primitive) and `@FastNative notifyGcFast`/`setManualInstrumentationModeFast`. **C++** (`com_tns_Runtime.cpp`): `RegisterOptimizedNatives` binds the critical/fast impls via `RegisterNatives` in `JNI_OnLoad` (dynamic lookup of annotated methods is broken on API 26-30); `*Legacy` externs auto-bind for older/ fallback. `generateNewObjectId` made static-safe via `GenerateNewObjectId(nullptr,nullptr)` (ignores env/obj); `getCurrentRuntimeId` uses `Runtime::Current()` (no JNI). notifyGc keeps the fork's `(int,int[])` → `"(I[I)Z"`. Verified: native+Java compile all ABIs exit 0. **Caveat:** ART calling-convention correctness is device-only — validate on Android 8-11 + 12+ and a release build. | +| 16 | df4e81b | fix: select correct runtime when calling from different threads (#1906) | ported | this commit | **Headline fix:** in `Runtime.callJSMethod(int runtimeId, …)`, re-select via `getObjectRuntime(javaObject)` not only when the id misses but also when the found runtime doesn't own the object (`runtime.getJavaObjectID(javaObject) == null`) — fixes a worker invoking a method on an object created in another runtime. **Diagnostic:** wrapped `GetJavaObjectByID` in `ObjectManager::GetJavaObjectByJsObject` with a clearer error (id + original message via the `what()` added in [[c05e283]]/#1908 port). Adapted to the fork's runtimeId-based dispatch; skipped upstream's NSE `ToString()`/`GetErrorMessage()` (fork already has its own message infra + `what()`). Verified: native+Java compile exit 0. | +| 17 | 54185ff | [jsparser]: disable sourcemap (#1796) | ported | this commit | `jsparser/webpack.config.js`: `devtool: false`. | +| 18 | e963d6c | fix: jsparser report webpack failure (#1797) | ported | this commit | `jsparser/js_parser.js`: `traverseFiles` throws when no files found (empty = broken webpack build) instead of silently succeeding. | +| 19 | dd2984b | fix(jsparser): skip non-Identifier keys in `.extend({})` (#1950) | ported | this commit | `jsparser/visitors/es5-visitors.js`: added `_getIdentifierKeyName` helper; both `.extend` property loops now skip spreads/computed/non-Identifier keys instead of crashing (e.g. bundled Zod `.extend({ ...other })`). Verified: jsparser `npm run build` (webpack) compiles clean (build/ is gitignored, rebuilt on demand). | +| 20 | 49da71b | fix: circular dependencies when using proguard (#1910) | ported | this commit | Comment out the `buildMetadata` task's `dependsOn compile*ArtProfile` and `dependsOn optimize*Resources` (app/build.gradle) — they created a circular task dependency under proguard/R8. Verified: `:app:help` config eval exit 0. **Caveat:** full effect only in a release R8 build. | +| 21 | 2b6cb03 | fix: ensure dex cache directory exists before proxy generation (#1938) | ported | this commit | `RuntimeHelper.java`: `mkdirs()` the `code_cache/secondary-dexes` dir and fall back to `appDir/secondary-dexes` when it's missing/unwritable. `ProxyGenerator.java` (runtime-binding-generator): create the parent dir before `createNewFile()` — prevents ENOENT crashes on newer Android. Verified: app + generators compile exit 0. | +| 23 | c9d41e6 + fce8e29 | fix(DexFactory): inject DEX into parent class loader (#1951) + register with a single class loader (#1968) | ported | this commit | Combined end-state (`fce8e29` supersedes `c9d41e6`). Enables `Class.forName()` to find runtime-generated proxies (e.g. FragmentFactory). `DexFactory`: new `injectIntoParentClassLoader` ctor flag; `resolveClass` adds the jar into the app's own `BaseDexClassLoader` via `injectDexIntoClassLoader` (API 24+ `addDexPath`; 23/<23 `appendDexElements` using `DexPathList.makePathElements`/`makeDexElements`) so the `DexFile` has a single owner (avoids ART "registered with multiple class loaders" on release builds), falling back to an isolated `DexClassLoader` on failure. `Runtime.java` passes `isMainThread` (workerId==0). Ported `testClassForNameDiscovery.js`. Verified: Java compile exit 0 (reflection has a try/catch fallback if hidden-API access is blocked). **Caveat:** hidden-API reflection is Android-version-sensitive — validate on real devices across versions **and a release/non-debuggable build**. | +| 24 | bfd7650 | fix(timers): order timers with the Java MessageQueue instead of ALooper fds | ported | this commit | Replaced the fork's ALooper-fd/pipe/watcher-thread/mutex/condvar timer delivery with a per-runtime `com.tns.TimerHandler` (new) bound to the runtime thread's Looper: each timer enqueues one anonymous due-token via `sendMessageAtTime`, and native `FireTimer()` (called from `handleMessage`) fires the earliest-due entry of `sortedTimers_` (now `vector` by exact sub-ms dueTime). `postTimer` posts due-now at `(long)now` (FIFO tie with `postDelayed(0)`) and future at `ceil(dueTime)`. Removed threadLoop/PumpTimerLoopCallback/fd/thread/mutex/condvar; added `extern "C" Java_com_tns_TimerHandler_nativeFireTimer` (symbol-bound). Also fixed the pre-existing callback-`napi_ref` leak by centralizing ref deletion in `TimerTask::Unschedule`. Ported `testNativeTimers.js`. Verified: native+Java compile all ABIs exit 0. **Caveat:** ordering semantics are device-only — run the regression tests on a device. | +| 25 | 89893ae | fix: URLSearchParams construction and iteration spec compliance (#1970) | ported | this commit | Rewrote the fork's napi `URLSearchParams` to the WebIDL spec: **constructor** now accepts string / record / sequence-of-pairs / primitive (via `napi_typeof` + `@@iterator` detection, `napi_get_all_property_names` for records, an ES-iterator driver for sequences with `IteratorClose` on abrupt completion, `napi_coerce_to_string` USVString coercion). **keys/values/entries** now return live spec iterators (a JS `{next}` object over ada `operator[]`, duplicate-key correct, `Symbol.iterator`→self, `Symbol.toStringTag`="URLSearchParams Iterator", freed via `napi_add_finalizer`); `prototype[@@iterator]===entries`. Added optional-value `delete`/`has`; `get` returns `null` (not undefined) on miss; brand checks ("Illegal invocation") + `ValueToString` coercion on every method. Ported the full 522-line spec test. Verified: native compile all ABIs exit 0. **Caveat:** spec behavior validated by the ported WPT-style tests **on-device**. | +| — | 92c2654 | fix: anchor relative dynamic imports at the file:// referrer's directory (#1976) | deferred | — | ESM-only: touches `ModuleInternalCallbacks.cpp` (deferred ESM file) + `.mjs` tests. Deferred with [[052cb21]]. | +| — | 45ed1f6 | fix: normalize "." and ".." in resolved module paths to dedupe modules (#1977) | deferred | — | ESM-only: `ModuleInternalCallbacks.cpp` + `.mjs` tests. Deferred with [[052cb21]]. | +| 26 | a84d3c7 | feat(workers): move worker threading and messaging to C++ (#1972) | partial | this commit | **Ported the engine-agnostic core (Path B):** moved worker threading/messaging from Java `HandlerThread` into C++ — new `runtime/workers/{WorkerWrapper,ConcurrentQueue,LooperTasks,WorkerMessage}`. Parent→worker via a `ConcurrentQueue` (eventfd+ALooper) inbox; worker→parent via the parent Runtime's `LooperTasks`. `WorkerWrapper` owns the worker `std::thread` (JVM attach → Java `initWorkerRuntime`/`runWorkerLoop`/`detachWorkerRuntime` → C++ inbox pump), nested-worker cascade, registry. Removed the Java `WorkerThread`/`WorkerThreadHandler`/`MainThreadHandler` worker branches, `initWorker`, the 6 worker native decls + JNI entrypoints, and `MessageType.java`/`JavaScriptErrorMessage.java`. **Perf:** removes 2 JNI hops + Java `Message`/`Handler` alloc + dispatch per postMessage. **Scoped out (V8-only, no napi surface):** `v8::ValueSerializer` structured clone and **SharedArrayBuffer** → payloads stay JSON on all engines (same semantics as before); `terminate()` is **cooperative** (no `v8::TerminateExecution` — a busy-loop worker isn't preempted). `napi_env`↔`WorkerWrapper` via a registry (no isolate data slot). Verified: native+Java compile all ABIs exit 0. **Caveat:** worker threading/lifecycle correctness is device-only — exercise the worker test suite on a device. | +| 27 | 55da2da + 4b5ab0a | feat(inspector): serve source maps to DevTools (#1969) + attach DevTools to Web Worker isolates (#1973) | ported | this commit | Ported both V8-inspector features onto the fork's napi+v8 hybrid inspector (`runtime/inspector/JsV8InspectorClient` reaches `env->isolate`). **#1969:** `Network.loadNetworkResource`/`IO.read`/`IO.close` disk-served source maps, `nsruntime://` sourceMapURL rewrite (+`disableSourceMapURLRewrite`), socket-thread message fast-path, resource-stream cleanup on disconnect. **#1973:** new `WorkerInspectorClient.{h,cpp}` (per-worker V8 inspector Target/flat-session CDP) + Target-domain worker-session routing + console history; hooked into the **new napi `WorkerWrapper`** — `CreateInspector(napi_env)`/`DestroyInspector()` in `BackgroundLooper` around `RunWorker`, `NotifyTerminating()` on cooperative terminate, `consoleLogCallback` via `WorkerWrapper::FromEnv`. Vendored `third_party/json.hpp`. All inspector code gated `#if defined(__V8__) && defined(APPLICATION_IN_DEBUG)` — **verified multi-engine-safe** (QuickJS build green). Verified: V8-13 debug + QuickJS native+Java compile exit 0. **Caveat:** actual DevTools attach / worker breakpoints / source-map fetch / pause-interrupt are device+DevTools-only — run a live debug session before shipping. | +| 4 | 3423e6f | feat: support 16 KB page sizes, gradle 8.5 (#1818) | partial | ca93e66 | **Ported:** 16 KB `-Wl,-z,max-page-size=16384` link option for arm64-v8a/x86_64 in `runtime/CMakeLists.txt` (verified present in ninja LINK_FLAGS); bumped `NS_DEFAULT_COMPILE_SDK_VERSION`/`NS_DEFAULT_BUILD_TOOLS_VERSION` 34→35 (both installed). **Skipped:** gradle-wrapper 8.4→8.7 and AGP 8.3.2→8.5.0 (new already newer: gradle 8.14.3 / AGP 8.12.1); STL `c++_shared`→`c++_static` (new deliberately uses `c++_shared` for multi-engine libc++ — would break engine `.so` setup). Verified: native reconfigure+relink exit 0. | + +## Verified duplication-check seeds (from planning; confirm at implementation time) + +**MISSING → implement:** `f033061` queueMicrotask · Ada v3.x chain (`b31fc5f`/`3633aed`/`3513ce7`/`83f611b`/`45fb275`) — new bundles Ada 2.9.0, old 3.3.0. + +**PARTIAL → port delta only:** `052cb21` ESM (deferred) · URLSearchParams spec follow-ups (`3e61cef`/`89893ae`/`288491f`). + +**ALREADY-PRESENT → skip w/ evidence:** `e98367c` performance api (Performance.h) · `e293636` companion-object inner-type (MetadataNode::SetInnerTypes hasOwnProperty guard) · `1fd144f`/`df4e81b` multithreaded runtime selection (Runtime.java ConcurrentHashMap + dual-path getCurrentRuntimeId). + +**Build deltas (new behind old):** compileSdk/targetSdk 34→35, buildTools 34→35, Kotlin 2.0.0→2.2.20, NDK default 27.1→27.3. AGP/Gradle 8.12.1, JDK 17, minSdk 21 already match. Apply only per-commit version/flag deltas; preserve multi-engine gradle/CMake machinery. diff --git a/build.gradle b/build.gradle index fadae203..074885e4 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,16 @@ def DIST_PATH = "$rootDir/dist_${jsEngine.toLowerCase()}" def TEST_APP_PATH = "$rootDir/test-app" def BUILD_TOOLS_PATH = "$TEST_APP_PATH/build-tools" def DIST_FRAMEWORK_PATH = "$DIST_PATH/framework" +def BYTECODE_TOOLS_PATH = "$rootDir/tools/bytecode-compiler" + +// Maps an ns_engine value to its bytecode-compiler bin/. Engines with no +// bytecode compiler (V8*, JSC) are absent, so no binaries are shipped for them. +def bytecodeEngineDir = [ + "HERMES" : "hermes", + "QUICKJS" : "quickjs", + "QUICKJS_NG": "quickjs-ng", + "PRIMJS" : "primjs", +].get(jsEngine) task checkEnvironmentVariables { if ("$System.env.JAVA_HOME" == "" || "$System.env.JAVA_HOME" == "null") { @@ -298,6 +308,20 @@ task copyFilesToProjectTemeplate { from "$BUILD_TOOLS_PATH/android-metadata-generator/build/libs/android-metadata-generator.jar" into "$DIST_FRAMEWORK_PATH/build-tools" } + // Ship the engine-generic bytecode compiler (driver + adapters). The + // per-host compiler binaries under bin/ are shipped only for the engine + // this framework is built for, so a V8 template doesn't carry hermesc. + copy { + from "$BYTECODE_TOOLS_PATH" + into "$DIST_FRAMEWORK_PATH/build-tools/bytecode-compiler" + exclude "bin/**" + } + if (bytecodeEngineDir != null) { + copy { + from "$BYTECODE_TOOLS_PATH/bin/${bytecodeEngineDir}" + into "$DIST_FRAMEWORK_PATH/build-tools/bytecode-compiler/bin/${bytecodeEngineDir}" + } + } copy { from "$TEST_APP_PATH/runtime/build/outputs/aar/runtime-regular-release.aar" into "$DIST_FRAMEWORK_PATH/app/libs/runtime-libs" @@ -330,10 +354,6 @@ task copyFilesToProjectTemeplate { into "$DIST_FRAMEWORK_PATH" } - ant.propertyfile(file: "$TEST_APP_PATH/gradle.properties") { - entry(key: "ns_engine", value: "V8") - } - copy { from "$TEST_APP_PATH/gradle-helpers/paths.gradle" into "$DIST_FRAMEWORK_PATH/gradle-helpers" diff --git a/docs/bytecode.md b/docs/bytecode.md new file mode 100644 index 00000000..6f3db0eb --- /dev/null +++ b/docs/bytecode.md @@ -0,0 +1,153 @@ +# Compile-time bytecode + +For **release** builds, NativeScript Android can ship pre-compiled engine +bytecode instead of plain JavaScript. Parsing + compiling JS is a large part of +cold-start cost; shipping bytecode moves that work to build time and gives a much +faster time-to-interactive (TTI). + +The mechanism is **engine-generic**. The only engine-specific piece is running +that engine's compiler binary, which is encapsulated in a small adapter under +[`tools/bytecode-compiler/lib`](../tools/bytecode-compiler/lib). + +| Engine | Bytecode | Compiler CLI | Status | +| ------- | -------- | ------------ | ------ | +| Hermes | ✅ HBC | `hermesc` (upstream) | wired end-to-end | +| QuickJS | ✅ | `nsbc-quickjs` shim (`native/qjs-compile.c`) | CLI built by CI; runtime read-support pending | +| QuickJS-NG | ✅ | `nsbc-quickjs-ng` shim | CLI built by CI; runtime read-support pending | +| PrimJS | ✅ | `nsbc-primjs` shim (`native/primjs-compile.c`) | CLI built by CI; runtime read-support pending | +| V8 | ⚠️ | runtime code-cache only (no AOT format) | see [v8-code-cache.md](./v8-code-cache.md) | +| JSC | ❌ never | — | n/a | + +Only Hermes is enabled today. The compiler CLIs for the QuickJS family are built +by the [bytecode-compilers workflow](../.github/workflows/bytecode-compilers.yml), +but those engines stay gated off (`ready: false`) until their runtime execution +path (`js_run_bytecode_file`) exists — see "Adding an engine" below. + +### Bytecode container (non-Hermes engines) + +`hermesc` emits a self-identifying HBC blob. QuickJS's `qjsc` emits a C array, not +a loadable blob, so the QuickJS-family shims (`native/`) wrap the engine's +`JS_WriteObject` output in a small container the runtime detects: + +``` +[8-byte magic][4-byte format version, little-endian][JS_WriteObject payload] +``` + +Magic is per engine (`NSBCQJS\0`, `NSBCNGS\0`, `NSBCPJS\0`) and must match the +adapter's `magic` and the runtime detection. The runtime read side strips the +12-byte header and hands the payload to `JS_ReadObject` + `JS_EvalFunction`. + +## How it works + +1. A normal app compiles its JS bundle and copies it into `assets/app` before the + Android build starts. +2. During a **release** build, after Gradle merges the assets, the + `compileBytecode` task runs the generic driver over the merged `app/` folder. + The driver replaces every `.js` file with bytecode for the active engine. + **The files keep their `.js` names**, so module resolution is unaffected. +3. At load time the runtime reads the file header. If it sees the engine's + bytecode magic (for Hermes, `0x1F1903C103BC1FC6`) it runs the bytecode + directly; otherwise it falls back to compiling the source. Detection is + per-file and authoritative — there is no separate flag that can get out of + sync. + +The C++ loader calls a single engine hook for both the `require` path +([ModuleInternal.cpp](../test-app/runtime/src/main/cpp/runtime/module/ModuleInternal.cpp)) +and the raw-script path +([Runtime::RunScript](../test-app/runtime/src/main/cpp/runtime/Runtime.cpp)): + +```c +// napi/common/jsr_common.h +napi_status js_run_bytecode_file(napi_env env, const char *file, + const char *source_url, napi_value *result); +``` + +### Module wrapping + +The runtime wraps every `require`d module in a function before executing it: + +```js +(function(module, exports, require, __filename, __dirname){ /* module source */ +}) +``` + +Running a module's bytecode must yield that same wrapper function, so the build +step wraps the source with the **identical** prologue/epilogue before compiling +(the completion value of the compiled program is the wrapper function — verified +by disassembly: the Hermes global function ends with `CreateClosure; Ret`). This +wrapping is engine-independent, so it lives in the generic driver. + +Files the runtime runs **unwrapped** as raw scripts (via `Runtime::RunScript`, +e.g. `internal/ts_helpers.js`) are compiled as-is. The build's `--raw` list keeps +these in sync (default: `internal/ts_helpers.js`). + +The prologue/epilogue live in two places that must stay byte-for-byte identical: + +- `runtime/src/main/cpp/runtime/module/ModuleInternal.cpp` + (`MODULE_PROLOGUE` / `MODULE_EPILOGUE`) +- `tools/bytecode-compiler/compile-bytecode.js` + (`MODULE_PROLOGUE` / `MODULE_EPILOGUE`) + +## The compiler tooling + +The compiler lives in [`tools/bytecode-compiler`](../tools/bytecode-compiler) and +is copied into the framework template under `build-tools/bytecode-compiler` when +the runtime is packaged (see `copyFilesToProjectTemeplate` in the root +`build.gradle`). Only the active engine's host binaries are shipped, so a V8 +template doesn't carry `hermesc`. + +Compiler binaries are host-specific, laid out as +`bin///` where `` is `-` +(`darwin-arm64`, `darwin-x64`, `linux-x64`, `linux-arm64`, `win32-x64`). Host +slots without a real binary contain a placeholder; the driver detects the current +host automatically and, if there's no compiler for it, leaves the app as source. + +## Enabling / disabling + +Bytecode is **on by default** for release builds when the active engine has a +ready compiler for the build host. To disable it (ship plain JS): + +``` +./gradlew assembleRelease -Prelease -PnsBytecodeDisabled +``` + +Gradle properties (all optional): + +| Property | Effect | +| ------------------------------ | ------------------------------------------------- | +| `-PnsBytecodeDisabled` | Disable bytecode for this release build | +| `-PnsBytecodeSourceMaps` | Emit `.map` next to each file when supported | +| `-PbytecodeToolsDir=` | Override the bytecode-compiler tools folder | +| `-PbytecodeCompilerBinary=

` | Force a specific compiler binary | +| `-PnodePath=` | Override the `node` executable | + +The compile step never touches the app source — it only rewrites the merged-assets +build intermediate that gets packaged into the APK, and it is idempotent (files +already in bytecode form are skipped), so incremental builds are safe. + +## Source maps + +With `-PnsBytecodeSourceMaps`, engines whose compiler supports it (Hermes) emit a +source map next to each file (`.map`). Because the module wrapper is a +single-line prologue with no newlines, line numbers are preserved (only a +column offset on line 1). + +## Version compatibility + +A compiler and the runtime's engine library must share the same bytecode version +(for Hermes, `BYTECODE_VERSION`, currently 99). They should be built from the same +engine checkout. On a mismatch the runtime fails loudly at load rather than +silently misbehaving. + +## Adding an engine + +1. Add `tools/bytecode-compiler/lib/.js` implementing the adapter contract + (documented in `lib/index.js`) and register it in `lib/index.js`. +2. Drop the host compiler binaries under `tools/bytecode-compiler/bin///`. +3. Implement runtime execution in `napi//jsr.cpp` — `js_run_bytecode_file` + currently returns `napi_cannot_run_js` (source fallback) for every engine + except Hermes. It must detect that engine's bytecode magic and run it, setting + `*result` to the completion value (the module wrapper function for modules). +4. Flip the adapter's `ready` flag to `true` **only** once both the compiler and + the runtime support exist — otherwise the build would ship bytecode the runtime + can't execute. diff --git a/docs/v8-code-cache.md b/docs/v8-code-cache.md new file mode 100644 index 00000000..92e013ba --- /dev/null +++ b/docs/v8-code-cache.md @@ -0,0 +1,61 @@ +# V8 code cache + +V8 has no ahead-of-time bytecode format like Hermes, but it can **serialize the +code it compiles at runtime** and consume it on the next launch to skip parsing +and compilation. This is wired into the V8 runtime and works by default — no +configuration required. + +Unlike the [compile-time bytecode](./bytecode.md) feature (Hermes et al.), this is +a *runtime* cache: the first launch compiles as usual and writes the cache; every +later launch loads it. + +## How it works + +All script execution funnels through `js_execute_script` +([napi/v8/jsr.cpp](../test-app/runtime/src/main/cpp/napi/v8/jsr.cpp)), which now: + +1. **Consumes** an existing cache via `js_run_cached_script`. If a `.cache` + sits next to the module and is current, V8 compiles from it (`kConsumeCodeCache`) + and runs — no parse. +2. On a **miss**, compiles the source **once** (`CompileRunAndCache`), serializes + a code cache from that same `UnboundScript`, then runs it. The cache is written + next to the module for next time. + + > `napi_run_script_source` only returns the completion value, not the compiled + > `UnboundScript` that `CreateCodeCache` needs — so the cold path compiles + > directly (rather than delegating and then re-compiling) to avoid parsing the + > source twice. + +The cache lives at `.cache` in the app's writable files dir (next to the +extracted JS). Only real `file://` modules are cached; synthetic sources such as +`` are skipped. + +## Correctness + +- **Staleness.** The `.cache` is stamped with its source file's mtime. If the + source changes (app update, LiveSync), the mtime no longer matches and the + cache is ignored, then rewritten. V8 additionally embeds a source hash and + version tag in the cache and rejects it on any mismatch (e.g. a runtime/V8 + upgrade), recompiling from source — so a stale or incompatible cache can never + run the wrong code. Rejected caches are refreshed automatically. +- **Exceptions.** `js_run_cached_script` distinguishes a *cache miss* (returns + `napi_cannot_run_js`, caller falls back to source) from *the module actually + running and throwing* (propagates the exception; the caller must not execute it + again). This prevents double execution of module side effects. +- **Atomic writes.** The cache is written to `.cache.tmp` and renamed into + place (mtime stamped before the rename), so a crash or a worker loading the same + module never sees a half-written cache. + +## Cost + +The win is on the **second and later** launches. The first launch (or any launch +after a change) compiles the source once — the same as before this feature — plus +a small serialize-and-write step to produce the cache. There is no double compile. +Real apps are bundled into a handful of files, so the extra work is negligible and +buys faster subsequent starts. + +## Notes + +- This is orthogonal to compile-time bytecode. V8 uses this runtime cache; Hermes + (and, later, QuickJS/PrimJS) use precompiled bytecode instead. +- `js_run_bytecode_file` remains a no-op for V8 (there is no AOT bytecode format). diff --git a/package.json b/package.json index fe10844e..8e0567c5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@nativescript/android", "description": "NativeScript for Android using Node-API", - "version": "8.8.5", + "version": "9.0.0", "repository": { "type": "git", "url": "https://github.com/NativeScript/android.git" @@ -28,7 +28,7 @@ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", "version": "npm run changelog && git add CHANGELOG.md", "build": "node ./scripts/build.js", - "setup": "cd test-app/build-tools/jsparser && npm install && cd ../../../" + "setup": "node ./scripts/setup.js" }, "devDependencies": { "conventional-changelog-cli": "^2.1.1", diff --git a/packages/android-shermes/LICENSE b/packages/android-shermes/LICENSE deleted file mode 100755 index 45141222..00000000 --- a/packages/android-shermes/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright (c) 2020, nStudio, LLC - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/android-shermes/README.md b/packages/android-shermes/README.md deleted file mode 100644 index 5d95941e..00000000 --- a/packages/android-shermes/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @nativescript/android-shermes - -NativeScript runtime package for Android, built with the Static Hermes (shermes) JavaScript engine. - -## Build - -From the repo root: - -```sh -./gradlew -Pengine=SHERMES -``` - -This produces the npm artifact in `dist_shermes/`. diff --git a/packages/android-shermes/package.json b/packages/android-shermes/package.json deleted file mode 100644 index 86b3de10..00000000 --- a/packages/android-shermes/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "@nativescript/android-shermes", - "version": "0.0.1", - "description": "NativeScript Runtime for Android (Static Hermes engine)", - "keywords": [ - "NativeScript", - "Android", - "runtime", - "hermes", - "shermes" - ], - "repository": { - "type": "git", - "url": "https://github.com/NativeScript/napi-android", - "directory": "packages/android-shermes" - }, - "author": { - "name": "NativeScript Team", - "email": "oss@nativescript.org" - }, - "license": "Apache-2.0" -} diff --git a/scripts/build.js b/scripts/build.js index b9e1c854..2a22d84e 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -7,8 +7,17 @@ const { spawn } = require('child_process'); const path = require('path'); const readline = require('readline'); -const VALID_ENGINES = ['V8-10',"V8-11","V8-13", 'QUICKJS', "QUICKJS_NG", 'HERMES', 'JSC', 'SHERMES', 'PRIMJS']; -const HOST_OBJECTS_SUPPORTED = new Set(['V8-10','V8-11',"V8-13", 'QUICKJS',"QUICKJS_NG", 'PRIMJS']); +const VALID_ENGINES = ['V8-10',"V8-11","V8-13", 'QUICKJS', "QUICKJS_NG", 'HERMES', 'JSC', 'PRIMJS']; +const HOST_OBJECTS_SUPPORTED = new Set(['V8-10','V8-11',"V8-13", 'QUICKJS',"QUICKJS_NG",'HERMES', 'PRIMJS', "JSC"]); + +// Host objects are enabled by default whenever the selected engine supports +// them. They can be force-disabled with --disable-host-objects (or by +// answering "no" to the interactive prompt). +function hostObjectsEnabled(opts) { + if (!HOST_OBJECTS_SUPPORTED.has(opts.engine)) return false; + if (opts['disable-host-objects']) return false; + return true; +} function parseArgs(argv) { const opts = {}; @@ -51,20 +60,6 @@ async function interactiveFill(opts) { opts.engine = VALID_ENGINES.includes(pick) ? pick : 'V8-10'; } - // Only prompt for host objects if the chosen engine supports it - if (HOST_OBJECTS_SUPPORTED.has(opts.engine)) { - if (typeof opts['use-host-objects'] === 'undefined') { - const ans = await prompt('Use host objects? [y/N]', rl, 'N'); - if (/^y(es)?$/i.test(ans)) opts['use-host-objects'] = true; - } - } else { - // ensure the flag is not set for unsupported engines - if (opts['use-host-objects']) { - console.log(`Warning: host objects not supported for engine ${opts.engine}; ignoring --use-host-objects`); - delete opts['use-host-objects']; - } - } - return opts; } @@ -72,29 +67,9 @@ async function interactiveFill(opts) { if (!opts.engine) { console.log('Select JS engine:'); VALID_ENGINES.forEach((e, i) => console.log(` ${i + 1}) ${e}`)); - const ans = await prompt('Choose number or name', rl, 'V8'); + const ans = await prompt('Choose number or name', rl, 'V8-10'); const pick = /^\d+$/.test(ans) ? VALID_ENGINES[Number(ans) - 1] : ans; - opts.engine = VALID_ENGINES.includes(pick) ? pick : 'V8'; - } - - const booleanPrompts = [ - { key: 'use-host-objects', prop: 'useHostObjects', desc: 'Use host objects (useHostObjects)' }, - ]; - - for (const p of booleanPrompts) { - // skip host-objects prompt if the selected engine does not support it - if (p.key === 'use-host-objects' && !HOST_OBJECTS_SUPPORTED.has(opts.engine)) { - if (opts['use-host-objects']) { - console.log(`Warning: host objects not supported for engine ${opts.engine}; ignoring --use-host-objects`); - delete opts['use-host-objects']; - } - continue; - } - - if (typeof opts[p.key] === 'undefined') { - const ans = await prompt(`${p.desc}? [y/N]`, rl, 'N'); - if (/^y(es)?$/i.test(ans)) opts[p.key] = true; - } + opts.engine = VALID_ENGINES.includes(pick) ? pick : 'V8-10'; } } finally { @@ -107,9 +82,9 @@ async function interactiveFill(opts) { function buildGradleArgs(opts) { const props = []; if (opts.engine) props.push(`-Pengine=${opts.engine}`); - if (opts['use-host-objects']) props.push('-PuseHostObjects'); + if (hostObjectsEnabled(opts)) props.push('-PuseHostObjects'); if (opts['as-napi-module']) props.push('-PasNapiModule'); - + return props; } diff --git a/scripts/get-next-version.js b/scripts/get-next-version.js index 2a370ccc..832380d2 100644 --- a/scripts/get-next-version.js +++ b/scripts/get-next-version.js @@ -15,7 +15,6 @@ function resolveCurrentVersion() { "android-jsc": "../packages/android-jsc/package.json", "android-quickjs": "../packages/android-quickjs/package.json", "android-quickjs-ng": "../packages/android-quickjs-ng/package.json", - "android-shermes": "../packages/android-shermes/package.json", "android-primjs": "../packages/android-primjs/package.json", }; diff --git a/scripts/get-npm-tag.js b/scripts/get-npm-tag.js index 8b4b7b91..d5615722 100644 --- a/scripts/get-npm-tag.js +++ b/scripts/get-npm-tag.js @@ -12,7 +12,6 @@ if (!currentVersion) { "android-jsc": "../packages/android-jsc/package.json", "android-quickjs": "../packages/android-quickjs/package.json", "android-quickjs-ng": "../packages/android-quickjs-ng/package.json", - "android-shermes": "../packages/android-shermes/package.json", "android-primjs": "../packages/android-primjs/package.json", }; diff --git a/scripts/setup.js b/scripts/setup.js new file mode 100644 index 00000000..dea20376 --- /dev/null +++ b/scripts/setup.js @@ -0,0 +1,72 @@ +#!/usr/bin/env node +// +// One-shot developer/CI setup for the android-runtime repo: +// 1. Fetch the engine source submodules (QuickJS / QuickJS-NG) and reset them +// to their pinned commit. +// 2. Apply the NativeScript local patches onto the pristine submodules. +// 3. Install the jsparser build-tool dependencies (the previous `npm run setup`). +// +// Idempotent: re-running resets the submodules to the pinned commit (discarding +// an already-applied patch) and re-applies, so it is safe to run repeatedly. +// +// The submodule + patch config mirrors scripts/vendor-engines-as-submodules.sh. + +'use strict'; + +const { execFileSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '..'); +const NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + +const NAPI_QJS = 'test-app/runtime/src/main/cpp/napi/quickjs'; +const ENGINES = [ + { sub: `${NAPI_QJS}/source`, patch: 'tools/patches/quickjs/0001-nativescript-local-changes.patch' }, + { sub: `${NAPI_QJS}/source_ng`, patch: 'tools/patches/quickjs_ng/0001-nativescript-local-changes.patch' }, +]; + +function run(cmd, args, opts = {}) { + console.log(`==> ${cmd} ${args.join(' ')}`); + execFileSync(cmd, args, { stdio: 'inherit', cwd: ROOT, ...opts }); +} + +function insideGitWorkTree() { + try { + return execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: ROOT }) + .toString().trim() === 'true'; + } catch (_) { + return false; + } +} + +function setupEngines() { + if (!insideGitWorkTree()) { + console.log('==> Not a git work tree; skipping submodule + patch setup.'); + return; + } + + // 1. Fetch + reset the submodules to their pinned commit. --force discards any + // previously-applied patch so step 2 always starts from a clean checkout. + run('git', ['submodule', 'update', '--init', '--force', ...ENGINES.map((e) => e.sub)]); + + // 2. Apply the local patches onto the pristine submodules. + for (const { sub, patch } of ENGINES) { + const patchAbs = path.join(ROOT, patch); + if (!fs.existsSync(patchAbs)) { + console.warn(` ! patch missing, skipping: ${patch}`); + continue; + } + run('git', ['-C', sub, 'apply', '--whitespace=nowarn', patchAbs]); + } +} + +function setupJsParser() { + const dir = path.join(ROOT, 'test-app/build-tools/jsparser'); + const hasLock = fs.existsSync(path.join(dir, 'package-lock.json')); + run(NPM, [hasLock ? 'ci' : 'install'], { cwd: dir }); +} + +setupEngines(); +setupJsParser(); +console.log('==> Setup complete'); diff --git a/scripts/vendor-engines-as-submodules.sh b/scripts/vendor-engines-as-submodules.sh new file mode 100755 index 00000000..cb8b9881 --- /dev/null +++ b/scripts/vendor-engines-as-submodules.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env bash +# +# Convert the vendored QuickJS / QuickJS-NG engine sources into pinned git +# submodules, preserving our hand-written local changes as patch files. +# +# What it does (default / full run): +# 1. Resolves the target upstream commit for each engine. +# 2. Generates a patch capturing the local edits in the current vendored tree +# relative to that upstream commit -> tools/patches//. +# 3. Replaces the vendored directory with a submodule pinned to that commit. +# 4. Re-applies the patch so the working tree stays buildable (the submodule is +# left "dirty" with the local changes, which is expected for this pattern). +# +# Engines: +# quickjs -> https://github.com/bellard/quickjs @ latest (default HEAD) +# quickjs_ng -> https://github.com/quickjs-ng/quickjs @ 3c8f3d68953955950074c41c6e4d999562ae82a7 +# +# Modes: +# (no args) full conversion described above +# --patches-only only (re)generate the patch files; do NOT touch submodules +# (safe, non-destructive preview) +# --apply-only re-apply existing patches onto already-converted submodules +# (use on a fresh checkout / in CI after `submodule update`) +# +# The script never commits; it prints the commands to review and commit at the end. + +set -euo pipefail + +# ---- engine table (bash 3.2-friendly parallel arrays) ---------------------- +ENGINE_NAMES=(quickjs quickjs_ng) +ENGINE_SUBDIRS=(source source_ng) +ENGINE_REPOS=(https://github.com/bellard/quickjs https://github.com/quickjs-ng/quickjs) +# Use HEAD to mean "latest default-branch commit"; otherwise pin to the given sha. +# quickjs : bellard master @ 04be246 (vendored base ~= this) -> auto-diff a clean patch. +# quickjs_ng: pinned to master @ d950d55 (BC_VERSION 27). The vendored source was +# v0.15.1 (BC_VERSION 26), so a diff can't isolate our edits; the patch +# is hand-migrated (see tools/patches/quickjs_ng) and used as-is. +ENGINE_REFS=(04be246001599f5995fa2f2d8c91a0f198d3f34c d950d55e950dd7994a96c669c31efa967b8b79f3) + +# Per-engine patch handling: +# auto -> regenerate the patch by diffing the vendored files vs upstream +# manual -> use the committed patch as-is (do NOT regenerate) +ENGINE_PATCH_MODE=(auto manual) + +# Only these files carry hand-written NativeScript changes, so the (auto) patch is +# scoped to them. Everything else comes verbatim from the pinned upstream commit. +PATCH_FILES=(quickjs.c quickjs.h) + +PATCH_NAME="0001-nativescript-local-changes.patch" + +# ---- setup ----------------------------------------------------------------- +REPO_ROOT="$(git rev-parse --show-toplevel)" +cd "$REPO_ROOT" + +NAPI_QJS_REL="test-app/runtime/src/main/cpp/napi/quickjs" +PATCHES_REL="tools/patches" + +command -v rsync >/dev/null || { echo "error: rsync is required" >&2; exit 1; } + +log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m warning:\033[0m %s\n' "$*" >&2; } + +resolve_commit() { + local repo="$1" ref="$2" + if [ "$ref" = "HEAD" ]; then + git ls-remote "$repo" HEAD | awk '{print $1}' + else + echo "$ref" + fi +} + +# Generate a patch of the local edits in $vendored relative to $repo@$commit, +# scoped to the files in PATCH_FILES (the ones we hand-edit). Overlaying only +# those files onto a pristine upstream checkout means the diff is purely our +# changes, with none of the upstream drift in the surrounding files. +gen_patch() { + local vendored="$1" repo="$2" commit="$3" patch_out="$4" + local tmp f + tmp="$(mktemp -d)" + git -C "$tmp" init -q + git -C "$tmp" config core.autocrlf false + git -C "$tmp" remote add origin "$repo" + git -C "$tmp" fetch -q --depth 1 origin "$commit" + git -C "$tmp" checkout -q FETCH_HEAD + # Overlay only the hand-edited files onto the pristine upstream checkout. + for f in "${PATCH_FILES[@]}"; do + if [ -f "$vendored/$f" ]; then + cp "$vendored/$f" "$tmp/$f" + else + warn "$(basename "$vendored"): patch file '$f' missing in vendored tree" + fi + done + git -C "$tmp" add -- "${PATCH_FILES[@]}" + mkdir -p "$(dirname "$patch_out")" + git -C "$tmp" diff --cached --binary -- "${PATCH_FILES[@]}" >"$patch_out" + rm -rf "$tmp" +} + +apply_patch() { + local subdir="$1" patch_abs="$2" + if [ ! -s "$patch_abs" ]; then + log " no local changes to apply ($patch_abs is empty)" + return 0 + fi + git -C "$subdir" apply --whitespace=nowarn "$patch_abs" +} + +to_submodule() { + local subdir_rel="$1" repo="$2" commit="$3" + # Drop the vendored copy from index + working tree, then clear any leftovers. + git rm -r -q --ignore-unmatch "$subdir_rel" || true + rm -rf "$subdir_rel" + # Add the submodule (non-recursive: upstream's own test submodules stay off). + git submodule add --force "$repo" "$subdir_rel" + git -C "$subdir_rel" checkout -q "$commit" + git add .gitmodules "$subdir_rel" +} + +MODE="full" +case "${1:-}" in + --patches-only) MODE="patches" ;; + --apply-only) MODE="apply" ;; + "") MODE="full" ;; + *) echo "usage: $0 [--patches-only|--apply-only]" >&2; exit 2 ;; +esac + +# ---- guard: don't clobber an existing submodule on a full run -------------- +if [ "$MODE" = "full" ] && [ -f .gitmodules ]; then + for i in "${!ENGINE_NAMES[@]}"; do + if git config -f .gitmodules --get-regexp path 2>/dev/null \ + | awk '{print $2}' | grep -qx "$NAPI_QJS_REL/${ENGINE_SUBDIRS[$i]}"; then + echo "error: '$NAPI_QJS_REL/${ENGINE_SUBDIRS[$i]}' is already a submodule." >&2 + echo " Use --apply-only to re-apply patches, or remove it first." >&2 + exit 1 + fi + done +fi + +# ---- run ------------------------------------------------------------------- +declare -a PINNED +for i in "${!ENGINE_NAMES[@]}"; do + name="${ENGINE_NAMES[$i]}" + subdir_rel="$NAPI_QJS_REL/${ENGINE_SUBDIRS[$i]}" + repo="${ENGINE_REPOS[$i]}" + patch_abs="$REPO_ROOT/$PATCHES_REL/$name/$PATCH_NAME" + + if [ "$MODE" = "apply" ]; then + log "$name: re-applying $PATCHES_REL/$name/$PATCH_NAME" + apply_patch "$subdir_rel" "$patch_abs" + continue + fi + + log "$name: resolving ${ENGINE_REFS[$i]} on $repo" + commit="$(resolve_commit "$repo" "${ENGINE_REFS[$i]}")" + PINNED+=("$name -> $commit") + log "$name: target commit $commit" + + if [ "${ENGINE_PATCH_MODE[$i]}" = "manual" ]; then + if [ ! -s "$patch_abs" ]; then + warn "$name: manual patch '$PATCHES_REL/$name/$PATCH_NAME' is missing/empty." + else + log "$name: using hand-migrated patch $PATCHES_REL/$name/$PATCH_NAME (not regenerated)" + fi + elif [ -d "$subdir_rel" ]; then + log "$name: generating patch -> $PATCHES_REL/$name/$PATCH_NAME" + gen_patch "$subdir_rel" "$repo" "$commit" "$patch_abs" + lines="$(wc -l <"$patch_abs" | tr -d ' ')" + log "$name: patch is $lines line(s)" + [ "$lines" -eq 0 ] && warn "$name: patch is empty (no local changes vs $commit)" + elif [ "$MODE" = "patches" ]; then + warn "$name: vendored dir '$subdir_rel' missing; cannot diff. Skipping patch." + continue + fi + + if [ "$MODE" = "full" ]; then + log "$name: converting '$subdir_rel' to submodule @ $commit" + to_submodule "$subdir_rel" "$repo" "$commit" + log "$name: applying local patch onto the submodule" + apply_patch "$subdir_rel" "$patch_abs" + fi +done + +# ---- summary --------------------------------------------------------------- +echo +log "Done (mode: $MODE)." +if [ "${#PINNED[@]:-0}" -gt 0 ]; then + echo "Pinned commits:" + printf ' %s\n' "${PINNED[@]}" +fi +if [ "$MODE" = "full" ]; then + cat < + + + + + \ No newline at end of file diff --git a/test-app/.idea/caches/deviceStreaming.xml b/test-app/.idea/caches/deviceStreaming.xml deleted file mode 100644 index a2c5a7a3..00000000 --- a/test-app/.idea/caches/deviceStreaming.xml +++ /dev/null @@ -1,969 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/test-app/.idea/kotlinc.xml b/test-app/.idea/kotlinc.xml index 6d0ee1c2..3efb2d8d 100644 --- a/test-app/.idea/kotlinc.xml +++ b/test-app/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/test-app/.idea/vcs.xml b/test-app/.idea/vcs.xml index 6c0b8635..7794184d 100644 --- a/test-app/.idea/vcs.xml +++ b/test-app/.idea/vcs.xml @@ -2,5 +2,7 @@ + + \ No newline at end of file diff --git a/test-app/analytics/build-statistics.json b/test-app/analytics/build-statistics.json index a972bc51..5b38e651 100644 --- a/test-app/analytics/build-statistics.json +++ b/test-app/analytics/build-statistics.json @@ -1,6 +1,6 @@ { - "kotlinUsage": { - "hasUseKotlinPropertyInApp": true, - "hasKotlinRuntimeClasses": false - } + "kotlinUsage": { + "hasUseKotlinPropertyInApp": true, + "hasKotlinRuntimeClasses": true + } } \ No newline at end of file diff --git a/test-app/app/build.gradle b/test-app/app/build.gradle index 5d7846de..2195875f 100644 --- a/test-app/app/build.gradle +++ b/test-app/app/build.gradle @@ -78,10 +78,10 @@ def pluginsJarLibraries = new LinkedList() def allJarLibraries = new LinkedList() def computeCompileSdkVersion = { -> - project.hasProperty("compileSdk") ? compileSdk : NS_DEFAULT_COMPILE_SDK_VERSION as int + project.hasProperty("compileSdk") ? compileSdk as int : NS_DEFAULT_COMPILE_SDK_VERSION as int } def computeTargetSdkVersion = { -> - project.hasProperty("targetSdk") ? targetSdk : NS_DEFAULT_COMPILE_SDK_VERSION as int + project.hasProperty("targetSdk") ? targetSdk as int : NS_DEFAULT_COMPILE_SDK_VERSION as int } def computeMinSdkVersion = { -> project.hasProperty("minSdk") ? minSdk : NS_DEFAULT_MIN_SDK_VERSION as int @@ -99,7 +99,7 @@ if (enableAnalytics) { analyticsCollector.writeAnalyticsFile() } -project.ext.selectedBuildType = project.hasProperty("release") ? "release" : "debug" +project.ext.selectedBuildType = "release" buildscript { def applyBuildScriptConfigurations = { -> @@ -233,6 +233,8 @@ android { targetSdkVersion computeTargetSdkVersion() ndk { if (onlyX86) { + // The updated JSC only ships 64-bit libraries, so fall back to + // x86_64 when a single-ABI (emulator) build is requested. abiFilters 'x86' } else { abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' @@ -886,12 +888,12 @@ task buildMetadata(type: BuildToolTask) { } - def compileArtProfileTaskName = "compile${buildTypeName}ArtProfile" - def compileArtProfileTask = tasks.findByName(compileArtProfileTaskName) + // def compileArtProfileTaskName = "compile${buildTypeName}ArtProfile" + // def compileArtProfileTask = tasks.findByName(compileArtProfileTaskName) - if (compileArtProfileTask) { - dependsOn compileArtProfileTask - } + // if (compileArtProfileTask) { + // dependsOn compileArtProfileTask + // } def extractNativeSymbolTablesTaskName = "extract${buildTypeName}NativeSymbolTables" @@ -902,12 +904,12 @@ task buildMetadata(type: BuildToolTask) { } - def optimizeResourcesTaskName = "optimize${buildTypeName}Resources" - def optimizeResourcesTask = tasks.findByName(optimizeResourcesTaskName) + // def optimizeResourcesTaskName = "optimize${buildTypeName}Resources" + // def optimizeResourcesTask = tasks.findByName(optimizeResourcesTaskName) - if (optimizeResourcesTask) { - dependsOn optimizeResourcesTask - } + // if (optimizeResourcesTask) { + // dependsOn optimizeResourcesTask + // } def bundleResourcesTaskName = "bundle${buildTypeName}Resources" def bundleResourcesTask = tasks.findByName(bundleResourcesTaskName) @@ -1122,6 +1124,120 @@ task 'validateAppIdMatch' { } } +//////////////////////////////////////////////////////////////////////////////////// +////////////////////////// COMPILE-TIME BYTECODE /////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +// +// For RELEASE builds, replace the plain-JS app bundle in the merged assets with +// pre-compiled bytecode for the active JS engine (Hermes today; QuickJS/QuickJS-NG/ +// PrimJS are wired but gated off until their compilers + runtime support land). +// The files keep their .js names, so module resolution is unaffected; the runtime +// detects the engine's bytecode magic at load time (see js_run_bytecode_file / +// ModuleInternal) and runs it directly instead of parsing + compiling source, +// which greatly improves TTI. +// +// The engine-generic driver decides whether the selected engine has a usable +// compiler for the build host, so this task simply runs it for every release +// build and lets it no-op when there's nothing to do. It only touches the +// merged-assets build intermediate, never the app source. +// +// Disable for a release build with: -PnsBytecodeDisabled +// Force-enable for a debug build with: -PnsBytecodeForce +// Enable source maps with: -PnsBytecodeSourceMaps +// +// Overridable paths: +// -PbytecodeToolsDir=

— the bytecode-compiler tools folder +// -PbytecodeCompilerBinary= — force a specific compiler binary +// -PnodePath= — the node executable + +def bytecodeDisabled = project.hasProperty("nsBytecodeDisabled") || project.hasProperty("bytecodeDisabled") +// Force bytecode compilation even for debug builds. Bytecode is a release-only +// feature by default; this opt-in flag makes debug builds compile to bytecode +// too, which is handy for testing/debugging the bytecode path itself. +def bytecodeForced = project.hasProperty("nsBytecodeForce") || project.hasProperty("bytecodeForce") +// A release build is not reliably signalled by -Prelease (the CLI runs +// assembleRelease/bundleRelease without it), so detect it from the requested +// task names — the same heuristic the rest of this file uses. +def isReleaseBuild = project.hasProperty("release") || + project.gradle.startParameter.taskNames.any { it.toLowerCase().contains("release") } +project.ext.bytecodeEnabled = (isReleaseBuild || bytecodeForced) && !bytecodeDisabled + +// Announce the bytecode status once at configuration time so it's obvious in the +// build log whether ahead-of-time bytecode will be produced. +if (project.ext.bytecodeEnabled) { + def buildKind = isReleaseBuild ? "release build" : "debug build (forced via -PnsBytecodeForce)" + outLogger.withStyle(Style.SuccessHeader).println "\t + [bytecode] ENABLED for ${buildKind} (engine: ${ns_engine})" + outLogger.withStyle(Style.Info).println "\t ~ app JS will be compiled to bytecode after asset merge (if ${ns_engine} has a compiler for this host)." + outLogger.withStyle(Style.Info).println "\t ~ disable with -PnsBytecodeDisabled" +} else { + def bytecodeReason = bytecodeDisabled ? "disabled via -PnsBytecodeDisabled" + : "debug build (release-only feature; force with -PnsBytecodeForce)" + outLogger.withStyle(Style.Info).println "\t ~ [bytecode] DISABLED — ${bytecodeReason}; shipping plain JS. isReleaseBuid: ${isReleaseBuild};" +} + +// The compiler tooling ships in the framework template under build-tools; fall +// back to the in-repo location when building the runtime itself (test-app). +def resolveBytecodeToolsDir = { -> + if (project.hasProperty("bytecodeToolsDir")) return bytecodeToolsDir as String + def candidates = [ + "$rootDir/build-tools/bytecode-compiler" as String, + "$rootDir/../tools/bytecode-compiler" as String, + ] + return candidates.find { new File(it).exists() } ?: candidates[0] +} +def resolveNodePath = { -> + project.hasProperty("nodePath") ? nodePath as String : "node" +} + +task compileBytecode { + // No inputs/outputs declared on purpose: the task is cheap and idempotent + // (it skips files that are already bytecode) and must re-run whenever the + // merged assets are refreshed. + onlyIf { project.ext.bytecodeEnabled } + doLast { + def appDir = getMergedAssetsOutputPath() + "/app" + if (!new File(appDir).exists()) { + outLogger.withStyle(Style.Info).println "\t ~ [bytecode] no merged app assets at ${appDir}, skipping" + return + } + def toolsDir = resolveBytecodeToolsDir() + def script = "$toolsDir/compile-bytecode.js" + def node = resolveNodePath() + def sourceMaps = project.hasProperty("nsBytecodeSourceMaps") + // Resilient by default: a file that fails to compile is left as plain JS + // (the runtime loads source directly) and the build continues. Opt into + // fail-the-build behaviour with -PnsBytecodeStrict. + def strict = project.hasProperty("nsBytecodeStrict") + + outLogger.withStyle(Style.SuccessHeader).println "\t + [bytecode] compiling app JS → ${ns_engine} bytecode" + outLogger.withStyle(Style.Info).println "\t ~ engine: ${ns_engine}" + outLogger.withStyle(Style.Info).println "\t ~ app assets: ${appDir}" + outLogger.withStyle(Style.Info).println "\t ~ driver: ${script}" + if (project.hasProperty("bytecodeCompilerBinary")) { + outLogger.withStyle(Style.Info).println "\t ~ compiler: ${bytecodeCompilerBinary}" + } + outLogger.withStyle(Style.Info).println "\t ~ source maps: ${sourceMaps ? "on" : "off"}" + outLogger.withStyle(Style.Info).println "\t ~ on error: ${strict ? "fail the build (strict)" : "skip file, keep source"}" + + def cmd = [node, script, "--app", appDir, "--engine", ns_engine as String] + if (project.hasProperty("bytecodeCompilerBinary")) { + cmd += ["--compiler", bytecodeCompilerBinary as String] + } + if (sourceMaps) { + cmd += ["--source-maps"] + } + if (!strict) { + cmd += ["--keep-going"] + } + // The driver prints its own "[bytecode] ... compiled N file(s)" summary to + // stdout, which surfaces in the build log below. + exec { + commandLine cmd + } + outLogger.withStyle(Style.SuccessHeader).println "\t + [bytecode] done." + } +} + //////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// OPTIONAL TASKS ////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// @@ -1174,11 +1290,41 @@ tasks.configureEach({ DefaultTask currentTask -> if (currentTask =~ /merge.*Assets/) { currentTask.dependsOn(buildMetadata) + // Compile the freshly-merged app JS to engine bytecode right after the + // assets are merged (the onlyIf guard makes this a no-op unless it's a + // release build with bytecode enabled). Packaging is made to wait for it below. + currentTask.finalizedBy(compileBytecode) + compileBytecode.mustRunAfter(currentTask) + } + + // The asset pipeline is: merge*Assets -> compress*Assets -> package*. The + // compress task reads the merged assets and zips each file into a `.js.jar` + // that is what actually lands in the APK/App Bundle. It only depends on the + // merge task, so without this it can run BEFORE compileBytecode and end up + // compressing plain source — the compiled bytecode in the merged assets then + // never reaches the package. Force compression to wait for compileBytecode so + // the order is merge -> compileBytecode -> compress -> package. (No cycle: + // compileBytecode only mustRunAfter the merge task, never the compress task.) + // Debug's compress*Assets task is included so a force-enabled (-PnsBytecodeForce) + // debug build compresses the compiled bytecode rather than plain source. The + // task is a no-op via compileBytecode's onlyIf guard when bytecode is off. + if (currentTask.name == "compressReleaseAssets" || currentTask.name == "compressDebugAssets") { + currentTask.dependsOn(compileBytecode) + } + + // Ensure the final APK/App Bundle packaging picks up the compiled bytecode. + // Match the packaging tasks by exact name — a broad /package.*Release/ also + // matches packageReleaseResources, which is upstream of buildMetadata -> + // mergeReleaseAssets -> compileBytecode and creates a circular dependency. + // Debug packaging is wired too so a force-enabled debug build ships bytecode; + // compileBytecode's onlyIf keeps it a no-op for ordinary debug builds. + if (currentTask.name in ["packageRelease", "packageReleaseBundle", "packageDebug", "packageDebugBundle"]) { + currentTask.dependsOn(compileBytecode) } // // ensure buildMetadata is done before R8 to allow custom proguard from metadata if (currentTask =~ /minify.*WithR8/) { - buildMetadata.finalizedBy(currentTask) + // buildMetadata.finalizedBy(currentTask) } if (currentTask =~ /assemble.*Debug/ || currentTask =~ /assemble.*Release/) { currentTask.finalizedBy("validateAppIdMatch") diff --git a/test-app/app/src/main/assets/app/MyActivity.js b/test-app/app/src/main/assets/app/MyActivity.js index 3ff34f5b..abd8d7da 100644 --- a/test-app/app/src/main/assets/app/MyActivity.js +++ b/test-app/app/src/main/assets/app/MyActivity.js @@ -21,7 +21,6 @@ } } */ -const benchmarkRunner = require("./benchmark.js"); var MyActivity = (function (_super) { __extends(MyActivity, _super); function MyActivity() { @@ -29,9 +28,19 @@ var MyActivity = (function (_super) { } MyActivity.prototype.onCreate = function (bundle) { _super.prototype.onCreate.call(this, bundle); - require('./tests/testsWithContext').run(this); - //run jasmine -// execute(); + // Launch mode is selected by an intent extra so we don't have to rebuild to + // switch between the jasmine regression suite and the perf benchmark: + // default -> run the jasmine test suite (regression check) + // --ez bench true -> run the marshalling benchmark in isolation + var runBench = false; + try { + runBench = this.getIntent().getBooleanExtra("bench", false); + } catch (e) {} + if (!runBench) { + require('./tests/testsWithContext').run(this); + // run jasmine + execute(); + } var layout = new android.widget.LinearLayout(this); layout.setOrientation(1); this.setContentView(layout); @@ -48,6 +57,10 @@ var MyActivity = (function (_super) { button2.setText("Run Benchmark"); layout.addView(button2); + var button3 = new android.widget.Button(this); + button3.setText("Run Marshalling Benchmark"); + layout.addView(button3); + var Color = android.graphics.Color; var colors = [ Color.BLUE, @@ -68,17 +81,82 @@ var MyActivity = (function (_super) { }, }) ); + var benchmarkWorker = null; button2.setOnClickListener( new android.view.View.OnClickListener("AppClickListener", { onClick: function () { - const result = benchmarkRunner.runBenchmark(); - setTimeout(() => { - globalThis.gc(); - }); - textView.setText(result); + // Run the benchmark on a worker thread so the UI thread stays + // responsive and Android does not raise an ANR. + if (benchmarkWorker) { + return; + } + button2.setText("Running Benchmark..."); + textView.setText("Running benchmark, please wait..."); + + benchmarkWorker = new Worker("./benchmark-worker.js"); + benchmarkWorker.onmessage = function (msg) { + textView.setText(msg.data); + button2.setText("Run Benchmark"); + benchmarkWorker.terminate(); + benchmarkWorker = null; + }; + benchmarkWorker.onerror = function (err) { + textView.setText("Benchmark error: " + (err && err.message ? err.message : err)); + button2.setText("Run Benchmark"); + benchmarkWorker.terminate(); + benchmarkWorker = null; + }; + benchmarkWorker.postMessage("start"); + }, + }) + ); + var marshallingWorker = null; + button3.setOnClickListener( + new android.view.View.OnClickListener("AppClickListener", { + onClick: function () { + // Run the marshalling benchmark on a worker thread so the UI thread + // stays responsive and Android does not raise an ANR. + if (marshallingWorker) { + return; + } + button3.setText("Running Marshalling Benchmark..."); + textView.setText("Running marshalling benchmark, please wait..."); + + marshallingWorker = new Worker("./marshalling-benchmark-worker.js"); + marshallingWorker.onmessage = function (msg) { + textView.setText(msg.data); + button3.setText("Run Marshalling Benchmark"); + marshallingWorker.terminate(); + marshallingWorker = null; + }; + marshallingWorker.onerror = function (err) { + textView.setText("Marshalling benchmark error: " + (err && err.message ? err.message : err)); + button3.setText("Run Marshalling Benchmark"); + marshallingWorker.terminate(); + marshallingWorker = null; + }; + marshallingWorker.postMessage("start"); }, }) ); + + // Report Time To Interactive: process start -> first activity fully built. + require("./tti").reportTTI("app launch"); + + // Benchmark mode (--ez bench true): auto-run the marshalling benchmark on + // launch. Results stream to logcat tagged NS_ENGINE_BENCHMARK. + if (runBench) { + var w = new Worker("./marshalling-benchmark-worker.js"); + w.onmessage = function (msg) { + textView.setText(msg.data); + w.terminate(); + }; + w.onerror = function (err) { + console.log("NS_ENGINE_BENCHMARK_DONE error=" + (err && err.message ? err.message : err)); + w.terminate(); + }; + w.postMessage("start"); + } }; MyActivity = __decorate( [JavaProxy("com.tns.NativeScriptActivity")], diff --git a/test-app/app/src/main/assets/app/benchmark-worker.js b/test-app/app/src/main/assets/app/benchmark-worker.js new file mode 100644 index 00000000..cc330edd --- /dev/null +++ b/test-app/app/src/main/assets/app/benchmark-worker.js @@ -0,0 +1,15 @@ +// Runs the Octane benchmark suite off the main (UI) thread so the app +// stays responsive and Android does not raise an ANR while benchmarking. +const benchmarkRunner = require("./benchmark.js"); + +self.onmessage = function () { + try { + const result = benchmarkRunner.runBenchmark(); + if (typeof globalThis.gc === "function") { + globalThis.gc(); + } + self.postMessage(result); + } catch (e) { + self.postMessage("Benchmark failed: " + (e && e.stack ? e.stack : e)); + } +}; diff --git a/test-app/app/src/main/assets/app/mainpage.js b/test-app/app/src/main/assets/app/mainpage.js index c5c36eca..3511a56d 100644 --- a/test-app/app/src/main/assets/app/mainpage.js +++ b/test-app/app/src/main/assets/app/mainpage.js @@ -73,5 +73,10 @@ require("./tests/testPostFrameCallback"); require("./tests/console/logTests.js"); require('./tests/testURLImpl.js'); require('./tests/testURLSearchParamsImpl.js'); +require('./tests/testURLPattern.js'); +require('./tests/testQueueMicrotask.js'); +require('./tests/testConcurrentAccess.js'); +require('./tests/testClassForNameDiscovery.js'); +require('./tests/testNativeTimers.js'); diff --git a/test-app/app/src/main/assets/app/marshalling-benchmark-worker.js b/test-app/app/src/main/assets/app/marshalling-benchmark-worker.js new file mode 100644 index 00000000..ea726588 --- /dev/null +++ b/test-app/app/src/main/assets/app/marshalling-benchmark-worker.js @@ -0,0 +1,30 @@ +// Runs the runtime marshalling benchmark suite off the main (UI) thread so the +// app stays responsive and Android does not raise an ANR while benchmarking. + +// CRITICAL: a Worker gets its own runtime context, which defaults to verbose +// native logging ON (main.js disables it only on the main thread). Left on, the +// runtime logs "CallJavaMethod ..." for EVERY marshalled call — flooding logcat +// and adding huge per-call overhead that pollutes the measurements. Disable it +// before any benchmarking so we measure marshalling, not logging. +if (typeof __disableVerboseLogging === "function") { + __disableVerboseLogging(); +} + +const benchmarkRunner = require("./marshalling-benchmark.js"); + +// Results are streamed to logcat, tagged NS_ENGINE_BENCHMARK, with a final +// NS_ENGINE_BENCHMARK_DONE line (see marshalling-benchmark.js). The host reads +// them live from adb logcat — no files needed. +self.onmessage = function () { + try { + const result = benchmarkRunner.runBenchmark(); + if (typeof globalThis.gc === "function") { + globalThis.gc(); + } + self.postMessage(result); + } catch (e) { + const msg = "Marshalling benchmark failed: " + (e && e.stack ? e.stack : e); + console.log("NS_ENGINE_BENCHMARK_DONE error=" + msg); + self.postMessage(msg); + } +}; diff --git a/test-app/app/src/main/assets/app/marshalling-benchmark.js b/test-app/app/src/main/assets/app/marshalling-benchmark.js new file mode 100644 index 00000000..8336514b --- /dev/null +++ b/test-app/app/src/main/assets/app/marshalling-benchmark.js @@ -0,0 +1,132 @@ +// Runtime marshalling micro-benchmarks. Exercises every JS <-> Java binding +// path exposed by org.nativescript.Benchmarks and reports per-call cost. +// +// Ported from the standalone NativeScript "TestAppNS" benchmark app so the +// numbers can be tracked against the runtime sources directly. Runs off the UI +// thread via marshalling-benchmark-worker.js. + +// Marker prefixed to every result line so the host can filter benchmark output +// straight out of live logcat (adb logcat | grep NS_ENGINE_BENCHMARK). +var MARKER = "NS_ENGINE_BENCHMARK"; + +// Workers don't get a `performance` global, so read the monotonic clock straight +// from the platform. nanoTime() is unaffected by wall-clock changes. +function now() { + return java.lang.System.nanoTime() / 1e6; // ms +} + +// Runs one entry and returns a formatted result line. `iterations` is the number +// of marshalling calls the action performs; ns/call is derived from it. +function run(entry) { + var n = entry.iterations || 1e6; + var start = now(); + entry.action(); + var ms = now() - start; + var perCall = (ms / n * 1000).toFixed(1); + return { + ms: ms, + line: pad(ms.toFixed(2), 9) + " ms [" + perCall + " ns/call] " + entry.name, + }; +} + +function pad(str, width) { + str = String(str); + while (str.length < width) { + str = " " + str; + } + return str; +} + +function buildSuite() { + var Benchmarks = org.nativescript.Benchmarks; + var b = new org.nativescript.Benchmarks(); + + return [ + { name: "Void Method on instance", action: function () { for (var i = 0; i < 1e6; i++) b.voidMethod1(); } }, + { name: "Field on instance", action: function () { for (var i = 0; i < 1e6; i++) void b.InstanceField; } }, + { name: "Int Field on instance", action: function () { for (var i = 0; i < 1e6; i++) void b.IntFieldInstance; } }, + { name: "Get Static Field", action: function () { for (var i = 0; i < 1e6; i++) void Benchmarks.Field; } }, + { name: "Void Static Method", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.voidMethod(); } }, + { name: "multiply(a, b)", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.multiply(3, 2); } }, + { name: "Pass and return string", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.passAndReturnString("hello world"); } }, + { name: "Return a String", action: function () { for (var i = 0; i < 1e6; i++) void Benchmarks.returnString(); } }, + { name: "Return an Int", action: function () { for (var i = 0; i < 1e6; i++) void Benchmarks.returnInt(); } }, + { name: "Pass a String", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.passString("test"); } }, + { name: "Return a Boolean", action: function () { for (var i = 0; i < 1e6; i++) void Benchmarks.returnBoolean(); } }, + { name: "Return a Double", action: function () { for (var i = 0; i < 1e6; i++) void Benchmarks.returnDouble(); } }, + { name: "Pass an Int", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.passInt(10); } }, + { name: "Pass a Double", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.passDouble(10.5); } }, + { name: "Pass a Boolean", action: function () { for (var i = 0; i < 1e6; i++) Benchmarks.passBoolean(true); } }, + + // Array benchmarks — each call allocates/marshals a Java array, so fewer iterations. + { name: "Return an Int Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) void Benchmarks.returnIntArray(); } }, + { name: "Pass an Int Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) Benchmarks.passIntArray([1, 2, 3, 4, 5]); } }, + { name: "Return a String Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) void Benchmarks.returnStringArray(); } }, + { name: "Pass a String Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) Benchmarks.passStringArray(["one", "two", "three"]); } }, + { name: "Return a Double Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) void Benchmarks.returnDoubleArray(); } }, + { name: "Pass a Double Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) Benchmarks.passDoubleArray([1.1, 2.2, 3.3]); } }, + { name: "Return a Boolean Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) void Benchmarks.returnBooleanArray(); } }, + { name: "Pass a Boolean Array", iterations: 1e4, action: function () { for (var i = 0; i < 1e4; i++) Benchmarks.passBooleanArray([true, false, true]); } }, + + // Indexed access on Java arrays — array fetched once, only element access is measured. + { name: "Int Array[0] read", iterations: 1e6, action: function () { var a = Benchmarks.returnIntArray(); for (var i = 0; i < 1e6; i++) void a[0]; } }, + { name: "String Array[0] read", iterations: 1e6, action: function () { var a = Benchmarks.returnStringArray(); for (var i = 0; i < 1e6; i++) void a[0]; } }, + { name: "Double Array[0] read", iterations: 1e6, action: function () { var a = Benchmarks.returnDoubleArray(); for (var i = 0; i < 1e6; i++) void a[0]; } }, + { name: "Boolean Array[0] read", iterations: 1e6, action: function () { var a = Benchmarks.returnBooleanArray(); for (var i = 0; i < 1e6; i++) void a[0]; } }, + { name: "Int Array[0] write", iterations: 1e6, action: function () { var a = Benchmarks.returnIntArray(); for (var i = 0; i < 1e6; i++) a[0] = 42; } }, + { name: "String Array[0] write", iterations: 1e6, action: function () { var a = Benchmarks.returnStringArray(); for (var i = 0; i < 1e6; i++) a[0] = "x"; } }, + { name: "Double Array[0] write", iterations: 1e6, action: function () { var a = Benchmarks.returnDoubleArray(); for (var i = 0; i < 1e6; i++) a[0] = 1.5; } }, + { name: "Boolean Array[0] write", iterations: 1e6, action: function () { var a = Benchmarks.returnBooleanArray(); for (var i = 0; i < 1e6; i++) a[0] = false; } }, + + // Object and callback marshalling — allocation-heavy, low iteration count. + { name: "Return a Date Object", iterations: 2000, action: function () { for (var i = 0; i < 2000; i++) void Benchmarks.returnDateObject(); } }, + { name: "Pass a Date Object", iterations: 2000, action: function () { var d = new java.util.Date(); for (var i = 0; i < 2000; i++) Benchmarks.passDateObject(d); } }, + { name: "Invoke void callback", iterations: 2000, action: function () { + var cb = new org.nativescript.BenchmarkCallback({ onCallback: function () {} }); + for (var i = 0; i < 2000; i++) Benchmarks.invokeCallback(cb); + } }, + ]; +} + +// Runs the full suite and returns a formatted report string. +function runBenchmark() { + var suite = buildSuite(); + var lines = []; + var totalMs = 0; + + // Every line is tagged MARKER so the host can filter it out of logcat live: + // adb logcat | grep NS_ENGINE_BENCHMARK + // The final NS_ENGINE_BENCHMARK_DONE line signals completion immediately. + var engine = typeof __engine === "string" ? __engine : "V8"; + + for (var i = 0; i < suite.length; i++) { + var entry = suite[i]; + try { + var result = run(entry); + totalMs += result.ms; + lines.push(result.line); + console.log(MARKER + " " + result.line); + } catch (e) { + var failLine = " FAILED " + entry.name + ": " + (e && e.stack ? e.stack : e); + lines.push(failLine); + console.log(MARKER + " " + failLine); + } + // Yield/GC hint between entries to keep allocation noise out of neighbours. + if (typeof globalThis.gc === "function") { + globalThis.gc(); + } + } + + console.log(MARKER + "_DONE engine=" + engine + " total_ms=" + totalMs.toFixed(2) + " entries=" + suite.length); + + var summary = [ + "", + "----------------------------------------------------", + "Engine : " + engine, + "Total : " + totalMs.toFixed(2) + " ms (lower = better)", + ].join("\n"); + + return lines.join("\n") + summary; +} + +exports.runBenchmark = runBenchmark; diff --git a/test-app/app/src/main/assets/app/shared/Workers/EvalWorker.js b/test-app/app/src/main/assets/app/shared/Workers/EvalWorker.js index bb0329d1..a66e5125 100644 --- a/test-app/app/src/main/assets/app/shared/Workers/EvalWorker.js +++ b/test-app/app/src/main/assets/app/shared/Workers/EvalWorker.js @@ -1,4 +1,7 @@ onmessage = function(msg) { - var value = msg.data.value; + // Expose `value` as a global so the eval'd snippet can reference it. A local + // `var value` is invisible to direct eval on engines that don't support + // lexical scope for eval (e.g. static Hermes); a global resolves everywhere. + globalThis.value = msg.data.value; eval(msg.data.eval || ""); } \ No newline at end of file diff --git a/test-app/app/src/main/assets/app/tests/kotlin/companions/testCompanionObjectsSupport.js b/test-app/app/src/main/assets/app/tests/kotlin/companions/testCompanionObjectsSupport.js index 119417d6..a14047a2 100644 --- a/test-app/app/src/main/assets/app/tests/kotlin/companions/testCompanionObjectsSupport.js +++ b/test-app/app/src/main/assets/app/tests/kotlin/companions/testCompanionObjectsSupport.js @@ -90,4 +90,48 @@ describe("Tests Kotlin companion objects support", function () { expect(str).toBe("someString"); }); + + it("Test Kotlin enum class companion object without a name should be supported", function () { + console.log(com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.Companion); + var stringFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.Companion.getStringFromCompanion(); + expect(stringFromCompanion).toBe("testCompanion"); + + var providedStringFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.Companion.getProvidedStringFromCompanion("providedString"); + expect(providedStringFromCompanion).toBe("providedString"); + + var simpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.Companion.getSimpleObjectFromCompanion(); + expect(simpleObjectFromCompanion.getSomeString()).toBe("test"); + + var simpleKotlinObject = new com.tns.tests.kotlin.SimpleKotlinObject(); + var providedSimpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.Companion.getProvidedSimpleObjectFromCompanion(simpleKotlinObject); + expect(simpleKotlinObject.equals(providedSimpleObjectFromCompanion)).toBe(true); + + var stringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.getStringJvmStaticFromCompanion(); + expect(stringJvmStaticFromCompanion).toBe("testCompanion"); + + var providedStringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithCompanionWithNestedClass.getProvidedStringJvmStaticFromCompanion("providedString"); + expect(providedStringJvmStaticFromCompanion).toBe("providedString"); + }); + + it("Test Kotlin enum class named companion object without a name should be supported", function () { + var stringFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithNamedCompanionWithNestedClass.NamedCompanion.getStringFromNamedCompanion(); + expect(stringFromCompanion).toBe("testCompanion"); + + var providedStringFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithNamedCompanionWithNestedClass.NamedCompanion.getProvidedStringFromNamedCompanion("providedString"); + expect(providedStringFromCompanion).toBe("providedString"); + + var simpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithNamedCompanionWithNestedClass.NamedCompanion.getSimpleObjectFromNamedCompanion(); + expect(simpleObjectFromCompanion.getSomeString()).toBe("test"); + + var simpleKotlinObject = new com.tns.tests.kotlin.SimpleKotlinObject(); + var providedSimpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithNamedCompanionWithNestedClass.NamedCompanion.getProvidedSimpleObjectFromNamedCompanion(simpleKotlinObject); + expect(simpleKotlinObject.equals(providedSimpleObjectFromCompanion)).toBe(true); + + var stringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithNamedCompanionWithNestedClass.getStringJvmStaticFromNamedCompanion(); + expect(stringJvmStaticFromCompanion).toBe("testCompanion"); + + var providedStringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinEnumClassWithNamedCompanionWithNestedClass.getProvidedStringJvmStaticFromNamedCompanion("providedString"); + expect(providedStringJvmStaticFromCompanion).toBe("providedString"); + }); + }); \ No newline at end of file diff --git a/test-app/app/src/main/assets/app/tests/testClassForNameDiscovery.js b/test-app/app/src/main/assets/app/tests/testClassForNameDiscovery.js new file mode 100644 index 00000000..4dcad0ee --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/testClassForNameDiscovery.js @@ -0,0 +1,67 @@ +describe("Tests Class.forName discovery of runtime generated classes", function () { + + // Android framework components (e.g. FragmentFactory) resolve classes with + // Class.forName(className, false, context.getClassLoader()). Runtime generated + // proxies must be discoverable through the app's class loader, otherwise + // framework lookups crash with ClassNotFoundException (see issue #1962 / PR #1951). + // + // The extend calls below are built dynamically so the static binding generator + // cannot pre-generate the proxies and DexFactory.resolveClass takes the runtime + // generation + parent class loader injection path. + var ext = "ex" + "tend"; + + // the app's PathClassLoader — the same loader the framework uses, + // e.g. in FragmentFactory.loadFragmentClass via context.getClassLoader() + var appClassLoader = com.tns.Runtime.class.getClassLoader(); + + it("When_extending_a_class_at_runtime_it_should_be_discoverable_through_the_app_class_loader", function () { + var MyObject = java.lang.Object[ext]("ClassForNameDiscoveryObject", { + toString: function () { + return "discoverable"; + } + }); + + var instance = new MyObject(); + var className = instance.getClass().getName(); + + var found = java.lang.Class.forName(className, false, appClassLoader); + + expect(found.getName()).toBe(className); + expect(found.equals(instance.getClass())).toBe(true); + }); + + it("When_implementing_an_interface_at_runtime_it_should_be_discoverable_through_the_app_class_loader", function () { + var MyRunnable = java.lang.Runnable[ext]("ClassForNameDiscoveryRunnable", { + run: function () { + } + }); + + var instance = new MyRunnable(); + var className = instance.getClass().getName(); + + var found = java.lang.Class.forName(className, false, appClassLoader); + + expect(found.getName()).toBe(className); + expect(found.equals(instance.getClass())).toBe(true); + }); + + it("When_a_runtime_generated_class_is_instantiated_through_reflection_it_should_dispatch_to_the_JS_implementation", function () { + var MyObject = java.lang.Object[ext]("ClassForNameDiscoveryInstantiated", { + toString: function () { + return "created via reflection"; + } + }); + + // make sure the implementation object is registered before Java constructs an instance + var instance = new MyObject(); + var className = instance.getClass().getName(); + + // FragmentFactory resolves the class by name and instantiates it through + // reflection. Class.newInstance() invokes the no-arg constructor without + // the varargs marshalling getDeclaredConstructor() would need. + var found = java.lang.Class.forName(className, false, appClassLoader); + var created = found.newInstance(); + + expect(created.toString()).toBe("created via reflection"); + }); +}); diff --git a/test-app/app/src/main/assets/app/tests/testConcurrentAccess.js b/test-app/app/src/main/assets/app/tests/testConcurrentAccess.js new file mode 100644 index 00000000..c8ac32c1 --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/testConcurrentAccess.js @@ -0,0 +1,78 @@ +// WARNING: IF THIS TEST FAILS IT COMPLETELY BREAKS ALL OTHER TESTS! + +describe("Tests concurrent access to JNI", function () { + // Customizable test parameters + const BACKGROUND_THREADS = 5; + const SYNC_CALLS = 2; + const ITERATIONS_PER_CALL = 100; + const TIMEOUT_MS = 3000; + + it("test_high_contention_concurrent_access_with_multiple_objects", (done) => { + console.log('STARTING PROBLEMATIC TEST. THIS MIGHT CRASH OR CAUSE ISSUES IN OTHER TESTS IF IT FAILS. If this is close to the end of the log, check test_high_contention_concurrent_access_with_multiple_objects'); + let callbackInvocations = 0; + + const callback = new com.tns.tests.ConcurrentAccessTest.Callback({ + invoke: ( + list1, + list2, + list3, + list4, + list5, + list6, + list7, + list8, + list9, + list10, + ) => { + callbackInvocations++; + // Assert that accessing size() on any of the lists doesn't throw + expect(() => list1.size()).not.toThrow(); + expect(() => list2.size()).not.toThrow(); + expect(() => list3.size()).not.toThrow(); + expect(() => list4.size()).not.toThrow(); + expect(() => list5.size()).not.toThrow(); + expect(() => list6.size()).not.toThrow(); + expect(() => list7.size()).not.toThrow(); + expect(() => list8.size()).not.toThrow(); + expect(() => list9.size()).not.toThrow(); + expect(() => list10.size()).not.toThrow(); + + // Verify that the lists actually have content + expect(list1.size()).toBe(5); + expect(list2.size()).toBe(5); + expect(list3.size()).toBe(5); + expect(list4.size()).toBe(5); + expect(list5.size()).toBe(5); + expect(list6.size()).toBe(5); + expect(list7.size()).toBe(5); + expect(list8.size()).toBe(5); + expect(list9.size()).toBe(5); + expect(list10.size()).toBe(5); + }, + }); + + // Start multiple background threads + for (let i = 0; i < BACKGROUND_THREADS; i++) { + com.tns.tests.ConcurrentAccessTest.callFromBackgroundThread( + callback, + ITERATIONS_PER_CALL, + ); + } + + // Call synchronously multiple times + for (let i = 0; i < SYNC_CALLS; i++) { + com.tns.tests.ConcurrentAccessTest.callSynchronously( + callback, + ITERATIONS_PER_CALL, + ); + } + + // Wait for all threads to complete + setTimeout(() => { + const expectedInvocations = + (BACKGROUND_THREADS + SYNC_CALLS) * ITERATIONS_PER_CALL; + expect(callbackInvocations).toBe(expectedInvocations); + done(); + }, TIMEOUT_MS); + }); +}); diff --git a/test-app/app/src/main/assets/app/tests/testNativeTimers.js b/test-app/app/src/main/assets/app/tests/testNativeTimers.js index 6dd4cc23..c1ce5767 100644 --- a/test-app/app/src/main/assets/app/tests/testNativeTimers.js +++ b/test-app/app/src/main/assets/app/tests/testNativeTimers.js @@ -1,7 +1,7 @@ describe('native timer', () => { /** @type {global.setTimeout} */ - let setTimeout = global.__ns__setTimeout; + let setTimeout = global.__ns__setTimeout; /** @type {global.setInterval} */ let setInterval = global.__ns__setInterval; /** @type global.setTimeout */ /** @type {global.clearTimeout} */ @@ -94,11 +94,60 @@ describe('native timer', () => { done(); }); }); - xit('frees up resources after complete', (done) => { + // these specs schedule from a java-posted runnable so they run outside any + // timer callback: jasmine chains specs through timer callbacks, and when + // the runtime is built with NS_TIMERS_NESTING_CLAMP the nesting clamp + // (>=5 levels -> 4ms minimum) would otherwise make setTimeout(0) + // legitimately lose to a postDelayed(0) + it('preserves order with java handler posts', (done) => { + const order = []; + const handler = new android.os.Handler(android.os.Looper.myLooper()); + handler.post(new java.lang.Runnable({ + run: () => { + setTimeout(() => order.push(1)); + handler.postDelayed(new java.lang.Runnable({ run: () => order.push(2) }), 0); + setTimeout(() => order.push(3)); + setTimeout(() => { + expect(order.join(',')).toBe('1,2,3'); + done(); + }, 100); + } + })); + }); + + it('interleaves many timers with a java handler post', (done) => { + const order = []; + const handler = new android.os.Handler(android.os.Looper.myLooper()); + handler.post(new java.lang.Runnable({ + run: () => { + for (let i = 0; i < 50; i++) { + setTimeout(() => order.push('t')); + } + handler.postDelayed(new java.lang.Runnable({ run: () => order.push('j') }), 0); + for (let i = 0; i < 50; i++) { + setTimeout(() => order.push('t')); + } + setTimeout(() => { + // the java runnable must land exactly between the two timer batches + expect(order.indexOf('j')).toBe(50); + expect(order.length).toBe(101); + done(); + }, 100); + } + })); + }); + + it('frees up resources after complete', (done) => { let timeout = 0; let interval = 0; let weakRef; - { + // Keep `obj` inside its own function environment rather than a bare block. + // Engines that allocate a single heap environment per function (e.g. Hermes) + // fold block scopes into the enclosing `it` environment, which the surviving + // weakRef-check closure below retains — keeping `obj` alive forever. A nested + // function gets its own environment that is collectable once the timer + // callbacks (its only other retainers) are cleared. + (function setupTimers() { let obj = { value: 0 }; @@ -109,7 +158,7 @@ describe('native timer', () => { interval = setInterval(() => { obj.value++; }, 50); - } + })(); setTimeout(() => { // use !! here because if you pass weakRef.get() it creates a strong reference (side effect of expect) expect(!!weakRef.get()).toBe(true); diff --git a/test-app/app/src/main/assets/app/tests/testQueueMicrotask.js b/test-app/app/src/main/assets/app/tests/testQueueMicrotask.js new file mode 100644 index 00000000..6dba7a5f --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/testQueueMicrotask.js @@ -0,0 +1,35 @@ +describe('queueMicrotask', () => { + it('should be defined as a function', () => { + expect(typeof queueMicrotask).toBe('function'); + }); + + it('should throw TypeError when callback is not a function', () => { + expect(() => queueMicrotask(null)).toThrow(); + expect(() => queueMicrotask(123)).toThrow(); + expect(() => queueMicrotask({})).toThrow(); + }); + + it('runs after current stack but before setTimeout(0)', (done) => { + const order = []; + queueMicrotask(() => order.push('microtask')); + setTimeout(() => { + order.push('timeout'); + expect(order).toEqual(['microtask', 'timeout']); + done(); + }, 0); + // at this point, nothing should have run yet + expect(order.length).toBe(0); + }); + + it('preserves ordering with Promise microtasks', (done) => { + const order = []; + queueMicrotask(() => order.push('qm1')); + Promise.resolve().then(() => order.push('p')); + queueMicrotask(() => order.push('qm2')); + + setTimeout(() => { + expect(order).toEqual(['qm1', 'p', 'qm2']); + done(); + }, 0); + }); +}); diff --git a/test-app/app/src/main/assets/app/tests/testRuntimeImplementedAPIs.js b/test-app/app/src/main/assets/app/tests/testRuntimeImplementedAPIs.js index baa3bcef..12d73644 100644 --- a/test-app/app/src/main/assets/app/tests/testRuntimeImplementedAPIs.js +++ b/test-app/app/src/main/assets/app/tests/testRuntimeImplementedAPIs.js @@ -1,31 +1,40 @@ describe("Runtime exposes", function () { it("__time a low overhead, high resolution, time in ms.", function() { - // Try to get the times using Date.now and __time and compare the results, expect them to be somewhat "close". - // Sometimes GC hits after Date.now is captured but before __time or the vice-versa and the test fails, - // so we are giving it several attempts. - for(var i = 0; i < 10; i++) { - try { - var dateTimeStart = Date.now(); - var timeStart = __time(); - var acc = 0; - var s = android.os.SystemClock.elapsedRealtime(); - for (var i = 0; i < 1000; i++) { - var c = android.os.SystemClock.elapsedRealtime(); - acc += (c - s); - s = c; - } - var dateTimeEnd = Date.now(); - var timeEnd = __time(); - var dateDelta = dateTimeEnd - dateTimeStart; - var timeDelta = timeEnd - timeStart; + // __time() must track the same wall clock as Date.now(): measure the same + // interval with both and expect the two deltas to agree. + // + // This is retried across several attempts because the comparison is sensitive + // to timing noise: the interval is only a few ms, both clocks are ms-granular, + // and a GC (or scheduler hiccup) between the two samples can skew one delta. + // Two things matter for the retry to actually work: + // - the inner loop must NOT reuse the outer counter `i` (that would clobber + // it to the loop bound and run a single attempt), and + // - the pass/fail must be accumulated and asserted ONCE at the end, because + // a failing expect() records a failure rather than throwing, so asserting + // inside the loop can never be "retried away". + var passed = false; + for (var i = 0; i < 10 && !passed; i++) { + var dateTimeStart = Date.now(); + var timeStart = __time(); + var acc = 0; + var s = android.os.SystemClock.elapsedRealtime(); + for (var j = 0; j < 1000; j++) { + var c = android.os.SystemClock.elapsedRealtime(); + acc += (c - s); + s = c; + } + var dateTimeEnd = Date.now(); + var timeEnd = __time(); + var dateDelta = dateTimeEnd - dateTimeStart; + var timeDelta = timeEnd - timeStart; - expect(Math.abs(dateDelta - timeDelta) <= dateDelta * 0.25).toBe(true); - break; - } catch(e) { - if (i == 9) { - throw e; - } + // Allow the larger of 25% of the interval or 2ms of slack to absorb the + // unavoidable ±1ms quantization on each of the two ms-granular clocks. + var tolerance = Math.max(dateDelta * 0.25, 2); + if (Math.abs(dateDelta - timeDelta) <= tolerance) { + passed = true; } } + expect(passed).toBe(true); }); }); \ No newline at end of file diff --git a/test-app/app/src/main/assets/app/tests/testURLImpl.js b/test-app/app/src/main/assets/app/tests/testURLImpl.js index 08c34094..0ca91e14 100644 --- a/test-app/app/src/main/assets/app/tests/testURLImpl.js +++ b/test-app/app/src/main/assets/app/tests/testURLImpl.js @@ -1,31 +1,61 @@ -describe("Test URL ", function () { +describe("URL", function () { + it("throws on invalid URL", function () { + var exceptionCaught = false; + try { + const url = new URL(""); + } catch (e) { + exceptionCaught = true; + } + expect(exceptionCaught).toBe(true); + }); - it("Test invalid URL parsing", function(){ - var exceptionCaught = false; - try { - const url = new URL(''); - }catch(e){ - exceptionCaught = true; - } - expect(exceptionCaught).toBe(true); + it("does not throw on valid URL", function () { + var exceptionCaught = false; + try { + const url = new URL("https://google.com"); + } catch (e) { + exceptionCaught = true; + } + expect(exceptionCaught).toBe(false); }); - it("Test valid URL parsing", function(){ - var exceptionCaught = false; - try { - const url = new URL('https://google.com'); - }catch(e){ - exceptionCaught = true; - } - expect(exceptionCaught).toBe(false); + it("parses simple urls", function () { + const url = new URL("https://google.com"); + expect(url.protocol).toBe("https:"); + expect(url.hostname).toBe("google.com"); + expect(url.pathname).toBe("/"); + expect(url.port).toBe(""); + expect(url.search).toBe(""); + expect(url.hash).toBe(""); + expect(url.username).toBe(""); + expect(url.password).toBe(""); + expect(url.origin).toBe("https://google.com"); + expect(url.searchParams.size).toBe(0); }); + it("parses with undefined base", function () { + const url = new URL("https://google.com", undefined); + expect(url.protocol).toBe("https:"); + expect(url.hostname).toBe("google.com"); + }); - it("Test URL fields", function(){ - var exceptionCaught = false; - const url = new URL('https://google.com'); - expect(url.protocol).toBe('https:'); - expect(url.hostname).toBe('google.com'); + it("parses with null base", function () { + const url = new URL("https://google.com", null); + expect(url.protocol).toBe("https:"); + expect(url.hostname).toBe("google.com"); }); -}); + it("parses query strings", function () { + const url = new URL("https://google.com?q=hello"); + expect(url.search).toBe("?q=hello"); + expect(url.searchParams.get("q")).toBe("hello"); + expect(url.pathname).toBe("/"); + }); + + it("parses query strings with pathname", function () { + const url = new URL("https://google.com/some/path?q=hello"); + expect(url.search).toBe("?q=hello"); + expect(url.searchParams.get("q")).toBe("hello"); + expect(url.pathname).toBe("/some/path"); + }); + }); diff --git a/test-app/app/src/main/assets/app/tests/testURLPattern.js b/test-app/app/src/main/assets/app/tests/testURLPattern.js new file mode 100644 index 00000000..0c2d1c1f --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/testURLPattern.js @@ -0,0 +1,49 @@ + +describe("URLPattern", function () { + it("throws on invalid URLPattern", function () { + var exceptionCaught = false; + try { + const pattern = new URLPattern(1); + } catch (e) { + exceptionCaught = true; + } + expect(exceptionCaught).toBe(true); + }); + + it("does not throw on valid URLPattern", function () { + var exceptionCaught = false; + try { + const pattern = new URLPattern("https://example.com/books/:id"); + } catch (e) { + exceptionCaught = true; + } + expect(exceptionCaught).toBe(false); + }); + + it("parses simple pattern", function () { + const pattern = new URLPattern("https://example.com/books/:id"); + expect(pattern.protocol).toBe("https"); + expect(pattern.hostname).toBe("example.com"); + expect(pattern.pathname).toBe("/books/:id"); + expect(pattern.port).toBe(""); + expect(pattern.search).toBe("*"); + expect(pattern.hash).toBe("*"); + expect(pattern.username).toBe("*"); + expect(pattern.password).toBe("*"); + expect(pattern.hasRegExpGroups).toBe(false); + }); + + + it("parses with undefined base", function () { + const pattern = new URLPattern("https://google.com", undefined); + expect(pattern.protocol).toBe("https"); + expect(pattern.hostname).toBe("google.com"); + }); + + it("parses with null base", function () { + const pattern = new URLPattern("https://google.com", null); + expect(pattern.protocol).toBe("https"); + expect(pattern.hostname).toBe("google.com"); + }); + +}); diff --git a/test-app/app/src/main/assets/app/tests/testURLSearchParamsImpl.js b/test-app/app/src/main/assets/app/tests/testURLSearchParamsImpl.js index 5437250d..b326af9a 100644 --- a/test-app/app/src/main/assets/app/tests/testURLSearchParamsImpl.js +++ b/test-app/app/src/main/assets/app/tests/testURLSearchParamsImpl.js @@ -1,15 +1,16 @@ describe("Test URLSearchParams ", function () { const fooBar = "foo=1&bar=2"; it("Test URLSearchParams keys", function(){ + // keys() returns a spec iterator, not an array — consume it via spread. const params = new URLSearchParams(fooBar); - const keys = params.keys(); + const keys = [...params.keys()]; expect(keys[0]).toBe("foo"); expect(keys[1]).toBe("bar"); }); it("Test URLSearchParams values", function(){ const params = new URLSearchParams(fooBar); - const values = params.values(); + const values = [...params.values()]; expect(values[0]).toBe("1"); expect(values[1]).toBe("2"); }); @@ -17,7 +18,7 @@ describe("Test URLSearchParams ", function () { it("Test URLSearchParams entries", function(){ const params = new URLSearchParams(fooBar); - const entries = params.entries(); + const entries = [...params.entries()]; expect(entries[0][0]).toBe("foo"); expect(entries[0][1]).toBe("1"); @@ -26,6 +27,74 @@ describe("Test URLSearchParams ", function () { }); + it("Test URLSearchParams keys/values/entries return spec iterators", function(){ + const params = new URLSearchParams(fooBar); + // A spec iterator has a next() and is itself iterable. + expect(typeof params.entries().next).toBe("function"); + expect(typeof params.keys().next).toBe("function"); + expect(typeof params.values().next).toBe("function"); + const it = params.entries(); + const first = it.next(); + expect(first.done).toBe(false); + expect(first.value[0]).toBe("foo"); + expect(first.value[1]).toBe("1"); + }); + + it("Test URLSearchParams entries preserves duplicate keys", function(){ + // Regression: the old get_keys()+get() path returned the first value for + // every occurrence of a repeated key. + const params = new URLSearchParams("a=1&a=2&b=3"); + const entries = [...params.entries()]; + expect(entries.length).toBe(3); + expect(entries[0][1]).toBe("1"); + expect(entries[1][1]).toBe("2"); + expect(entries[2][1]).toBe("3"); + expect([...params.values()].join(",")).toBe("1,2,3"); + }); + + it("Test URLSearchParams default iterator aliases entries", function(){ + // The default @@iterator IS the entries method (browser identity). This binding + // installs members per-instance (not on the prototype), so assert on an instance + // AND assert it is actually a function — not the vacuous undefined === undefined. + const params = new URLSearchParams(fooBar); + expect(typeof params[Symbol.iterator]).toBe("function"); + expect(params[Symbol.iterator]).toBe(params.entries); + }); + + it("Test URLSearchParams iterator carries the spec brand", function(){ + const params = new URLSearchParams(fooBar); + expect(Object.prototype.toString.call(params.entries())).toBe("[object URLSearchParams Iterator]"); + }); + + it("Test URLSearchParams iterator is live", function(){ + // Spec iterators reflect mutations made after they are created. + const params = new URLSearchParams("a=1&b=2"); + const it = params.entries(); + expect(it.next().value[0]).toBe("a"); // consume "a" + params.append("c", "3"); // mutate mid-iteration + const rest = []; + let r; + while (!(r = it.next()).done) { + rest.push(r.value[0]); + } + expect(rest.join(",")).toBe("b,c"); // sees the appended "c" + }); + + it("Test URLSearchParams closes the source iterator on a bad pair", function(){ + // On an abrupt completion (a too-long pair) the source iterator must be + // closed, so a generator's finally runs and resource-backed iterables free. + let closed = false; + function* gen() { + try { + yield ["a", "1", "2"]; // 3-element pair → TypeError + } finally { + closed = true; + } + } + expect(function(){ new URLSearchParams(gen()); }).toThrow(); + expect(closed).toBe(true); + }); + it("Test URLSearchParams size", function(){ const params = new URLSearchParams(fooBar); @@ -43,7 +112,27 @@ describe("Test URLSearchParams ", function () { const params = new URLSearchParams(fooBar); params.append("first", "Osei"); params.delete("first"); - expect(params.get("first")).toBe(undefined); + // Spec: get() returns null for a missing name (url.bs:4016). + expect(params.get("first")).toBe(null); + }); + + it("Test URLSearchParams get returns null for a missing name", function(){ + // Spec get(name): "...otherwise null" (url.bs:4016). + const params = new URLSearchParams("a=1"); + expect(params.get("missing")).toBe(null); + }); + + it("Test URLSearchParams delete with value removes only matching pairs", function(){ + // Spec delete(name, value): when value is given, remove only tuples + // matching BOTH name and value (url.bs:4000). The value is a USVString, + // so a non-string (the number 1) coerces to "1". + const params = new URLSearchParams("a=1&a=2&a=1&b=1"); + params.delete("a", 1); + expect(params.getAll("a").join(",")).toBe("2"); + expect(params.getAll("b").join(",")).toBe("1"); + // Single-arg delete still removes every pair with that name. + params.delete("a"); + expect(params.has("a")).toBe(false); }); @@ -52,6 +141,47 @@ describe("Test URLSearchParams ", function () { expect(params.has("foo")).toBe(true); }); + it("Test URLSearchParams has with value matches name and value", function(){ + // Spec has(name, value): true only for a tuple matching BOTH (url.bs:4028). + // The value is a USVString, so non-strings (number, boolean) coerce. + const params = new URLSearchParams("a=1&a=2&flag=true"); + expect(params.has("a", "1")).toBe(true); + expect(params.has("a", 2)).toBe(true); // number coerces to "2" + expect(params.has("a", "3")).toBe(false); + expect(params.has("flag", true)).toBe(true); // boolean coerces to "true" + expect(params.has("missing", "1")).toBe(false); + // Single-arg has still matches by name only. + expect(params.has("a")).toBe(true); + }); + + it("Test URLSearchParams has/delete throw when the value cannot be coerced", function(){ + // The value argument is a USVString; a Symbol (or a throwing toString) + // cannot convert, so the call must throw rather than silently matching "" + // (WebIDL USVString conversion, url.bs:4000 / 4028). + const params = new URLSearchParams("a=1"); + expect(function(){ params.has("a", Symbol("x")); }).toThrow(); + expect(function(){ params.delete("a", Symbol("x")); }).toThrow(); + }); + + it("Test URLSearchParams has/delete treat an explicit undefined value as omitted", function(){ + // Per WPT (urlsearchparams-has / -delete "respects undefined as second + // arg"), an explicit `undefined` second argument is treated as omitted + // (name-only), NOT as the string "undefined". + const params = new URLSearchParams("a=b&a=d&c&e&"); + expect(params.has("a", "b")).toBe(true); + expect(params.has("a", "c")).toBe(false); + expect(params.has("a", undefined)).toBe(true); // undefined -> name-only + + const del = new URLSearchParams(); + del.append("a", "b"); + del.append("a", "c"); + del.append("b", "c"); + del.append("b", "d"); + del.delete("b", "c"); + del.delete("a", undefined); // undefined -> delete by name + expect(del.toString()).toBe("b=d"); + }); + it("Test URLSearchParams changes propagates to URL parent", function(){ const toBe = 'https://github.com/triniwiz?first=Osei'; const url = new URL('https://github.com/triniwiz'); @@ -61,4 +191,332 @@ describe("Test URLSearchParams ", function () { expect(url.toString()).toBe(toBe); }); + it("Test URLSearchParams forEach", function(){ + const params = new URLSearchParams(fooBar); + const results = []; + params.forEach((value, key, searchParams) => { + results.push({ key, value }); + expect(searchParams).toBe(params); + }); + expect(results.length).toBe(2); + expect(results[0].key).toBe("foo"); + expect(results[0].value).toBe("1"); + expect(results[1].key).toBe("bar"); + expect(results[1].value).toBe("2"); + }); + + it("Test URLSearchParams forEach with URL", function(){ + const url = new URL('https://example.com?si=abc123&name=test'); + const results = []; + url.searchParams.forEach((value, key) => { + results.push({ key, value }); + }); + expect(results.length).toBe(2); + expect(results[0].key).toBe("si"); + expect(results[0].value).toBe("abc123"); + expect(results[1].key).toBe("name"); + expect(results[1].value).toBe("test"); + }); + + it("Test URLSearchParams forEach with thisArg", function(){ + const params = new URLSearchParams(fooBar); + const context = { results: [] }; + params.forEach(function(value, key) { + this.results.push({ key, value }); + }, context); + expect(context.results.length).toBe(2); + expect(context.results[0].key).toBe("foo"); + expect(context.results[0].value).toBe("1"); + }); + + it("Test URLSearchParams forEach with duplicate keys", function(){ + const params = new URLSearchParams("foo=1&foo=2&bar=3"); + const results = []; + params.forEach((value, key) => { + results.push({ key, value }); + }); + expect(results.length).toBe(3); + expect(results[0].key).toBe("foo"); + expect(results[0].value).toBe("1"); + expect(results[1].key).toBe("foo"); + expect(results[1].value).toBe("2"); + expect(results[2].key).toBe("bar"); + expect(results[2].value).toBe("3"); + }); + + it("Test URLSearchParams from record object", function(){ + const params = new URLSearchParams({ foo: "1", bar: "2" }); + expect(params.get("foo")).toBe("1"); + expect(params.get("bar")).toBe("2"); + expect(params.size).toBe(2); + // A plain object must expand to its entries, not collapse into a + // single "[object Object]" key. + expect(params.has("[object Object]")).toBe(false); + }); + + it("Test URLSearchParams from record serializes every pair in toString", function(){ + const params = new URLSearchParams({ one: "1", two: "2" }); + expect(params.toString()).toBe("one=1&two=2"); + }); + + it("Test URLSearchParams from record coerces values to strings", function(){ + const params = new URLSearchParams({ a: 1, b: true }); + expect(params.get("a")).toBe("1"); + expect(params.get("b")).toBe("true"); + }); + + it("Test URLSearchParams from record encodes special characters", function(){ + const params = new URLSearchParams({ q: "a b&c" }); + expect(params.get("q")).toBe("a b&c"); + expect(params.toString()).toBe("q=a+b%26c"); + }); + + it("Test URLSearchParams from array of pairs", function(){ + const params = new URLSearchParams([["foo", "1"], ["bar", "2"], ["foo", "3"]]); + expect(params.get("foo")).toBe("1"); + expect(params.getAll("foo").length).toBe(2); + expect(params.get("bar")).toBe("2"); + expect(params.size).toBe(3); + }); + + it("Test URLSearchParams empty record and no-arg produce empty query", function(){ + expect(new URLSearchParams().toString()).toBe(""); + expect(new URLSearchParams({}).toString()).toBe(""); + }); + + it("Test URLSearchParams from record throws when a value cannot be coerced to a string", function(){ + // Per spec the record/sequence init coerces every value to a USVString; + // a value that cannot convert (a Symbol here) must throw rather than + // silently dropping or emptying the entry. + expect(function(){ new URLSearchParams({ bad: Symbol("x") }); }).toThrow(); + }); + + // --- Iterable (sequence) init: any iterable of pairs, not only arrays. --- + + it("Test URLSearchParams from a Map", function(){ + const params = new URLSearchParams(new Map([["a", "1"], ["b", "2"]])); + expect(params.toString()).toBe("a=1&b=2"); + }); + + it("Test URLSearchParams from a Set of pairs", function(){ + const params = new URLSearchParams(new Set([["x", "1"], ["y", "2"]])); + expect(params.toString()).toBe("x=1&y=2"); + }); + + it("Test URLSearchParams copy-constructs from another URLSearchParams", function(){ + // A URLSearchParams is iterable, so per spec it resolves to the sequence + // (copy) form — including duplicate keys, which proves the @@iterator walks + // entries rather than collapsing them. + const source = new URLSearchParams("a=1&a=2&b=3"); + const copy = new URLSearchParams(source); + expect(copy.toString()).toBe("a=1&a=2&b=3"); + expect(copy.getAll("a").length).toBe(2); + }); + + it("Test URLSearchParams from a generator of pairs", function(){ + function* pairs() { + yield ["a", "1"]; + yield ["b", "2"]; + } + const params = new URLSearchParams(pairs()); + expect(params.toString()).toBe("a=1&b=2"); + }); + + it("Test URLSearchParams from sequence with non-array inner pairs", function(){ + // Each pair need only be a 2-element iterable, not specifically an array. + // A Set is iterable but not an Array, so it exercises the inner iterator path. + const params = new URLSearchParams([new Set(["k", "v"])]); + expect(params.get("k")).toBe("v"); + }); + + it("Test URLSearchParams sequence init throws on a too-long pair", function(){ + expect(function(){ new URLSearchParams([["a", "1", "2"]]); }).toThrow(); + }); + + it("Test URLSearchParams sequence init throws on a too-short pair", function(){ + expect(function(){ new URLSearchParams([["a"]]); }).toThrow(); + }); + + it("Test URLSearchParams sequence init throws on a non-iterable element", function(){ + expect(function(){ new URLSearchParams([null]); }).toThrow(); + expect(function(){ new URLSearchParams([1]); }).toThrow(); + }); + + it("Test URLSearchParams sequence init throws on a primitive string element", function(){ + // WebIDL converts each element to sequence, whose first step throws + // when the element is not an Object. A 2-code-point string must NOT be accepted + // as the pair ("a","b"). + expect(function(){ new URLSearchParams(["ab"]); }).toThrow(); + }); + + it("Test URLSearchParams sequence init accepts a String-object element", function(){ + // A String *object* IS an Object and is iterable, so it is a valid 2-char pair. + const params = new URLSearchParams([new String("ab")]); + expect(params.get("a")).toBe("b"); + }); + + it("Test URLSearchParams throws when @@iterator is present but not callable", function(){ + // Per WebIDL GetMethod, a non-callable @@iterator is a TypeError, not a + // silent fall-back to the record form. + expect(function(){ new URLSearchParams({ [Symbol.iterator]: 5 }); }).toThrow(); + }); + + // --- The type itself is iterable. --- + + it("Test URLSearchParams is spread-iterable via Symbol.iterator", function(){ + const params = new URLSearchParams("a=1&b=2"); + const entries = [...params]; + expect(entries.length).toBe(2); + expect(entries[0][0]).toBe("a"); + expect(entries[0][1]).toBe("1"); + expect(entries[1][0]).toBe("b"); + expect(entries[1][1]).toBe("2"); + }); + + it("Test URLSearchParams works in a for..of loop", function(){ + const params = new URLSearchParams("a=1&a=2"); + const seen = []; + for (const [key, value] of params) { + seen.push(key + "=" + value); + } + expect(seen.length).toBe(2); + expect(seen[0]).toBe("a=1"); + expect(seen[1]).toBe("a=2"); + }); + + // --- Primitive init: coerced to USVString, then parsed. --- + + it("Test URLSearchParams from a number", function(){ + expect(new URLSearchParams(123).toString()).toBe("123="); + }); + + it("Test URLSearchParams from a boolean", function(){ + expect(new URLSearchParams(true).toString()).toBe("true="); + }); + + it("Test URLSearchParams from a bigint", function(){ + expect(new URLSearchParams(10n).toString()).toBe("10="); + }); + + it("Test URLSearchParams strips a single leading question mark", function(){ + expect(new URLSearchParams("?a=1").get("a")).toBe("1"); + }); + + it("Test URLSearchParams throws when init is a Symbol", function(){ + expect(function(){ new URLSearchParams(Symbol("x")); }).toThrow(); + }); + + it("Test URLSearchParams from undefined or no argument is empty", function(){ + expect(new URLSearchParams(undefined).toString()).toBe(""); + expect(new URLSearchParams().toString()).toBe(""); + }); + + it("Test URLSearchParams from null parses as the string null", function(){ + // The IDL union has no null special case (the type is not nullable and + // a record is not a dictionary), so null coerces like any primitive. + const params = new URLSearchParams(null); + expect(params.toString()).toBe("null="); + expect(params.get("null")).toBe(""); + }); + + it("Test URLSearchParams throws when a record key is a Symbol", function(){ + // Per WebIDL record conversion every own enumerable key is converted to + // a USVString, and converting a Symbol throws. + expect(function(){ new URLSearchParams({ a: "1", [Symbol("x")]: "v" }); }).toThrow(); + }); + + // --- The name argument is a USVString: coerced, not assumed. --- + + it("Test URLSearchParams coerces a non-string name in get/getAll/has/delete", function(){ + const params = new URLSearchParams("1=a&true=b&null=c"); + expect(params.get(1)).toBe("a"); + expect(params.getAll(1).length).toBe(1); + expect(params.getAll(1)[0]).toBe("a"); + expect(params.has(true)).toBe(true); + expect(params.get(null)).toBe("c"); + params.delete(true); + expect(params.has("true")).toBe(false); + expect(params.has("1")).toBe(true); + }); + + it("Test URLSearchParams coerces an object name via toString", function(){ + const params = new URLSearchParams("a=1"); + const name = { toString: function(){ return "a"; } }; + expect(params.get(name)).toBe("1"); + expect(params.has(name)).toBe(true); + params.delete(name); + expect(params.has("a")).toBe(false); + }); + + it("Test URLSearchParams throws when the name is a Symbol", function(){ + const params = new URLSearchParams("a=1"); + expect(function(){ params.get(Symbol("x")); }).toThrow(); + expect(function(){ params.getAll(Symbol("x")); }).toThrow(); + expect(function(){ params.has(Symbol("x")); }).toThrow(); + expect(function(){ params.delete(Symbol("x")); }).toThrow(); + expect(params.get("a")).toBe("1"); + }); + + it("Test URLSearchParams coerces non-string arguments in append and set", function(){ + const params = new URLSearchParams(); + params.append(1, 2); + expect(params.get("1")).toBe("2"); + params.set(true, { toString: function(){ return "x"; } }); + expect(params.get("true")).toBe("x"); + params.append("a", null); + expect(params.get("a")).toBe("null"); + }); + + it("Test URLSearchParams append and set throw when an argument is a Symbol", function(){ + const params = new URLSearchParams("a=1"); + expect(function(){ params.append(Symbol("x"), "v"); }).toThrow(); + expect(function(){ params.append("k", Symbol("x")); }).toThrow(); + expect(function(){ params.set(Symbol("x"), "v"); }).toThrow(); + expect(function(){ params.set("k", Symbol("x")); }).toThrow(); + // Nothing was appended or replaced by the failed calls. + expect(params.toString()).toBe("a=1"); + }); + + // --- Brand checks: iterators require a genuine receiver. --- + + it("Test URLSearchParams entries/keys/values throw on a foreign receiver", function(){ + const params = new URLSearchParams("a=1"); + expect(function(){ params.entries.call({}); }).toThrow(); + expect(function(){ params.keys.call({}); }).toThrow(); + expect(function(){ params.values.call({}); }).toThrow(); + }); + + it("Test URLSearchParams methods throw on a foreign receiver", function(){ + const params = new URLSearchParams("a=1"); + expect(function(){ params.get.call({}, "a"); }).toThrow(); + expect(function(){ params.getAll.call({}, "a"); }).toThrow(); + expect(function(){ params.has.call({}, "a"); }).toThrow(); + expect(function(){ params.append.call({}, "a", "b"); }).toThrow(); + expect(function(){ params.set.call({}, "a", "b"); }).toThrow(); + expect(function(){ params.delete.call({}, "a"); }).toThrow(); + expect(function(){ params.forEach.call({}, function(){}); }).toThrow(); + expect(function(){ params.sort.call({}); }).toThrow(); + expect(function(){ params.toString.call({}); }).toThrow(); + }); + + it("Test URLSearchParams iterator next() throws on a foreign receiver", function(){ + const params = new URLSearchParams("a=1"); + const iterator = params.entries(); + const next = iterator.next; + expect(function(){ next.call({}); }).toThrow(); + // The iterator keeps working when invoked correctly afterwards. + const first = iterator.next(); + expect(first.done).toBe(false); + expect(first.value[0]).toBe("a"); + }); + + it("Test URLSearchParams entries retargets to another instance receiver", function(){ + const a = new URLSearchParams("a=1"); + const b = new URLSearchParams("b=2"); + const entries = [...a.entries.call(b)]; + expect(entries.length).toBe(1); + expect(entries[0][0]).toBe("b"); + expect(entries[0][1]).toBe("2"); + }); + }); diff --git a/test-app/app/src/main/assets/app/tti.js b/test-app/app/src/main/assets/app/tti.js new file mode 100644 index 00000000..232b43a5 --- /dev/null +++ b/test-app/app/src/main/assets/app/tti.js @@ -0,0 +1,40 @@ +// Time To Interactive (TTI) on app launch. +// +// Measures the wall time from process start to the moment the app becomes +// interactive (the runtime has booted, the main JS module has executed, and the +// first activity has finished building its UI). This captures the full cold-start +// cost the user perceives: native runtime init + JS engine warm-up + metadata +// resolution + app JS execution. +// +// Both endpoints use SystemClock.uptimeMillis() so the delta excludes deep-sleep +// and is immune to wall-clock adjustments. android.os.Process.getStartUptimeMillis() +// (API 24+) is the process fork time on the same clock. + +function processStartUptimeMs() { + // API 24+. Falls back to app-class-load time captured below if unavailable. + try { + return android.os.Process.getStartUptimeMillis(); + } catch (e) { + return -1; + } +} + +// Report TTI. `label` describes the interactive milestone that was just reached. +// Returns the measured value in ms (or -1 if it could not be computed). +function reportTTI(label) { + var startMs = processStartUptimeMs(); + var nowMs = android.os.SystemClock.uptimeMillis(); + + if (startMs < 0) { + console.log("[TTI] process start time unavailable (API < 24) — cannot measure launch TTI"); + return -1; + } + + var ttiMs = nowMs - startMs; + var engine = typeof __engine === "string" ? __engine : "V8"; + // Tagged so the host can read it straight from live logcat. + console.log("NS_ENGINE_BENCHMARK_TTI engine=" + engine + " tti_ms=" + ttiMs + " label=" + (label || "interactive")); + return ttiMs; +} + +exports.reportTTI = reportTTI; diff --git a/test-app/app/src/main/assets/internal/ts_helpers.js b/test-app/app/src/main/assets/internal/ts_helpers.js index 83823ac8..49562aaa 100644 --- a/test-app/app/src/main/assets/internal/ts_helpers.js +++ b/test-app/app/src/main/assets/internal/ts_helpers.js @@ -12,10 +12,10 @@ d; if ( - typeof global.Reflect === "object" && - typeof global.Reflect.decorate === "function" + typeof globalThis.Reflect === "object" && + typeof globalThis.Reflect.decorate === "function" ) { - r = global.Reflect.decorate(decorators, target, key, desc); + r = globalThis.Reflect.decorate(decorators, target, key, desc); } else { for (var i = decorators.length - 1; i >= 0; i--) { if ((d = decorators[i])) { @@ -54,8 +54,13 @@ }; var __extends = function (Child, Parent) { + // Detect a native class by a brand the runtime stamps on its native extend() rather than + // sniffing Parent.extend.toString() for "[native code]": in release/bytecode builds every + // function (JS or native) stringifies to "[native code]", which misdetects a plain JS class + // with a static "extend" method as native. The brand works for source and bytecode alike. var extendNativeClass = - !!Parent.extend && Parent.extend.toString().indexOf("[native code]") > -1; + !!Parent.extend && Parent.extend.__isNativeExtend__ === true; + if (!extendNativeClass) { __extends_ts(Child, Parent); return; @@ -189,82 +194,25 @@ } }; } + Object.defineProperty(globalThis, "__native", { value: __native }); + Object.defineProperty(globalThis, "__extends", { value: __extends }); + Object.defineProperty(globalThis, "__decorate", { value: __decorate }); - Object.defineProperty(global, "__native", { value: __native }); - Object.defineProperty(global, "__extends", { value: __extends }); - Object.defineProperty(global, "__decorate", { value: __decorate }); - if (!global.__ns__worker) { - global.JavaProxy = JavaProxy; - } - global.Interfaces = Interfaces; - if (global.WeakRef && !global.WeakRef.prototype.get) { - global.WeakRef.prototype.get = global.WeakRef.prototype.deref; + if (!globalThis.__ns__worker) { + globalThis.JavaProxy = JavaProxy; } + globalThis.Interfaces = Interfaces; - global.setNativeArrayProp = (target, prop, value, receiver) => { - if (typeof prop !== "symbol" && !isNaN(prop)) { - receiver.setValueAtIndex(parseInt(prop), value); - return true; - } - target[prop] = value; - return true; - }; - - global.getNativeArrayProp = (target, prop, receiver) => { - if (typeof prop !== "symbol" && !isNaN(prop)) { - return receiver.getValueAtIndex(parseInt(prop)); - } - - if (prop === Symbol.iterator) { - var index = 0; - const l = target.length; - return function () { - return { - next: function () { - if (index < l) { - return { - value: receiver.getValueAtIndex(index++), - done: false, - }; - } else { - return { done: true }; - } - }, - }; - }; - } - if (prop === "map") { - return function (callback) { - const values = receiver.getAllValues(); - const result = []; - const l = target.length; - for (var i = 0; i < l; i++) { - result.push(callback(values[i], i, target)); - } - return result; - }; - } - - if (prop === "toString") { - return function () { - const result = receiver.getAllValues(); - return result.join(","); - }; - } + if (globalThis.WeakRef && !globalThis.WeakRef.prototype.get) { + globalThis.WeakRef.prototype.get = globalThis.WeakRef.prototype.deref; + } - if (prop === "forEach") { - return function (callback) { - const values = receiver.getAllValues(); - const l = values.length; - for (var i = 0; i < l; i++) { - callback(values[i], i, target); - } - }; - } - return target[prop]; - }; + // Native array access: numeric indexing and the map/forEach/toString/ + // Symbol.iterator helpers are now implemented natively (see MetadataNode's + // array prototype + the host object's indexed accessors), so the old JS + // getNativeArrayProp/setNativeArrayProp helpers are gone. function findInPrototypeChain(obj, prop) { while (obj) { @@ -292,8 +240,11 @@ get: function (target, prop) { if (prop === EXTERNAL_PROP) return this[EXTERNAL_PROP]; if (prop === REFERENCE_PROP_JSC) return this[REFERENCE_PROP_JSC]; - if (target.__is__javaArray) { - return global.getNativeArrayProp(target, prop, target); + // Numeric indices go straight to the native element accessor; the + // map/forEach/toString/Symbol.iterator helpers live on the array + // prototype now, so everything else just forwards to the target. + if (target.__is__javaArray && typeof prop !== "symbol" && !isNaN(prop)) { + return target.getValueAtIndex(parseInt(prop)); } return target[prop]; }, @@ -477,6 +428,15 @@ handledPromise(promise); } }; + } else if (globalThis.__engine === "JSC") { + // JSC's unhandled-rejection callback only fires for rejections that go + // unhandled (there is no "handled later" retraction event), and it is + // invoked with (promise, reason). + globalThis.onUnhandledPromiseRejectionTracker = (promise, reason) => { + hasBeenNotifiedProperty.set(promise, false); + const error = makeRejectionError(reason); + unhandledPromise(promise, error); + }; } else if (globalThis.__engine === "Hermes") { HermesInternal.enablePromiseRejectionTracker({ allRejections: true, diff --git a/test-app/app/src/main/java/com/tns/AndroidJsV8Inspector.java b/test-app/app/src/main/java/com/tns/AndroidJsV8Inspector.java index 1a285e19..d1cb270b 100644 --- a/test-app/app/src/main/java/com/tns/AndroidJsV8Inspector.java +++ b/test-app/app/src/main/java/com/tns/AndroidJsV8Inspector.java @@ -37,6 +37,8 @@ class AndroidJsV8Inspector { protected native final void dispatchMessage(String message); + private native String handleMessageOnSocketThread(String message); + private Handler mainHandler; private final Object debugBrkLock; @@ -294,6 +296,27 @@ protected void onMessage(final NanoWSD.WebSocketFrame message) { Log.d("Inspector", "To dbg backend: " + message.getTextPayload() + " ThreadId:" + Thread.currentThread().getId()); } + // Network.loadNetworkResource / IO.read / IO.close are served from + // disk on this thread so source maps load even while the isolate is + // paused at a breakpoint or busy running JS; Target domain commands + // and worker-session messages (top-level sessionId) are routed here + // too. Debugger.pause schedules a V8 interrupt and still flows + // through the queue. A null return means "not handled" (queue it); + // an empty string means handled with nothing left to send. + String fastPathResponse = handleMessageOnSocketThread(message.getTextPayload()); + if (fastPathResponse != null) { + if (!fastPathResponse.isEmpty()) { + try { + send(fastPathResponse); + } catch (IOException e) { + if (com.tns.Runtime.isDebuggable()) { + e.printStackTrace(); + } + } + } + return; + } + inspectorMessages.offer(message.getTextPayload()); if (!AndroidJsV8Inspector.ReadyToProcessMessages.get()) { diff --git a/test-app/app/src/main/java/com/tns/RuntimeHelper.java b/test-app/app/src/main/java/com/tns/RuntimeHelper.java index 90a64f16..fa154296 100644 --- a/test-app/app/src/main/java/com/tns/RuntimeHelper.java +++ b/test-app/app/src/main/java/com/tns/RuntimeHelper.java @@ -123,6 +123,18 @@ public static Runtime initRuntime(Context context) { ClassLoader classLoader = context.getClassLoader(); File dexDir = new File(rootDir, "code_cache/secondary-dexes"); + if (!dexDir.exists()) { + dexDir.mkdirs(); + } + if (!dexDir.exists() || !dexDir.canWrite()) { + if (logger.isEnabled()) { + logger.write("Unable to use dex dir: " + dexDir.getAbsolutePath() + ", falling back to files/secondary-dexes"); + } + dexDir = new File(appDir, "secondary-dexes"); + if (!dexDir.exists()) { + dexDir.mkdirs(); + } + } String dexThumb = null; try { dexThumb = Util.getDexThumb(context); diff --git a/test-app/app/src/main/java/com/tns/tests/ConcurrentAccessTest.java b/test-app/app/src/main/java/com/tns/tests/ConcurrentAccessTest.java new file mode 100644 index 00000000..acd9d853 --- /dev/null +++ b/test-app/app/src/main/java/com/tns/tests/ConcurrentAccessTest.java @@ -0,0 +1,76 @@ +package com.tns.tests; + +import java.util.ArrayList; + +public class ConcurrentAccessTest { + + public interface Callback { + void invoke(ArrayList list1, ArrayList list2, ArrayList list3, ArrayList list4, ArrayList list5, + ArrayList list6, ArrayList list7, ArrayList list8, ArrayList list9, ArrayList list10); + } + + public interface ErrorCallback { + void onError(Throwable error); + } + + /** + * Calls the callback from a background thread multiple times. + * @param callback The callback to invoke + * @param times Number of times to call the callback (default 50) + */ + public static void callFromBackgroundThread(final Callback callback, final int times) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < times; i++) { + invokeCallbackWithArrayLists(callback, i); + } + } + }); + thread.start(); + } + + /** + * Calls the callback synchronously from the current thread. + * @param callback The callback to invoke + * @param times Number of times to call the callback (default 50) + */ + public static void callSynchronously(Callback callback, int times) { + for (int i = 0; i < times; i++) { + invokeCallbackWithArrayLists(callback, i); + } + } + + /** + * Helper method that creates 10 ArrayLists and invokes the callback with them. + * Each ArrayList contains some data based on the iteration number. + */ + private static void invokeCallbackWithArrayLists(Callback callback, int iteration) { + ArrayList list1 = new ArrayList<>(); + ArrayList list2 = new ArrayList<>(); + ArrayList list3 = new ArrayList<>(); + ArrayList list4 = new ArrayList<>(); + ArrayList list5 = new ArrayList<>(); + ArrayList list6 = new ArrayList<>(); + ArrayList list7 = new ArrayList<>(); + ArrayList list8 = new ArrayList<>(); + ArrayList list9 = new ArrayList<>(); + ArrayList list10 = new ArrayList<>(); + + // Add some data to each list + for (int i = 0; i < 5; i++) { + list1.add(iteration * 10 + i); + list2.add(iteration * 10 + i + 1); + list3.add(iteration * 10 + i + 2); + list4.add(iteration * 10 + i + 3); + list5.add(iteration * 10 + i + 4); + list6.add(iteration * 10 + i + 5); + list7.add(iteration * 10 + i + 6); + list8.add(iteration * 10 + i + 7); + list9.add(iteration * 10 + i + 8); + list10.add(iteration * 10 + i + 9); + } + + callback.invoke(list1, list2, list3, list4, list5, list6, list7, list8, list9, list10); + } +} \ No newline at end of file diff --git a/test-app/app/src/main/java/com/tns/tests/kotlin/companions/KotlinEnumClassWithCompanionWithNestedClass.kt b/test-app/app/src/main/java/com/tns/tests/kotlin/companions/KotlinEnumClassWithCompanionWithNestedClass.kt new file mode 100644 index 00000000..0934c40c --- /dev/null +++ b/test-app/app/src/main/java/com/tns/tests/kotlin/companions/KotlinEnumClassWithCompanionWithNestedClass.kt @@ -0,0 +1,28 @@ +package com.tns.tests.kotlin.companions + +import com.tns.tests.kotlin.SimpleKotlinObject + + +enum class KotlinEnumClassWithCompanionWithNestedClass { + TEST_ENTRY; + + companion object { + fun getStringFromCompanion(): String = "testCompanion" + fun getProvidedStringFromCompanion(providedString: String) = providedString + + fun getSimpleObjectFromCompanion(): SimpleKotlinObject = SimpleKotlinObject() + fun getProvidedSimpleObjectFromCompanion(providedSimpleObject: SimpleKotlinObject) = + providedSimpleObject + + @JvmStatic + fun getStringJvmStaticFromCompanion(): String = "testCompanion" + + @JvmStatic + fun getProvidedStringJvmStaticFromCompanion(providedString: String) = providedString + + class TestClass { + + } + } + +} diff --git a/test-app/app/src/main/java/com/tns/tests/kotlin/companions/KotlinEnumClassWithNamedCompanionWithNestedClass.kt b/test-app/app/src/main/java/com/tns/tests/kotlin/companions/KotlinEnumClassWithNamedCompanionWithNestedClass.kt new file mode 100644 index 00000000..e3abc190 --- /dev/null +++ b/test-app/app/src/main/java/com/tns/tests/kotlin/companions/KotlinEnumClassWithNamedCompanionWithNestedClass.kt @@ -0,0 +1,29 @@ +package com.tns.tests.kotlin.companions + +import com.tns.tests.kotlin.SimpleKotlinObject + +enum class KotlinEnumClassWithNamedCompanionWithNestedClass { + TEST_ENTRY; + + companion object NamedCompanion { + fun getStringFromNamedCompanion(): String = "testCompanion" + fun getProvidedStringFromNamedCompanion(providedString: String) = providedString + + fun getSimpleObjectFromNamedCompanion(): SimpleKotlinObject = SimpleKotlinObject() + fun getProvidedSimpleObjectFromNamedCompanion(providedSimpleObject: SimpleKotlinObject) = + providedSimpleObject + + @JvmStatic + fun getStringJvmStaticFromNamedCompanion(): String = "testCompanion" + + @JvmStatic + fun getProvidedStringJvmStaticFromNamedCompanion(providedString: String) = + providedString + + + class TestClass { + + } + } + +} \ No newline at end of file diff --git a/test-app/app/src/main/java/org/nativescript/BenchmarkCallback.java b/test-app/app/src/main/java/org/nativescript/BenchmarkCallback.java new file mode 100644 index 00000000..ecfea2e5 --- /dev/null +++ b/test-app/app/src/main/java/org/nativescript/BenchmarkCallback.java @@ -0,0 +1,7 @@ +package org.nativescript; + +// Interface implemented from JS to measure the cost of invoking a JS callback +// through a Java interface (see Benchmarks.invokeCallback). +public interface BenchmarkCallback { + void onCallback(); +} diff --git a/test-app/app/src/main/java/org/nativescript/Benchmarks.java b/test-app/app/src/main/java/org/nativescript/Benchmarks.java new file mode 100644 index 00000000..bd0d61b0 --- /dev/null +++ b/test-app/app/src/main/java/org/nativescript/Benchmarks.java @@ -0,0 +1,85 @@ +package org.nativescript; + +// Marshalling micro-benchmark surface exercised from JS by +// app/src/main/assets/app/marshalling-benchmark.js. Each method/field targets a +// specific JS <-> Java marshalling path (primitives, strings, arrays, objects, +// callbacks) so the runtime's binding overhead can be measured per call. +public class Benchmarks { + public Benchmarks() {} + + public static String Field = "Field"; + + public static void voidMethod() {} + + public void voidMethod1() {} + + public String InstanceField = "Field"; + + public int IntFieldInstance = 1; + + public static String passAndReturnString(String str) { + return str; + } + + public static int multiply(int a, int b) { + return a + b; + } + + public static String returnString() { + return "Method"; + } + + public static int returnInt() { + return 10; + } + + public static void passString(String arg) {} + + public static boolean returnBoolean() { + return true; + } + + public static double returnDouble() { + return 10.5; + } + + public static void passInt(int arg) {} + + public static void passDouble(double arg) {} + + public static void passBoolean(boolean arg) {} + + public static int[] returnIntArray() { + return new int[]{1, 2, 3, 4, 5}; + } + + public static void passIntArray(int[] arg) {} + + public static String[] returnStringArray() { + return new String[]{"one", "two", "three"}; + } + + public static void passStringArray(String[] arg) {} + + public static double[] returnDoubleArray() { + return new double[]{1.1, 2.2, 3.3}; + } + + public static void passDoubleArray(double[] arg) {} + + public static boolean[] returnBooleanArray() { + return new boolean[]{true, false, true}; + } + + public static void passBooleanArray(boolean[] arg) {} + + public static java.util.Date returnDateObject() { + return new java.util.Date(); + } + + public static void passDateObject(java.util.Date date) {} + + public static void invokeCallback(BenchmarkCallback callback) { + callback.onCallback(); + } +} diff --git a/test-app/build-tools/android-metadata-generator/build.gradle b/test-app/build-tools/android-metadata-generator/build.gradle index 655af1f0..85ca0723 100644 --- a/test-app/build-tools/android-metadata-generator/build.gradle +++ b/test-app/build-tools/android-metadata-generator/build.gradle @@ -2,8 +2,8 @@ apply plugin: 'java' apply plugin: 'kotlin' java { - sourceCompatibility = '17' - targetCompatibility = '17' + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } // todo: check if still needed diff --git a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/Builder.java b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/Builder.java index 23e2a4db..6c46b9cb 100644 --- a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/Builder.java +++ b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/Builder.java @@ -456,6 +456,30 @@ private static TreeNode getOrCreateNode(TreeNode root, NativeClassDescriptor cla for (int i = outerClasses.size() - 1; i >= 0; i--) { outer = outerClasses.get(i); String outerClassname = ClassUtil.getSimpleName(outer); + + // A Kotlin companion object is stored as a package-level sibling + // node named "$" (see createCompanionNode), + // not as a direct child. When the companion itself declares nested + // classes, those nested classes carry the companion in their outer + // chain. Descend into the companion sibling here instead of calling + // createChild, which would create a shadow "" child that + // hides the real companion node and drops its members. + NativeClassDescriptor outerEnclosing = ClassUtil.getEnclosingClass(outer); + if (node.parentNode != null && isNestedClassKotlinCompanionObject(outerEnclosing, outer)) { + String companionNodeName = node.getName() + "$" + outerClassname; + TreeNode companionChild = node.parentNode.getChild(companionNodeName); + if (companionChild == null) { + companionChild = node.createCompanionNode(outerClassname); + companionChild.nodeType = outer.isInterface() ? TreeNode.Interface + : TreeNode.Class; + if (outer.isStatic()) { + companionChild.nodeType |= TreeNode.Static; + } + } + node = companionChild; + continue; + } + TreeNode child = node.getChild(outerClassname); if (child == null) { child = node.createChild(outerClassname); @@ -470,8 +494,15 @@ private static TreeNode getOrCreateNode(TreeNode root, NativeClassDescriptor cla } TreeNode child = node.getChild(name); + boolean clazzIsCompanion = isNestedClassKotlinCompanionObject(outer, clazz); + if (child == null && clazzIsCompanion && node.parentNode != null) { + // The companion may already have been created as a package-level + // sibling if one of its nested classes was processed first; reuse it + // instead of creating a duplicate companion node. + child = node.parentNode.getChild(node.getName() + "$" + name); + } if (child == null) { - if (isNestedClassKotlinCompanionObject(outer, clazz)) { + if (clazzIsCompanion) { child = node.createCompanionNode(name); } else { child = node.createChild(name); diff --git a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt index 43e6bf27..5343cf1f 100644 --- a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt +++ b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt @@ -62,9 +62,9 @@ class KotlinClassDescriptor(nativeClass: JavaClass, private val metadataAnnotati fields.add(possibleObjectInstanceField.get()) } - if (metaClass.enumEntries.isNotEmpty()) { - - val enumFields = getEnumEntriesAsFields(nativeClass, metaClass.enumEntries) + if (metaClass.kmEnumEntries.isNotEmpty()) { + val enums: Collection = metaClass.kmEnumEntries.map { it.name } + val enumFields = getEnumEntriesAsFields(nativeClass, enums) fields.addAll(enumFields) } diff --git a/test-app/build-tools/buildMetadata.log b/test-app/build-tools/buildMetadata.log index ad5e712d..04701873 100644 --- a/test-app/build-tools/buildMetadata.log +++ b/test-app/build-tools/buildMetadata.log @@ -1,6 +1,7 @@ -Added Properties 54 +WARNING: Skipping interface androidx.dynamicanimation.animation.DynamicAnimation$OnAnimationUpdateListener implemented in androidx.transition.Transition$SeekController as it cannot be resolved +Added Properties 59 Ignored Properties 2 duplicates. -Added Methods 81322 -Ignored Methods 3302 duplicates. -Added Fields 54617 -Ignored Fields 16693 duplicates. +Added Methods 77490 +Ignored Methods 3333 duplicates. +Added Fields 43788 +Ignored Fields 5605 duplicates. diff --git a/test-app/build-tools/jsparser/js_parser.js b/test-app/build-tools/jsparser/js_parser.js index 5d5839fe..abada574 100644 --- a/test-app/build-tools/jsparser/js_parser.js +++ b/test-app/build-tools/jsparser/js_parser.js @@ -184,7 +184,7 @@ function readInterfaceNames(data, err) { } /* - * Traverses a given input directory and attempts to visit every ".js" file. + * Traverses a given input directory and attempts to visit every ".js" and ".mjs" file. * It passes each found file down the line. */ function traverseAndAnalyseFilesDir(inputDir, err) { @@ -196,9 +196,12 @@ function traverseAndAnalyseFilesDir(inputDir, err) { } function traverseFiles(filesToTraverse) { + if (filesToTraverse.length === 0) { + throw "no file was found in " + inputDir + ". Something must be wrong with the webpack build"; + } for (let i = 0; i < filesToTraverse.length; i += 1) { const fp = filesToTraverse[i]; - logger.info("Visiting JavaScript file: " + fp); + logger.info("Visiting JavaScript/ES Module file: " + fp); readFile(fp) .then(astFromFileContent.bind(null, fp)) @@ -230,6 +233,7 @@ const readFile = function (filePath, err) { /* * Get's the AST (https://en.wikipedia.org/wiki/Abstract_syntax_tree) from the file content and passes it down the line. + * Supports both CommonJS (.js) and ES modules (.mjs) files. */ const astFromFileContent = function (path, data, err) { return new Promise(function (resolve, reject) { @@ -238,13 +242,28 @@ const astFromFileContent = function (path, data, err) { return reject(err); } - const ast = babelParser.parse(data.data, { + // Determine if this is an ES module based on file extension + const isESModule = path.endsWith('.mjs'); + + // Configure Babel parser based on file type + const parserOptions = { minify: false, plugins: [ ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }], "objectRestSpread", ], - }); + }; + + // For ES modules, set sourceType to 'module' + if (isESModule) { + parserOptions.sourceType = 'module'; + logger.info(`Parsing ES module: ${path}`); + } else { + // For regular JS files, keep existing behavior (default sourceType is 'script') + logger.info(`Parsing CommonJS file: ${path}`); + } + + const ast = babelParser.parse(data.data, parserOptions); data.ast = ast; return resolve(data); }); @@ -268,6 +287,10 @@ const visitAst = function (path, data, err) { traverse.default(data.ast, { enter: function (path) { + // Determine file extension length to properly strip it from the path + const fileExtension = data.filePath.endsWith('.mjs') ? '.mjs' : '.js'; + const extensionLength = fileExtension.length; + const decoratorConfig = { logger: logger, extendDecoratorName: extendDecoratorName, @@ -275,7 +298,7 @@ const visitAst = function (path, data, err) { filePath: data.filePath.substring( inputDir.length + 1, - data.filePath.length - 3 + data.filePath.length - extensionLength ) || "", fullPathName: data.filePath .substring(inputDir.length + 1) diff --git a/test-app/build-tools/jsparser/visitors/es5-visitors.js b/test-app/build-tools/jsparser/visitors/es5-visitors.js index 2648eac8..44604c55 100644 --- a/test-app/build-tools/jsparser/visitors/es5-visitors.js +++ b/test-app/build-tools/jsparser/visitors/es5-visitors.js @@ -657,6 +657,20 @@ var es5_visitors = (function () { * HELPER METHODS */ + // Returns the Identifier name of a property's key, or null when the property + // is a SpreadElement, has a computed key, or uses a non-Identifier key + // (e.g. StringLiteral, NumericLiteral). Such properties cannot be mapped to + // a Java method binding, so callers should skip them. + function _getIdentifierKeyName(property) { + if (!property || property.computed) { + return null; + } + if (!property.key || !types.isIdentifier(property.key)) { + return null; + } + return property.key.name; + } + function getTypeScriptExtendSuperCallLocation(extendPath, config) { var constructorFunctionName; var returnIdentifierName; @@ -862,7 +876,11 @@ var es5_visitors = (function () { var objectProperties = node.properties; for (var index in objectProperties) { - overriddenMethodNames.push(objectProperties[index].key.name); + var keyName = _getIdentifierKeyName(objectProperties[index]); + if (keyName === null) { + continue; + } + overriddenMethodNames.push(keyName); } } @@ -897,10 +915,19 @@ var es5_visitors = (function () { will get 'method1' and 'method3' */ for (var index in objectProperties) { + var keyName = _getIdentifierKeyName(objectProperties[index]); + // Skip spreads, computed keys, and non-Identifier keys — they cannot be + // statically mapped to a Java binding. Without this guard, valid (non-NS) + // `.extend({ ...other, foo })` calls in bundled vendor code (e.g. Zod + // schemas) would crash the parser. + if (keyName === null) { + continue; + } + // if the user has declared interfaces that he is implementing if ( !interfacesFound && - objectProperties[index].key.name.toLowerCase() === "interfaces" && + keyName.toLowerCase() === "interfaces" && types.isArrayExpression(objectProperties[index].value) ) { interfacesFound = true; @@ -913,7 +940,7 @@ var es5_visitors = (function () { implementedInterfaces.push(interfaceName); } } else { - overriddenMethodNames.push(objectProperties[index].key.name); + overriddenMethodNames.push(keyName); } } } diff --git a/test-app/build-tools/jsparser/webpack.config.js b/test-app/build-tools/jsparser/webpack.config.js index f335f249..e2667f02 100644 --- a/test-app/build-tools/jsparser/webpack.config.js +++ b/test-app/build-tools/jsparser/webpack.config.js @@ -12,4 +12,5 @@ module.exports = { path: path.join(__dirname, "build"), filename: "js_parser.js", }, + devtool: false }; diff --git a/test-app/build.gradle b/test-app/build.gradle index 99a1643b..8a698925 100644 --- a/test-app/build.gradle +++ b/test-app/build.gradle @@ -46,6 +46,7 @@ version of the {N} CLI install a previous version of the runtime package - 'tns project.ext.nativescriptDependencies = new JsonSlurper().parseText(dependenciesJson.text) project.ext.PLATFORMS_ANDROID = "platforms/android" project.ext.USER_PROJECT_ROOT = "$rootDir/../.." +// project.ext.nsBytecodeForce = true project.ext.getAppPath = { -> def relativePathToApp = "app" diff --git a/test-app/gradle.properties b/test-app/gradle.properties index 59473d4a..1f576bc0 100644 --- a/test-app/gradle.properties +++ b/test-app/gradle.properties @@ -1,4 +1,4 @@ -#Thu, 30 Oct 2025 18:27:02 +0500 +#Thu, 09 Jul 2026 00:31:20 +0500 # Project-wide Gradle settings. @@ -21,29 +21,29 @@ android.enableJetifier=true android.useAndroidX=true # Default versions used throughout the gradle configurations -NS_DEFAULT_BUILD_TOOLS_VERSION=34.0.0 -NS_DEFAULT_COMPILE_SDK_VERSION=34 +NS_DEFAULT_BUILD_TOOLS_VERSION=35.0.0 +NS_DEFAULT_COMPILE_SDK_VERSION=35 NS_DEFAULT_MIN_SDK_VERSION=21 NS_DEFAULT_ANDROID_BUILD_TOOLS_VERSION=8.12.1 -ns_default_androidx_appcompat_version=1.5.1 -ns_default_androidx_exifinterface_version=1.3.7 -ns_default_androidx_fragment_version=1.5.7 -ns_default_androidx_material_version=1.8.0 -ns_default_androidx_multidex_version=2.0.1 -ns_default_androidx_transition_version=1.4.1 -ns_default_androidx_viewpager_version=1.0.0 -ns_default_asm_util_version=9.7 -ns_default_asm_version=9.7 -ns_default_bcel_version=6.8.2 -ns_default_commons_io_version=2.6 -ns_default_google_java_format_version=1.6 -ns_default_gson_version=2.10.1 -ns_default_json_version=20180813 -ns_default_junit_version=4.13.2 -ns_default_kotlin_version=2.0.0 -ns_default_kotlinx_metadata_jvm_version=2.0.0 -ns_default_mockito_core_version=3.0.0 -ns_default_spotbugs_version=3.1.12 - -ns_engine=V8 +ns_default_androidx_appcompat_version = 1.7.0 +ns_default_androidx_exifinterface_version = 1.3.7 +ns_default_androidx_fragment_version = 1.8.5 +ns_default_androidx_material_version = 1.8.0 +ns_default_androidx_multidex_version = 2.0.1 +ns_default_androidx_transition_version = 1.5.1 +ns_default_androidx_viewpager_version = 1.0.0 +ns_default_asm_util_version = 9.7 +ns_default_asm_version = 9.7 +ns_default_bcel_version = 6.8.2 +ns_default_commons_io_version = 2.6 +ns_default_google_java_format_version = 1.6 +ns_default_gson_version = 2.10.1 +ns_default_json_version = 20180813 +ns_default_junit_version = 4.13.2 +ns_default_kotlin_version = 2.2.20 +ns_default_kotlinx_metadata_jvm_version = 2.2.20 +ns_default_mockito_core_version = 3.0.0 +ns_default_spotbugs_version = 3.1.12 + +ns_engine=JSC diff --git a/test-app/hello/src/main/libs/arm64-v8a/libJavaScriptCore.so b/test-app/hello/src/main/libs/arm64-v8a/libJavaScriptCore.so new file mode 100755 index 00000000..28493b4e Binary files /dev/null and b/test-app/hello/src/main/libs/arm64-v8a/libJavaScriptCore.so differ diff --git a/test-app/runtime-binding-generator/build.gradle b/test-app/runtime-binding-generator/build.gradle index d98d8376..d7f20b31 100644 --- a/test-app/runtime-binding-generator/build.gradle +++ b/test-app/runtime-binding-generator/build.gradle @@ -7,8 +7,8 @@ dependencies { } java { - sourceCompatibility = '17' - targetCompatibility = '17' + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } // Disable compilation tasks as these are compiled *with* the runtime and not separately diff --git a/test-app/runtime-binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java b/test-app/runtime-binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java index 546b4259..52506fbd 100644 --- a/test-app/runtime-binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java +++ b/test-app/runtime-binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java @@ -58,6 +58,10 @@ public String generateProxy(String proxyName, ClassDescriptor classToProxy, Hash private String saveProxy(String proxyName, byte[] proxyBytes) throws IOException { File file = new File(path + File.separator + proxyName + ".dex"); + File parentDir = file.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } file.createNewFile(); FileOutputStream stream = new FileOutputStream(file); stream.write(proxyBytes); diff --git a/test-app/runtime/CMakeLists.txt b/test-app/runtime/CMakeLists.txt index e9c4f1ca..0bb32d39 100644 --- a/test-app/runtime/CMakeLists.txt +++ b/test-app/runtime/CMakeLists.txt @@ -21,13 +21,14 @@ endif () set(COMMON_CMAKE_ARGUMENTS "-Wno-error -Wno-deprecated-declarations -Wno-unused-result -mstackrealign -fexceptions -fno-builtin-stpcpy") set(MI_OVERRIDE OFF) -if (V8_13) - set(COMMON_CMAKE_ARGUMENTS "${COMMON_CMAKE_ARGUMENTS} -std=c++20") -elseif () - set(COMMON_CMAKE_ARGUMENTS "${COMMON_CMAKE_ARGUMENTS} -std=c++17") -endif () +# All engines build with C++20: the vendored ada 3.3.0 (used by the URL module, +# compiled for every engine) requires C++20 (std::endian, concepts). Previously +# only V8_13 got -std=c++20 (and the empty `elseif ()` set nothing for the rest), +# so non-V8-13 engine builds failed on ada.h. +set(COMMON_CMAKE_ARGUMENTS "${COMMON_CMAKE_ARGUMENTS} -std=c++20") -if (SHERMES) +if (HERMES) + # Hermes/JSI headers require RTTI. set(COMMON_CMAKE_ARGUMENTS "${COMMON_CMAKE_ARGUMENTS} -frtti") else () set(COMMON_CMAKE_ARGUMENTS "${COMMON_CMAKE_ARGUMENTS} -fno-rtti") @@ -64,6 +65,7 @@ include_directories( src/main/cpp/runtime/constants src/main/cpp/runtime/conversion src/main/cpp/runtime/exceptions + src/main/cpp/runtime/finalizer src/main/cpp/runtime/global src/main/cpp/runtime/instrumentation src/main/cpp/runtime/inspector @@ -77,6 +79,8 @@ include_directories( src/main/cpp/runtime/sighandler src/main/cpp/runtime/timers src/main/cpp/runtime/util + # workers: native worker threading & messaging (WorkerWrapper, ConcurrentQueue, LooperTasks) + src/main/cpp/runtime/workers src/main/cpp/runtime/jsonhelper src/main/cpp/runtime/version src/main/cpp/runtime/weakref @@ -86,6 +90,7 @@ include_directories( ) # Search for all CPP files in runtime/ directory and add them to our sources +# (includes runtime/inspector/WorkerInspectorClient.cpp for the V8 worker inspector) file(GLOB_RECURSE RUNTIME_FILES "${PROJECT_SOURCE_DIR}/src/main/cpp/runtime/*.cpp" "${PROJECT_SOURCE_DIR}/src/main/cpp/runtime/**/*.cpp" @@ -95,21 +100,45 @@ file(GLOB_RECURSE MODULE_FILES "${PROJECT_SOURCE_DIR}/src/main/cpp/modules/*.cpp" "${PROJECT_SOURCE_DIR}/src/main/cpp/modules/**/*.cpp" ) +# modules/url: URL, URLSearchParams, URLPattern (backed by vendored ada) set(SOURCES ${RUNTIME_FILES} ${MODULE_FILES}) if (QUICKJS OR QUICKJS_NG) + # Build mimalloc (v3) for speed regardless of the APK variant. Android + # Gradle passes -DCMAKE_BUILD_TYPE=Debug for debug variants, and mimalloc v3 + # then *explicitly* defines MI_DEBUG=2 and auto-enables MI_GUARDED (guard + # pages) — assertion + invariant checking on the alloc/free hot path that can + # make it slower than the system allocator. MI_NO_DEBUG forces MI_DEBUG=0 + # (and takes priority over the Debug branch, which also suppresses the + # auto-guard). arm64 already gets -march=armv8.1-a via v3's default + # MI_OPT_ARCH, so no arch flag is needed here. + set(MI_NO_DEBUG ON CACHE BOOL "" FORCE) # define MI_DEBUG=0 in all builds + set(MI_GUARDED OFF CACHE BOOL "" FORCE) # no guard-page allocations + set(MI_NO_PADDING ON CACHE BOOL "" FORCE) # define MI_PADDING=0 in all builds + set(MI_SECURE OFF CACHE BOOL "" FORCE) + set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(MI_BUILD_SHARED OFF CACHE BOOL "" FORCE) # we only link mimalloc-static + set(MI_BUILD_OBJECT OFF CACHE BOOL "" FORCE) + set(MI_OVERRIDE OFF CACHE BOOL "" FORCE) # we call mi_* explicitly + add_subdirectory(${PROJECT_SOURCE_DIR}/src/main/cpp/napi/quickjs/mimalloc-dev mimalloc) + # Optimize the mimalloc sources even in a debug configuration, scoped to the + # mimalloc target only (not quickjs.c or the rest of the C runtime). -O2 is + # appended after the Debug -O0 so it wins; -DNDEBUG drops any remaining + # assert()s in mimalloc's own code. + target_compile_options(mimalloc-static PRIVATE -O2 -DNDEBUG) + if (QUICKJS_NG) set(QJS_SOURCE_DIR src/main/cpp/napi/quickjs/source_ng) + add_compile_definitions(__QUICKJS_NG__) else() set(QJS_SOURCE_DIR src/main/cpp/napi/quickjs/source) endif() set(SOURCES ${SOURCES} # quickjs - ${QJS_SOURCE_DIR}/cutils.c ${QJS_SOURCE_DIR}/libregexp.c ${QJS_SOURCE_DIR}/libunicode.c ${QJS_SOURCE_DIR}/quickjs.c @@ -121,6 +150,12 @@ if (QUICKJS OR QUICKJS_NG) ) + if (NOT QUICKJS_NG) + set(SOURCES ${SOURCES} + ${QJS_SOURCE_DIR}/cutils.c + ) + endif () + include_directories( src/main/cpp/napi/quickjs ${QJS_SOURCE_DIR} @@ -135,9 +170,8 @@ endif () if (PRIMJS) set(SOURCES ${SOURCES} src/main/cpp/napi/primjs/jsr.cpp - src/main/cpp/napi/primjs/code_cache.cc - src/main/cpp/napi/primjs/primjs-api.cc - src/main/cpp/napi/primjs/napi_env.cc + src/main/cpp/napi/primjs/js_native_api_adapter.cc + src/main/cpp/napi/primjs/js_native_api_extensions.cc ) include_directories( src/main/cpp/napi/primjs @@ -148,6 +182,8 @@ if (PRIMJS) endif () if (HERMES) + # Hermes is the static-hermes (hermesvm) engine. Headers are extracted from + # the hermes-engine AAR into napi/hermes/include. include_directories( src/main/cpp/napi/hermes src/main/cpp/napi/hermes/include @@ -158,17 +194,6 @@ if (HERMES) ) endif () -if (SHERMES) - include_directories( - src/main/cpp/napi/hermes - src/main/cpp/napi/hermes/include_shermes - src/main/cpp/napi/common - ) - set(SOURCES ${SOURCES} - src/main/cpp/napi/hermes/jsr.cpp - ) -endif () - if (JSC) include_directories( src/main/cpp/napi/jsc @@ -289,21 +314,31 @@ MESSAGE(STATUS "# CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS}) target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/common/${ANDROID_ABI}/libzip.a) #target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/common/${ANDROID_ABI}/libclang_rt.asan-aarch64-android.so) -if (SHERMES) - target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/shermes/${ANDROID_ABI}/libhermesvm.so) - target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/shermes/${ANDROID_ABI}/libjsi.so) - add_compile_definitions(NativeScript, PRIVATE __HERMES__) - add_compile_definitions(NativeScript, PRIVATE __SHERMES__) -endif () - if (HERMES) - target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/hermes/${ANDROID_ABI}/libhermes.so) - target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/hermes/${ANDROID_ABI}/libjsi.so) + # Select debug or release Hermes .so files based on build type. + # Optimized builds use the MinSizeRel release libraries; all other builds + # (including regular debug and debugOptimized-with-inspector) use the debug + # libraries, which include HERMES_ENABLE_DEBUGGER and full debug info. + if (OPTIMIZED_BUILD OR OPTIMIZED_WITH_INSPECTOR_BUILD) + set(HERMES_LIB_VARIANT release) + else () + set(HERMES_LIB_VARIANT debug) + endif () + + set(HERMES_LIB_DIR ${PROJECT_SOURCE_DIR}/src/main/libs/hermes/${HERMES_LIB_VARIANT}/${ANDROID_ABI}) + MESSAGE(STATUS "# Hermes lib variant: ${HERMES_LIB_VARIANT} (${HERMES_LIB_DIR})") + + target_link_libraries(NativeScript ${HERMES_LIB_DIR}/libhermesvm.so) + target_link_libraries(NativeScript ${HERMES_LIB_DIR}/libjsi.so) add_compile_definitions(NativeScript, PRIVATE __HERMES__) + + # napi_create_host_object / napi_get_host_object_data / napi_is_host_object + # are compiled into libhermesvm.so (via hermesNapi) when USE_HOST_OBJECT is + # set at Hermes build time. No separate .so to link here. endif () if (JSC) - target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/jsc/${ANDROID_ABI}/libjsc.so) + target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/jsc/${ANDROID_ABI}/libJavaScriptCore.so) add_compile_definitions(NativeScript, PRIVATE __JSC__) endif () @@ -327,8 +362,12 @@ if (V8_13) endif () if (PRIMJS) - # target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/primjs/${ANDROID_ABI}/libnapi.so) + # libnapi.so provides the napi host/setup entry points (napi_new_env, + # napi_attach_quickjs, napi_setup_loader, the quickjs<->napi bridges, ...) + # and installs the value-op vtable consumed by js_native_api_adapter.cc. + # It depends on libquick.so (the quickjs engine); link both. target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/primjs/${ANDROID_ABI}/libquick.so) + target_link_libraries(NativeScript ${PROJECT_SOURCE_DIR}/src/main/libs/primjs/${ANDROID_ABI}/libnapi.so) add_compile_definitions(NativeScript, PRIVATE __PRIMJS__) endif () @@ -339,6 +378,12 @@ if (QUICKJS OR QUICKJS_NG) endif () if (USE_MIMALLOC) add_compile_definitions(NativeScript, PRIVATE USE_MIMALLOC) + # Opt-in mimalloc runtime tuning (see qjs_create_runtime): trades higher + # memory residency for fewer syscalls / faster allocation. Enable from + # runtime/build.gradle with -DMIMALLOC_PERF=1. + if (MIMALLOC_PERF) + add_compile_definitions(NativeScript, PRIVATE MIMALLOC_PERF) + endif () endif () endif () @@ -350,6 +395,13 @@ if (IS_NAPI_MODULE) add_compile_definitions(NativeScript, PRIVATE IS_NAPI_MODULE) endif () +# Bytecode builds embed app-relative source names (no "file://" scheme). This +# lets the JS stack-trace parser (BuildStacktraceFrames) accept those frames and +# rebuild the full runtime URL from the app root. Wired from runtime/build.gradle. +if (NS_BYTECODE) + add_compile_definitions(NativeScript, PRIVATE NS_BYTECODE_ENABLED) +endif () + # if("${ANDROID_ABI}" MATCHES "armeabi-v7a$" OR "${ANDROID_ABI}" MATCHES "x86$") # # On API Level 19 and lower we need to link with android_support # # because it contains some implementation of functions such as "strtoll" and "strtoul" @@ -357,6 +409,11 @@ endif () # target_link_libraries(NativeScript ${ANDROID_NDK_ROOT}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libandroid_support.a) # endif() + +if("${ANDROID_ABI}" MATCHES "arm64-v8a$" OR "${ANDROID_ABI}" MATCHES "x86_64$") + target_link_options(NativeScript PRIVATE "-Wl,-z,max-page-size=16384") +endif() + # Command info: https://cmake.org/cmake/help/v3.4/command/find_library.html # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by @@ -375,7 +432,7 @@ if (QUICKJS) else () target_link_libraries(NativeScript ${system-log} ${system-z} ${system-android}) endif () -elseif (HERMES OR SHERMES) +elseif (HERMES) find_package(fbjni REQUIRED CONFIG) target_link_libraries(NativeScript ${system-log} ${system-z} fbjni::fbjni ${system-android}) elseif (JSC OR V8 OR PRIMJS) diff --git a/test-app/runtime/build.gradle b/test-app/runtime/build.gradle index 06ac738f..185cec16 100644 --- a/test-app/runtime/build.gradle +++ b/test-app/runtime/build.gradle @@ -1,14 +1,15 @@ apply plugin: 'com.android.library' -// can be: "V8-11", "V8-10","V8-13", "JSC", "HERMES", "QUICKJS", "QUICKJS_NG", "SHERMES", "PRIMJS" +// can be: "V8-11", "V8-10","V8-13", "JSC", "HERMES", "QUICKJS", "QUICKJS_NG", "PRIMJS" + +def jsEngine = "PRIMJS" -def jsEngine = "QUICKJS_NG" def hasEngine = project.hasProperty("engine") if (hasEngine) { jsEngine = engine } -def hasHostObjects = project.hasProperty("useHostObjects") +def hasHostObjects = true //project.hasProperty("useHostObjects") def isNapiModule = project.hasProperty("asNapiModule"); printf("Compiling NativeScript with %s.\n", jsEngine) @@ -33,6 +34,15 @@ if (useCCache) { println "Use CCache build triggered." } +// Bytecode stack-trace support. Defaults on (opt out with -PnsBytecodeDisabled). +// Defines NS_BYTECODE_ENABLED so BuildStacktraceFrames accepts bytecode frames +// (app-relative source names) and rebuilds their full runtime URL. Harmless when +// the app ships plain JS — those frames always carry a "file://" URL already. +def bytecodeStacktraces = !project.hasProperty("nsBytecodeDisabled") +if (bytecodeStacktraces) { + println "Bytecode stack-trace frame support enabled." +} + def defaultNdkVersion = "27.1.12297006" def hasNdkVersion = project.hasProperty("ndkVersion") @@ -133,16 +143,23 @@ android { arguments.add("-DIS_NAPI_MODULE=true") } + if (bytecodeStacktraces) { + arguments.add("-DNS_BYTECODE=1") + } + if (jsEngine == "QUICKJS" || jsEngine == "QUICKJS_NG") { arguments.add("-DQUICKJS=1") arguments.add("-DUSE_MIMALLOC=1") + // Trade memory for allocator speed (disables page purging, + // eager-commits arenas, enables THP). See qjs_create_runtime. + arguments.add("-DMIMALLOC_PERF=1") if (jsEngine == "QUICKJS_NG") { arguments.add("-DQUICKJS_NG=1") } } else if (jsEngine == "HERMES") { + // SHERMES is kept as an alias for the static-hermes (hermesvm) + // engine, which is now the canonical HERMES engine. arguments.add("-DHERMES=1") - } else if (jsEngine == "SHERMES") { - arguments.add("-DSHERMES=1") } else if (jsEngine == "PRIMJS") { arguments.add("-DPRIMJS=1") }else if (jsEngine == "JSC") { @@ -176,11 +193,24 @@ android { ndk { minSdkVersion NS_DEFAULT_MIN_SDK_VERSION as int if (onlyX86) { - abiFilters 'x86' + // The updated JSC only ships 64-bit libraries, so fall back to + // x86_64 when a single-ABI (emulator) build is requested. + abiFilters jsEngine == "JSC" ? 'x86_64' : 'x86' + } else if (jsEngine == "JSC") { + // The newer JSC engine only provides arm64-v8a and x86_64 + // prebuilts, so restrict the runtime to those two ABIs. + abiFilters 'x86_64', 'arm64-v8a' } else { abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' } } + + consumerProguardFiles 'consumer-rules.pro' + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } buildTypes { @@ -207,7 +237,7 @@ android { exclude "**/libjsi.so" exclude "**/libfbjni.so" } - } else if (jsEngine == "HERMES" || jsEngine == "SHERMES") { + } else if (jsEngine == "HERMES") { packagingOptions { exclude "**/libjsc.so" exclude '**/libfbjni.so' @@ -243,7 +273,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation "junit:junit:${ns_default_junit_version}" testImplementation "org.mockito:mockito-core:${ns_default_mockito_core_version}" - if (jsEngine == "HERMES" || jsEngine == "SHERMES") { + if (jsEngine == "HERMES") { implementation 'com.facebook.fbjni:fbjni:0.7.0' } } diff --git a/test-app/runtime/consumer-rules.pro b/test-app/runtime/consumer-rules.pro new file mode 100644 index 00000000..1bea022f --- /dev/null +++ b/test-app/runtime/consumer-rules.pro @@ -0,0 +1,11 @@ +# Keep all runtime classes +-keep class com.tns.* { *; } + +# Keep SBG-generated classes +-keep class com.tns.gen.** { *; } + +# Keep internal support/runtime classes +-keep class com.tns.internal.** { *; } + +# Preserve annotation metadata so reflection sees them +-keepattributes RuntimeVisibleAnnotations diff --git a/test-app/runtime/src/main/cpp/modules/NSRuntimeModules.h b/test-app/runtime/src/main/cpp/modules/NSRuntimeModules.h index c57b5453..731908c1 100644 --- a/test-app/runtime/src/main/cpp/modules/NSRuntimeModules.h +++ b/test-app/runtime/src/main/cpp/modules/NSRuntimeModules.h @@ -8,6 +8,7 @@ #include "js_native_api.h" #include "URL.h" #include "URLSearchParams.h" +#include "URLPattern.h" namespace tns { class NSRuntimeModules { @@ -15,6 +16,7 @@ namespace tns { static void Init(napi_env env) { URL::Init(env); URLSearchParams::Init(env); + URLPattern::Init(env); } }; } diff --git a/test-app/runtime/src/main/cpp/modules/url/URL.cpp b/test-app/runtime/src/main/cpp/modules/url/URL.cpp index ed109893..2403c5c8 100644 --- a/test-app/runtime/src/main/cpp/modules/url/URL.cpp +++ b/test-app/runtime/src/main/cpp/modules/url/URL.cpp @@ -9,7 +9,7 @@ namespace { URL *GetInstance(napi_env env, napi_callback_info info) { NAPI_PREAMBLE napi_value jsThis; - void *data; + void *data = nullptr; NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, &data)) { return nullptr; } @@ -196,7 +196,13 @@ napi_value URL::New(napi_env env, napi_callback_info info) { url_aggregator url; std::string_view url_string_view(url_buffer.data(), url_buffer.size()); - if (argc > 1) { + // Only treat the 2nd argument as a base URL when it is provided and is + // neither undefined nor null; otherwise fall through to the no-base parse. + bool hasBase = argc > 1 && + !napi_util::is_of_type(env, argv[1], napi_undefined) && + !napi_util::is_of_type(env, argv[1], napi_null); + + if (hasBase) { // Handle base URL size_t base_str_size; NAPI_GUARD(napi_get_value_string_utf8(env, argv[1], nullptr, 0, &base_str_size)) { diff --git a/test-app/runtime/src/main/cpp/modules/url/URLPattern.cpp b/test-app/runtime/src/main/cpp/modules/url/URLPattern.cpp new file mode 100644 index 00000000..aaa83446 --- /dev/null +++ b/test-app/runtime/src/main/cpp/modules/url/URLPattern.cpp @@ -0,0 +1,529 @@ +// +// napi port of URLPatternImpl (upstream nativescript/android-runtime #1830). +// + +#include "URLPattern.h" +#include + +using namespace std; +using namespace tns; +using namespace ada; + +thread_local napi_env URLPattern::s_current_env = nullptr; + +namespace { + napi_value js_str(napi_env env, std::string_view s) { + napi_value v; + napi_create_string_utf8(env, s.data(), s.size(), &v); + return v; + } + + std::string to_std_string(napi_env env, napi_value v) { + size_t len = 0; + napi_get_value_string_utf8(env, v, nullptr, 0, &len); + std::string s(len, '\0'); + napi_get_value_string_utf8(env, v, s.data(), len + 1, &len); + return s; + } + + std::optional get_str_prop(napi_env env, napi_value obj, const char *key) { + napi_value v; + if (napi_get_named_property(env, obj, key, &v) != napi_ok) { + return std::nullopt; + } + if (napi_util::is_of_type(env, v, napi_string)) { + return to_std_string(env, v); + } + return std::nullopt; + } + + void clear_pending(napi_env env) { + bool pending = false; + napi_is_exception_pending(env, &pending); + if (pending) { + napi_value e; + napi_get_and_clear_last_exception(env, &e); + } + } + + URLPattern *GetInstance(napi_env env, napi_callback_info info) { + size_t argc = 0; + napi_value jsThis; + void *data = nullptr; + if (napi_get_cb_info(env, info, &argc, nullptr, &jsThis, &data) != napi_ok) { + return nullptr; + } + URLPattern *instance = nullptr; + if (napi_unwrap(env, jsThis, reinterpret_cast(&instance)) != napi_ok) { + return nullptr; + } + return instance; + } + + napi_value GetPatternProperty(napi_env env, napi_callback_info info, + std::string_view (url_pattern::*getter)() const) { + URLPattern *instance = GetInstance(env, info); + if (instance == nullptr) { + return js_str(env, ""); + } + auto value = (instance->GetPattern()->*getter)(); + return js_str(env, value); + } +} + +// --------------------------------------------------------------------------- +// napi RegExp provider +// --------------------------------------------------------------------------- + +std::optional +napi_regex_provider::create_instance(std::string_view pattern, bool ignore_case) { + napi_env env = URLPattern::s_current_env; + if (env == nullptr) { + return std::nullopt; + } + + napi_value global; + napi_get_global(env, &global); + + napi_value regexpCtor; + if (napi_get_named_property(env, global, "RegExp", ®expCtor) != napi_ok) { + return std::nullopt; + } + + napi_value patternStr = js_str(env, pattern); + + // v8 impl always sets the unicode flag and optionally ignore-case. + std::string flags = "u"; + if (ignore_case) { + flags += "i"; + } + napi_value flagsStr = js_str(env, flags); + + napi_value argv[2] = {patternStr, flagsStr}; + napi_value regex; + if (napi_new_instance(env, regexpCtor, 2, argv, ®ex) != napi_ok) { + clear_pending(env); + return std::nullopt; + } + + napi_ref ref = napi_util::make_ref(env, regex, 1); + return NapiRegex(env, ref); +} + +std::optional>> +napi_regex_provider::regex_search(std::string_view input, const regex_type &pattern) { + napi_env env = pattern.env; + if (env == nullptr || pattern.ref == nullptr) { + return std::nullopt; + } + + napi_value regex = napi_util::get_ref_value(env, pattern.ref); + if (regex == nullptr) { + return std::nullopt; + } + + napi_value execFn; + if (napi_get_named_property(env, regex, "exec", &execFn) != napi_ok) { + return std::nullopt; + } + + napi_value inputStr = js_str(env, input); + napi_value argv[1] = {inputStr}; + napi_value result; + if (napi_call_function(env, regex, execFn, 1, argv, &result) != napi_ok) { + clear_pending(env); + return std::nullopt; + } + + if (napi_util::is_null_or_undefined(env, result)) { + return std::nullopt; + } + + std::vector> ret; + bool isArray = false; + napi_is_array(env, result, &isArray); + if (isArray) { + uint32_t len = 0; + napi_get_array_length(env, result, &len); + ret.reserve(len); + for (uint32_t i = 0; i < len; i++) { + napi_value item; + if (napi_get_element(env, result, i, &item) != napi_ok) { + return std::nullopt; + } + if (napi_util::is_undefined(env, item)) { + ret.emplace_back(std::nullopt); + } else if (napi_util::is_of_type(env, item, napi_string)) { + ret.emplace_back(to_std_string(env, item)); + } + } + } + + return ret; +} + +bool napi_regex_provider::regex_match(std::string_view input, const regex_type &pattern) { + napi_env env = pattern.env; + if (env == nullptr || pattern.ref == nullptr) { + return false; + } + + napi_value regex = napi_util::get_ref_value(env, pattern.ref); + if (regex == nullptr) { + return false; + } + + napi_value execFn; + if (napi_get_named_property(env, regex, "exec", &execFn) != napi_ok) { + return false; + } + + napi_value inputStr = js_str(env, input); + napi_value argv[1] = {inputStr}; + napi_value result; + if (napi_call_function(env, regex, execFn, 1, argv, &result) != napi_ok) { + clear_pending(env); + return false; + } + + return !napi_util::is_null_or_undefined(env, result); +} + +// --------------------------------------------------------------------------- +// URLPattern +// --------------------------------------------------------------------------- + +URLPattern::URLPattern(url_pattern pattern) + : pattern_(std::move(pattern)) {} + +url_pattern *URLPattern::GetPattern() { + return &this->pattern_; +} + +void URLPattern::Destructor(napi_env env, void *data, void *hint) { +#ifdef __HERMES__ + URLPattern *pattern = static_cast(hint); +#else + URLPattern *pattern = static_cast(data); +#endif + delete pattern; +} + +static std::optional ParseInput(napi_env env, napi_value input) { + if (!napi_util::is_object(env, input)) { + return {}; + } + + auto init = ada::url_pattern_init{}; + if (auto v = get_str_prop(env, input, "protocol")) init.protocol = *v; + if (auto v = get_str_prop(env, input, "username")) init.username = *v; + if (auto v = get_str_prop(env, input, "password")) init.password = *v; + if (auto v = get_str_prop(env, input, "hostname")) init.hostname = *v; + if (auto v = get_str_prop(env, input, "port")) init.port = *v; + if (auto v = get_str_prop(env, input, "pathname")) init.pathname = *v; + if (auto v = get_str_prop(env, input, "search")) init.search = *v; + if (auto v = get_str_prop(env, input, "hash")) init.hash = *v; + if (auto v = get_str_prop(env, input, "baseURL")) init.base_url = *v; + return init; +} + +static void SetComponent(napi_env env, napi_value object, const char *componentKey, + const ada::url_pattern_component_result &component) { + napi_value ret; + napi_create_object(env, &ret); + napi_set_named_property(env, ret, "input", js_str(env, component.input)); + + napi_value groupValue; + napi_create_object(env, &groupValue); + for (const auto &[key, value]: component.groups) { + if (value) { + napi_set_named_property(env, groupValue, key.c_str(), js_str(env, value.value())); + } else { + napi_set_named_property(env, groupValue, key.c_str(), napi_util::undefined(env)); + } + } + napi_set_named_property(env, ret, "groups", groupValue); + + napi_set_named_property(env, object, componentKey, ret); +} + +static void BuildJS(napi_env env, napi_value object, const ada::url_pattern_result &result) { + auto len = result.inputs.size(); + napi_value inputs; + napi_create_array_with_length(env, len, &inputs); + for (uint32_t i = 0; i < len; i++) { + const auto &item = result.inputs[i]; + if (std::holds_alternative(item)) { + auto view = std::get(item); + napi_set_element(env, inputs, i, js_str(env, view)); + } + } + napi_set_named_property(env, object, "inputs", inputs); + + SetComponent(env, object, "protocol", result.protocol); + SetComponent(env, object, "hash", result.hash); + SetComponent(env, object, "hostname", result.hostname); + SetComponent(env, object, "username", result.username); + SetComponent(env, object, "password", result.password); + SetComponent(env, object, "pathname", result.pathname); + SetComponent(env, object, "port", result.port); + SetComponent(env, object, "search", result.search); +} + +napi_value URLPattern::New(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(3) + s_current_env = env; + + // No-args: construct an empty pattern. + if (argc == 0 || napi_util::is_undefined(env, argv[0])) { + auto init = ada::url_pattern_init{}; + auto pattern = ada::parse_url_pattern(std::move(init)); + if (!pattern) { + napi_throw_type_error(env, nullptr, "Failed to construct URLPattern"); + return nullptr; + } + auto *impl = new URLPattern(std::move(*pattern)); + napi_wrap(env, jsThis, impl, URLPattern::Destructor, impl, nullptr); + return jsThis; + } + + std::optional init{}; + std::optional input{}; + std::optional base_url{}; + std::optional options{}; + + if (napi_util::is_of_type(env, argv[0], napi_string)) { + input = to_std_string(env, argv[0]); + } else if (napi_util::is_object(env, argv[0])) { + auto parsed = ParseInput(env, argv[0]); + if (parsed) { + init = std::move(*parsed); + } + } else { + napi_throw_type_error(env, nullptr, "Input must be an object or a string"); + return nullptr; + } + + // 2nd arg may be a base URL string or an options object. + napi_value baseOrOptions = argv[1]; + if (napi_util::is_of_type(env, baseOrOptions, napi_string)) { + base_url = to_std_string(env, baseOrOptions); + } else if (napi_util::is_object(env, baseOrOptions)) { + napi_value ignoreCase; + if (napi_get_named_property(env, baseOrOptions, "ignoreCase", &ignoreCase) == napi_ok && + napi_util::is_of_type(env, ignoreCase, napi_boolean)) { + options = ada::url_pattern_options{.ignore_case = napi_util::get_bool(env, ignoreCase)}; + } + } + + // 3rd arg is the options object when a base URL was given as 2nd arg. + napi_value opts = argv[2]; + if (napi_util::is_object(env, opts)) { + napi_value ignoreCase; + if (napi_get_named_property(env, opts, "ignoreCase", &ignoreCase) == napi_ok && + napi_util::is_of_type(env, ignoreCase, napi_boolean)) { + options = ada::url_pattern_options{.ignore_case = napi_util::get_bool(env, ignoreCase)}; + } + } + + std::string_view base_url_view{}; + if (base_url) { + base_url_view = {base_url->data(), base_url->size()}; + } + + ada::url_pattern_input arg0; + if (init.has_value()) { + arg0 = *init; + } else { + arg0 = std::string_view(*input); + } + + auto pattern = ada::parse_url_pattern( + std::move(arg0), + base_url.has_value() ? &base_url_view : nullptr, + options.has_value() ? &options.value() : nullptr); + + if (!pattern) { + napi_throw_type_error(env, nullptr, "Failed to construct URLPattern"); + return nullptr; + } + + auto *impl = new URLPattern(std::move(*pattern)); + napi_wrap(env, jsThis, impl, URLPattern::Destructor, impl, nullptr); + return jsThis; +} + +napi_value URLPattern::GetHash(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_hash); +} + +napi_value URLPattern::GetHostName(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_hostname); +} + +napi_value URLPattern::GetPassword(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_password); +} + +napi_value URLPattern::GetPathName(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_pathname); +} + +napi_value URLPattern::GetPort(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_port); +} + +napi_value URLPattern::GetProtocol(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_protocol); +} + +napi_value URLPattern::GetSearch(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_search); +} + +napi_value URLPattern::GetUserName(napi_env env, napi_callback_info info) { + return GetPatternProperty(env, info, &url_pattern::get_username); +} + +napi_value URLPattern::GetHasRegexpGroups(napi_env env, napi_callback_info info) { + URLPattern *instance = GetInstance(env, info); + napi_value result; + bool value = instance != nullptr && instance->GetPattern()->has_regexp_groups(); + napi_get_boolean(env, value, &result); + return result; +} + +napi_value URLPattern::Test(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(2) + s_current_env = env; + + URLPattern *instance = GetInstance(env, info); + if (instance == nullptr) { + napi_value result; + napi_get_boolean(env, false, &result); + return result; + } + + ada::url_pattern_input input; + std::string input_base; + + if (argc == 0 || napi_util::is_undefined(env, argv[0])) { + input = ada::url_pattern_init{}; + } else if (napi_util::is_of_type(env, argv[0], napi_string)) { + input_base = to_std_string(env, argv[0]); + input = std::string_view(input_base); + } else if (napi_util::is_object(env, argv[0])) { + auto parsed = ParseInput(env, argv[0]); + if (parsed) { + input = std::move(*parsed); + } + } else { + napi_throw_type_error(env, nullptr, "URLPattern input needs to be a string or an object"); + return nullptr; + } + + std::optional baseURL{}; + if (argc > 1 && !napi_util::is_undefined(env, argv[1])) { + if (!napi_util::is_of_type(env, argv[1], napi_string)) { + napi_throw_type_error(env, nullptr, "baseURL must be a string"); + return nullptr; + } + baseURL = to_std_string(env, argv[1]); + } + + std::optional baseURL_opt = + baseURL ? std::optional(*baseURL) : std::nullopt; + + napi_value result; + if (auto res = instance->GetPattern()->test(input, baseURL_opt ? &*baseURL_opt : nullptr)) { + napi_get_boolean(env, res.value(), &result); + } else { + napi_get_null(env, &result); + } + return result; +} + +napi_value URLPattern::Exec(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(2) + s_current_env = env; + + URLPattern *instance = GetInstance(env, info); + if (instance == nullptr) { + return napi_util::undefined(env); + } + + ada::url_pattern_input input; + std::string input_base; + + if (argc == 0 || napi_util::is_undefined(env, argv[0])) { + input = ada::url_pattern_init{}; + } else if (napi_util::is_of_type(env, argv[0], napi_string)) { + input_base = to_std_string(env, argv[0]); + input = std::string_view(input_base); + } else if (napi_util::is_object(env, argv[0])) { + auto parsed = ParseInput(env, argv[0]); + if (parsed) { + input = std::move(*parsed); + } + } else { + napi_throw_type_error(env, nullptr, "URLPattern input needs to be a string or an object"); + return nullptr; + } + + std::optional baseURL{}; + if (argc > 1 && !napi_util::is_undefined(env, argv[1])) { + if (!napi_util::is_of_type(env, argv[1], napi_string)) { + napi_throw_type_error(env, nullptr, "baseURL must be a string"); + return nullptr; + } + baseURL = to_std_string(env, argv[1]); + } + + std::optional baseURL_opt = + baseURL ? std::optional(*baseURL) : std::nullopt; + + napi_value result; + napi_get_null(env, &result); + + if (auto res = instance->GetPattern()->exec(input, baseURL_opt ? &*baseURL_opt : nullptr)) { + auto value = res.value(); + if (value.has_value()) { + napi_create_object(env, &result); + BuildJS(env, result, value.value()); + } + } + return result; +} + +void URLPattern::Init(napi_env env) { + NAPI_PREAMBLE + napi_value ctor; + static const int instance_prop_count = 11; + napi_property_descriptor properties[instance_prop_count] = { + {"test", nullptr, Test, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"exec", nullptr, Exec, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"hasRegExpGroups", nullptr, nullptr, GetHasRegexpGroups, nullptr, nullptr, napi_default, nullptr}, + {"hash", nullptr, nullptr, GetHash, nullptr, nullptr, napi_default, nullptr}, + {"hostname", nullptr, nullptr, GetHostName, nullptr, nullptr, napi_default, nullptr}, + {"password", nullptr, nullptr, GetPassword, nullptr, nullptr, napi_default, nullptr}, + {"pathname", nullptr, nullptr, GetPathName, nullptr, nullptr, napi_default, nullptr}, + {"port", nullptr, nullptr, GetPort, nullptr, nullptr, napi_default, nullptr}, + {"protocol", nullptr, nullptr, GetProtocol, nullptr, nullptr, napi_default, nullptr}, + {"search", nullptr, nullptr, GetSearch, nullptr, nullptr, napi_default, nullptr}, + {"username", nullptr, nullptr, GetUserName, nullptr, nullptr, napi_default, nullptr}}; + + NAPI_GUARD(napi_define_class(env, "URLPattern", NAPI_AUTO_LENGTH, New, + nullptr, instance_prop_count, + properties, &ctor)) { + return; + } + + napi_value global; + NAPI_GUARD(napi_get_global(env, &global)) { + return; + } + + NAPI_GUARD(napi_set_named_property(env, global, "URLPattern", ctor)) { + return; + } +} diff --git a/test-app/runtime/src/main/cpp/modules/url/URLPattern.h b/test-app/runtime/src/main/cpp/modules/url/URLPattern.h new file mode 100644 index 00000000..a72e042e --- /dev/null +++ b/test-app/runtime/src/main/cpp/modules/url/URLPattern.h @@ -0,0 +1,113 @@ +// +// napi port of URLPatternImpl (upstream nativescript/android-runtime #1830). +// Backed by ada's url_pattern with a Node-API RegExp provider so it works +// across every engine the runtime supports (V8, QuickJS, Hermes, JSC, PrimJS). +// + +#ifndef TEST_APP_URLPATTERN_H +#define TEST_APP_URLPATTERN_H + +#include "native_api_util.h" +#include "FinalizerQueue.h" +#include "ada/ada.h" +#include +#include +#include + +using namespace ada; + +namespace tns { + + // Move-only RAII wrapper around a napi_ref to a JS RegExp. Mirrors the + // move-only semantics of v8::Global used by the original impl, + // and stores its own env so it can release the reference on destruction. + class NapiRegex { + public: + napi_env env = nullptr; + napi_ref ref = nullptr; + + NapiRegex() = default; + NapiRegex(napi_env e, napi_ref r) : env(e), ref(r) {} + + NapiRegex(const NapiRegex &) = delete; + NapiRegex &operator=(const NapiRegex &) = delete; + + NapiRegex(NapiRegex &&other) noexcept: env(other.env), ref(other.ref) { + other.ref = nullptr; + other.env = nullptr; + } + + NapiRegex &operator=(NapiRegex &&other) noexcept { + if (this != &other) { + if (ref != nullptr && env != nullptr) { + napi_delete_reference(env, ref); + } + env = other.env; + ref = other.ref; + other.ref = nullptr; + other.env = nullptr; + } + return *this; + } + + ~NapiRegex() { + if (ref != nullptr && env != nullptr) { + // This destructor can run from URLPattern's GC finalizer + // (URLPattern::Destructor, via napi_wrap), where deleting a napi_ref + // is unsafe on every engine. Defer it to the runtime's post-GC + // finalizer drain (which falls back to inline during teardown). + tns::PostFinalizer(env, [](napi_env env, void *d, void *) { + napi_delete_reference(env, (napi_ref) d); + }, ref, nullptr); + } + } + }; + + class napi_regex_provider { + public: + napi_regex_provider() = default; + + using regex_type = NapiRegex; + + static std::optional create_instance(std::string_view pattern, + bool ignore_case); + + static std::optional>> regex_search( + std::string_view input, const regex_type &pattern); + + static bool regex_match(std::string_view input, const regex_type &pattern); + }; + + class URLPattern { + public: + static void Init(napi_env env); + static void Destructor(napi_env env, void *nativeObject, void *finalize_hint); + + explicit URLPattern(url_pattern pattern); + url_pattern *GetPattern(); + + // Set at the entry of every callback that drives ada's regex provider + // (New/Test/Exec), so create_instance can reach the current env. + static thread_local napi_env s_current_env; + + private: + static napi_value New(napi_env env, napi_callback_info info); + + static napi_value GetHash(napi_env env, napi_callback_info info); + static napi_value GetHostName(napi_env env, napi_callback_info info); + static napi_value GetPassword(napi_env env, napi_callback_info info); + static napi_value GetPathName(napi_env env, napi_callback_info info); + static napi_value GetPort(napi_env env, napi_callback_info info); + static napi_value GetProtocol(napi_env env, napi_callback_info info); + static napi_value GetSearch(napi_env env, napi_callback_info info); + static napi_value GetUserName(napi_env env, napi_callback_info info); + static napi_value GetHasRegexpGroups(napi_env env, napi_callback_info info); + + static napi_value Test(napi_env env, napi_callback_info info); + static napi_value Exec(napi_env env, napi_callback_info info); + + url_pattern pattern_; + }; +} + +#endif //TEST_APP_URLPATTERN_H diff --git a/test-app/runtime/src/main/cpp/modules/url/URLSearchParams.cpp b/test-app/runtime/src/main/cpp/modules/url/URLSearchParams.cpp index 8cf11da4..076a2e76 100644 --- a/test-app/runtime/src/main/cpp/modules/url/URLSearchParams.cpp +++ b/test-app/runtime/src/main/cpp/modules/url/URLSearchParams.cpp @@ -1,24 +1,338 @@ #include "URLSearchParams.h" #include +#include using namespace ada; using namespace tns; namespace { + enum IterKind { ITER_KEYS = 0, ITER_VALUES = 1, ITER_ENTRIES = 2 }; + + // Per-iterator state: keeps the source URLSearchParams alive via a strong + // ref, holds a weak self-ref to the iterator object for the `next` receiver + // brand check, and tracks the current index. Freed by the iterator's finalizer. + struct IterState { + uint32_t idx; + int kind; + }; + + // Internal, non-enumerable keys the iterator carries: + // - ITER_SOURCE_KEY: a strong reference to the source URLSearchParams (kept + // alive for `next`); released by ordinary property teardown. + // - ITER_BRAND_KEY: a napi_external wrapping this iterator's IterState*, used + // by `next` for the receiver brand check. + // Holding these as properties (not napi_refs) means the iterator's finalizer + // never has to delete a reference — illegal during the GC sweep on every + // engine — so it can stay a plain C++ delete (see MakeIterator). + static constexpr const char *ITER_SOURCE_KEY = "__nsSource"; + static constexpr const char *ITER_BRAND_KEY = "__nsBrand"; + + napi_value js_str(napi_env env, std::string_view s) { + napi_value v; + napi_create_string_utf8(env, s.data(), s.length(), &v); + return v; + } + + void ThrowTypeError(napi_env env, const char *msg) { + napi_throw_type_error(env, nullptr, msg); + } + + // WebIDL USVString coercion: ToString (invokes user toString, throws for Symbol). + // Returns false leaving a pending exception on failure. + bool ValueToString(napi_env env, napi_value v, std::string &out) { + napi_value str; + if (napi_coerce_to_string(env, v, &str) != napi_ok) { + return false; + } + size_t len = 0; + if (napi_get_value_string_utf8(env, str, nullptr, 0, &len) != napi_ok) { + return false; + } + std::vector buf(len + 1); + napi_get_value_string_utf8(env, str, buf.data(), len + 1, nullptr); + out.assign(buf.data(), len); + return true; + } + + // Brand-checked instance retrieval: throws "Illegal invocation" (TypeError) when + // the receiver is not a wrapped URLSearchParams, matching the spec. URLSearchParams *GetInstance(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE napi_value jsThis; - void *data; - NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, &data)) { + if (napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr) != napi_ok) { + return nullptr; + } + URLSearchParams *instance = nullptr; + if (napi_unwrap(env, jsThis, reinterpret_cast(&instance)) != napi_ok || + instance == nullptr) { + ThrowTypeError(env, "Illegal invocation"); return nullptr; } + return instance; + } + + napi_value WellKnownSymbol(napi_env env, const char *name) { + napi_value global, symbolCtor, sym; + napi_get_global(env, &global); + napi_get_named_property(env, global, "Symbol", &symbolCtor); + napi_get_named_property(env, symbolCtor, name, &sym); + return sym; + } + + napi_value SymbolIterator(napi_env env) { + return WellKnownSymbol(env, "iterator"); + } - URLSearchParams *instance; - NAPI_GUARD(napi_unwrap(env, jsThis, reinterpret_cast(&instance))) { + // ES IteratorClose: best-effort call to the iterator's return() on abrupt + // completion (so a generator's `finally` runs), preserving any pending exception. + void IteratorClose(napi_env env, napi_value iterObj) { + napi_value savedEx = nullptr; + bool pending = false; + napi_is_exception_pending(env, &pending); + if (pending) { + napi_get_and_clear_last_exception(env, &savedEx); + } + napi_value returnFn; + if (napi_get_named_property(env, iterObj, "return", &returnFn) == napi_ok && + napi_util::is_of_type(env, returnFn, napi_function)) { + napi_value res; + napi_call_function(env, iterObj, returnFn, 0, nullptr, &res); + bool p2 = false; + napi_is_exception_pending(env, &p2); + if (p2) { + napi_value e; + napi_get_and_clear_last_exception(env, &e); + } + } + if (savedEx != nullptr) { + napi_throw(env, savedEx); + } + } + + // The `next` function of a live URLSearchParams iterator. + napi_value IteratorNext(napi_env env, napi_callback_info info) { + void *data = nullptr; + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, &data); + auto *st = static_cast(data); + + // Brand check: `next` must be invoked on the very iterator it belongs to. + // The receiver carries a napi_external wrapping its IterState*; a matching + // pointer proves identity. A foreign receiver (e.g. next.call({})) has no + // such brand and throws, per spec. + if (st == nullptr) { + ThrowTypeError(env, "Illegal invocation"); + return nullptr; + } + napi_value brandVal = nullptr; + void *brand = nullptr; + if (napi_get_named_property(env, jsThis, ITER_BRAND_KEY, &brandVal) != napi_ok || + !napi_util::is_of_type(env, brandVal, napi_external) || + napi_get_value_external(env, brandVal, &brand) != napi_ok || brand != st) { + ThrowTypeError(env, "Illegal invocation"); return nullptr; } - return instance; + napi_value result; + napi_create_object(env, &result); + + napi_value srcObj; + URLSearchParams *self = nullptr; + if (napi_get_named_property(env, jsThis, ITER_SOURCE_KEY, &srcObj) != napi_ok || + napi_unwrap(env, srcObj, reinterpret_cast(&self)) != napi_ok || + self == nullptr) { + napi_set_named_property(env, result, "value", napi_util::undefined(env)); + napi_set_named_property(env, result, "done", napi_util::get_true(env)); + return result; + } + + auto *p = self->GetURLSearchParams(); + if (st->idx >= p->size()) { + napi_set_named_property(env, result, "value", napi_util::undefined(env)); + napi_set_named_property(env, result, "done", napi_util::get_true(env)); + return result; + } + + auto pair = (*p)[st->idx++]; // live, duplicate-key correct + napi_value value; + if (st->kind == ITER_KEYS) { + value = js_str(env, pair.first); + } else if (st->kind == ITER_VALUES) { + value = js_str(env, pair.second); + } else { + napi_create_array_with_length(env, 2, &value); + napi_set_element(env, value, 0, js_str(env, pair.first)); + napi_set_element(env, value, 1, js_str(env, pair.second)); + } + napi_set_named_property(env, result, "value", value); + napi_set_named_property(env, result, "done", napi_util::get_false(env)); + return result; + } + + // iterator[Symbol.iterator]() -> this (iterators are iterable). + napi_value IteratorSelf(napi_env env, napi_callback_info info) { + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + return jsThis; + } + + napi_value MakeIterator(napi_env env, napi_value jsThis, int kind) { + auto *st = new IterState{0, kind}; + + napi_value iterator; + napi_create_object(env, &iterator); + + // Attach two non-enumerable internal properties (napi_default => not + // enumerable, invisible to JS): + // - the strong source URLSearchParams reference, kept alive for `next` + // and released by ordinary property teardown (no cycle: the iterator + // does not reference the source's keys); + // - a napi_external wrapping the IterState* for the `next` brand check. + // The external owns no data (nullptr finalize) — st is freed by the + // iterator's own finalizer below. + napi_value brandVal; + napi_create_external(env, st, nullptr, nullptr, &brandVal); + napi_property_descriptor descs[2] = {}; + descs[0].utf8name = ITER_SOURCE_KEY; + descs[0].value = jsThis; + descs[0].attributes = napi_default; + descs[1].utf8name = ITER_BRAND_KEY; + descs[1].value = brandVal; + descs[1].attributes = napi_default; + napi_define_properties(env, iterator, 2, descs); + + // Free the IterState when the iterator is collected. napi_add_finalizer + // works on a plain object on every engine (unlike napi_wrap, which this + // runtime backs with a V8 internal-field slot a plain object lacks). The + // callback only frees C++ memory (no napi/JS-heap calls), so it is safe to + // run synchronously during the GC sweep everywhere — no deferral needed. + napi_add_finalizer(env, iterator, st, [](napi_env, void *d, void *) { + delete static_cast(d); + }, nullptr, nullptr); + + napi_value nextFn; + napi_create_function(env, "next", NAPI_AUTO_LENGTH, IteratorNext, st, &nextFn); + napi_set_named_property(env, iterator, "next", nextFn); + + napi_value selfFn; + napi_create_function(env, "[Symbol.iterator]", NAPI_AUTO_LENGTH, IteratorSelf, nullptr, + &selfFn); + napi_set_property(env, iterator, SymbolIterator(env), selfFn); + + // Symbol.toStringTag = "URLSearchParams Iterator" for spec-correct + // Object.prototype.toString output. + napi_set_property(env, iterator, WellKnownSymbol(env, "toStringTag"), + js_str(env, "URLSearchParams Iterator")); + + return iterator; + } + + // Drives the ES iterator protocol on `iterable`, invoking fn(itemValue) for each + // yielded value. Returns false (with a pending exception) on protocol error or if + // fn returns false. + template + bool ForEachOfIterable(napi_env env, napi_value iterable, F &&fn) { + napi_value iterMethod; + if (napi_get_property(env, iterable, SymbolIterator(env), &iterMethod) != napi_ok) { + return false; + } + if (!napi_util::is_of_type(env, iterMethod, napi_function)) { + ThrowTypeError(env, "value is not iterable"); + return false; + } + napi_value iterObj; + if (napi_call_function(env, iterable, iterMethod, 0, nullptr, &iterObj) != napi_ok) { + return false; + } + if (!napi_util::is_object(env, iterObj)) { + ThrowTypeError(env, "iterator result is not an object"); + return false; + } + napi_value nextFn; + napi_get_named_property(env, iterObj, "next", &nextFn); + if (!napi_util::is_of_type(env, nextFn, napi_function)) { + ThrowTypeError(env, "iterator.next is not a function"); + return false; + } + + while (true) { + napi_value res; + if (napi_call_function(env, iterObj, nextFn, 0, nullptr, &res) != napi_ok) { + return false; + } + if (!napi_util::is_object(env, res)) { + ThrowTypeError(env, "iterator result is not an object"); + return false; + } + napi_value doneVal; + napi_get_named_property(env, res, "done", &doneVal); + bool done = false; + napi_value doneCoerced; + if (napi_coerce_to_bool(env, doneVal, &doneCoerced) == napi_ok) { + napi_get_value_bool(env, doneCoerced, &done); + } + if (done) { + break; + } + napi_value value; + napi_get_named_property(env, res, "value", &value); + if (!fn(value)) { + // abrupt completion → close the source iterator (runs finally blocks) + IteratorClose(env, iterObj); + return false; + } + } + return true; + } + + // sequence> form. + bool BuildFromSequence(napi_env env, napi_value iterable, url_search_params ¶ms) { + return ForEachOfIterable(env, iterable, [&](napi_value entry) -> bool { + if (!napi_util::is_object(env, entry)) { + ThrowTypeError(env, "URLSearchParams init sequence entry is not iterable"); + return false; + } + std::vector pair; + bool ok = ForEachOfIterable(env, entry, [&](napi_value item) -> bool { + std::string s; + if (!ValueToString(env, item, s)) { + return false; + } + pair.push_back(std::move(s)); + return true; + }); + if (!ok) { + return false; + } + if (pair.size() != 2) { + ThrowTypeError(env, + "URLSearchParams init sequence entry does not contain exactly two elements"); + return false; + } + params.append(pair[0], pair[1]); + return true; + }); + } + + // record form. + bool BuildFromRecord(napi_env env, napi_value object, url_search_params ¶ms) { + napi_value keys; + if (napi_get_all_property_names(env, object, napi_key_own_only, napi_key_enumerable, + napi_key_numbers_to_strings, &keys) != napi_ok) { + return false; + } + uint32_t len = 0; + napi_get_array_length(env, keys, &len); + for (uint32_t i = 0; i < len; i++) { + napi_value key, value; + napi_get_element(env, keys, i, &key); + napi_get_property(env, object, key, &value); + std::string k, v; + if (!ValueToString(env, key, k) || !ValueToString(env, value, v)) { + return false; // e.g. Symbol key + } + params.append(k, v); + } + return true; } } @@ -34,21 +348,40 @@ napi_value URLSearchParams::New(napi_env env, napi_callback_info info) { url_search_params params; - if (argc > 0) { - size_t str_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_size)) { - return nullptr; - } - - std::vector buffer(str_size + 1); - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], buffer.data(), str_size + 1, nullptr)) { - return nullptr; + if (argc > 0 && !napi_util::is_undefined(env, argv[0])) { + if (napi_util::is_of_type(env, argv[0], napi_string)) { + std::string init; + if (!ValueToString(env, argv[0], init)) { + return nullptr; + } + params = url_search_params(init); + } else if (napi_util::is_object(env, argv[0])) { + napi_value iterMethod; + if (napi_get_property(env, argv[0], SymbolIterator(env), &iterMethod) != napi_ok) { + return nullptr; + } + if (napi_util::is_null_or_undefined(env, iterMethod)) { + if (!BuildFromRecord(env, argv[0], params)) { + return nullptr; + } + } else if (napi_util::is_of_type(env, iterMethod, napi_function)) { + if (!BuildFromSequence(env, argv[0], params)) { + return nullptr; + } + } else { + ThrowTypeError(env, "URLSearchParams init Symbol.iterator is not a function"); + return nullptr; + } + } else { + // number / boolean / bigint / null / symbol -> USVString + std::string init; + if (!ValueToString(env, argv[0], init)) { + return nullptr; + } + params = url_search_params(init); } - - params = url_search_params(std::string_view(buffer.data(), str_size)); } - URLSearchParams *searchParams = new URLSearchParams(params); napi_wrap(env, jsThis, searchParams, URLSearchParams::Destructor, searchParams, nullptr); @@ -71,56 +404,46 @@ napi_value URLSearchParams::Append(napi_env env, napi_callback_info info) { URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 2) return nullptr; - - size_t key_size, value_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &key_size)) { - return nullptr; - } - NAPI_GUARD(napi_get_value_string_utf8(env, argv[1], nullptr, 0, &value_size)) { + if (argc < 2) { + ThrowTypeError(env, "URLSearchParams.append requires 2 arguments"); return nullptr; } - std::vector key_buffer(key_size + 1); - std::vector value_buffer(value_size + 1); - - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], key_buffer.data(), key_size + 1, nullptr)) { - return nullptr; - } - NAPI_GUARD(napi_get_value_string_utf8(env, argv[1], value_buffer.data(), value_size + 1, - nullptr)) { + std::string key, value; + if (!ValueToString(env, argv[0], key) || !ValueToString(env, argv[1], value)) { return nullptr; } - instance->GetURLSearchParams()->append(key_buffer.data(), value_buffer.data()); + instance->GetURLSearchParams()->append(key, value); return nullptr; } napi_value URLSearchParams::Has(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN(1) + NAPI_CALLBACK_BEGIN(2) URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 1) return nullptr; + if (argc < 1) return napi_util::get_false(env); - size_t str_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_size)) { + std::string key; + if (!ValueToString(env, argv[0], key)) { return nullptr; } - std::vector buffer(str_size + 1); - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], buffer.data(), str_size + 1, nullptr)) { - return nullptr; + bool has; + if (argc > 1 && !napi_util::is_undefined(env, argv[1])) { + std::string value; + if (!ValueToString(env, argv[1], value)) { + return nullptr; + } + has = instance->GetURLSearchParams()->has(key, value); + } else { + has = instance->GetURLSearchParams()->has(key); } - bool has = instance->GetURLSearchParams()->has(buffer.data()); - napi_value result; - NAPI_GUARD(napi_get_boolean(env, has, &result)) { - return nullptr; - } - + napi_get_boolean(env, has, &result); return result; } @@ -130,55 +453,47 @@ napi_value URLSearchParams::Get(napi_env env, napi_callback_info info) { URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 1) return nullptr; + if (argc < 1) return napi_util::null(env); - size_t str_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_size)) { + std::string key; + if (!ValueToString(env, argv[0], key)) { return nullptr; } - std::vector buffer(str_size + 1); - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], buffer.data(), str_size + 1, nullptr)) { - return nullptr; - } - - auto value = instance->GetURLSearchParams()->get(buffer.data()); + auto value = instance->GetURLSearchParams()->get(key); if (!value.has_value()) { - napi_value undefined; - NAPI_GUARD(napi_get_undefined(env, &undefined)) { - return nullptr; - } - return undefined; + // Per spec, a missing name returns null. + return napi_util::null(env); } - napi_value result; - NAPI_GUARD( - napi_create_string_utf8(env, value.value().data(), value.value().length(), &result)) { - return nullptr; - } - - return result; + return js_str(env, value.value()); } napi_value URLSearchParams::Delete(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN(1) + NAPI_CALLBACK_BEGIN(2) URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 1) return nullptr; - - size_t str_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_size)) { + if (argc < 1) { + ThrowTypeError(env, "URLSearchParams.delete requires 1 argument"); return nullptr; } - std::vector buffer(str_size + 1); - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], buffer.data(), str_size + 1, nullptr)) { + std::string key; + if (!ValueToString(env, argv[0], key)) { return nullptr; } - instance->GetURLSearchParams()->remove(buffer.data()); + if (argc > 1 && !napi_util::is_undefined(env, argv[1])) { + std::string value; + if (!ValueToString(env, argv[1], value)) { + return nullptr; + } + instance->GetURLSearchParams()->remove(key, value); + } else { + instance->GetURLSearchParams()->remove(key); + } return nullptr; } @@ -188,35 +503,23 @@ napi_value URLSearchParams::GetAll(napi_env env, napi_callback_info info) { URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 1) return nullptr; - - size_t str_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_size)) { + if (argc < 1) { + ThrowTypeError(env, "URLSearchParams.getAll requires 1 argument"); return nullptr; } - std::vector buffer(str_size + 1); - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], buffer.data(), str_size + 1, nullptr)) { + std::string key; + if (!ValueToString(env, argv[0], key)) { return nullptr; } - auto values = instance->GetURLSearchParams()->get_all(buffer.data()); + auto values = instance->GetURLSearchParams()->get_all(key); napi_value result; - NAPI_GUARD(napi_create_array_with_length(env, values.size(), &result)) { - return nullptr; - } - + napi_create_array_with_length(env, values.size(), &result); for (size_t i = 0; i < values.size(); i++) { - napi_value item; - NAPI_GUARD(napi_create_string_utf8(env, values[i].data(), values[i].length(), &item)) { - return nullptr; - } - NAPI_GUARD(napi_set_element(env, result, i, item)) { - return nullptr; - } + napi_set_element(env, result, i, js_str(env, values[i])); } - return result; } @@ -226,48 +529,31 @@ napi_value URLSearchParams::Set(napi_env env, napi_callback_info info) { URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 2) return nullptr; - - size_t key_size, value_size; - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], nullptr, 0, &key_size)) { - return nullptr; - } - NAPI_GUARD(napi_get_value_string_utf8(env, argv[1], nullptr, 0, &value_size)) { + if (argc < 2) { + ThrowTypeError(env, "URLSearchParams.set requires 2 arguments"); return nullptr; } - std::vector key_buffer(key_size + 1); - std::vector value_buffer(value_size + 1); - - NAPI_GUARD(napi_get_value_string_utf8(env, argv[0], key_buffer.data(), key_size + 1, nullptr)) { - return nullptr; - } - NAPI_GUARD(napi_get_value_string_utf8(env, argv[1], value_buffer.data(), value_size + 1, - nullptr)) { + std::string key, value; + if (!ValueToString(env, argv[0], key) || !ValueToString(env, argv[1], value)) { return nullptr; } - instance->GetURLSearchParams()->set(key_buffer.data(), value_buffer.data()); + instance->GetURLSearchParams()->set(key, value); return nullptr; } napi_value URLSearchParams::GetSize(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; auto size = instance->GetURLSearchParams()->size(); - napi_value result; - NAPI_GUARD(napi_create_int32(env, static_cast(size), &result)) { - return nullptr; - } - + napi_create_int32(env, static_cast(size), &result); return result; } napi_value URLSearchParams::Sort(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; @@ -276,176 +562,63 @@ napi_value URLSearchParams::Sort(napi_env env, napi_callback_info info) { } napi_value URLSearchParams::ToString(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; auto value = instance->GetURLSearchParams()->to_string(); - - napi_value result; - NAPI_GUARD(napi_create_string_utf8(env, value.data(), value.length(), &result)) { - return nullptr; - } - - return result; + return js_str(env, value); } napi_value URLSearchParams::Keys(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE - URLSearchParams *instance = GetInstance(env, info); - if (!instance) return nullptr; - - auto keys = instance->GetURLSearchParams()->get_keys(); - std::vector key_list; - - while (keys.has_next()) { - if (auto key = keys.next()) { - key_list.push_back(key.value()); - } - } - - napi_value result; - NAPI_GUARD(napi_create_array_with_length(env, key_list.size(), &result)) { - return nullptr; - } - - for (size_t i = 0; i < key_list.size(); i++) { - napi_value item; - NAPI_GUARD(napi_create_string_utf8(env, key_list[i].data(), key_list[i].length(), &item)) { - return nullptr; - } - NAPI_GUARD(napi_set_element(env, result, i, item)) { - return nullptr; - } - } - - return result; + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + if (!GetInstance(env, info)) return nullptr; + return MakeIterator(env, jsThis, ITER_KEYS); } napi_value URLSearchParams::Values(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE - URLSearchParams *instance = GetInstance(env, info); - if (!instance) return nullptr; - - auto keys = instance->GetURLSearchParams()->get_keys(); - std::vector value_list; - - while (keys.has_next()) { - if (auto key = keys.next()) { - if (auto value = instance->GetURLSearchParams()->get(key.value())) { - value_list.push_back(value.value()); - } - } - } - - napi_value result; - NAPI_GUARD(napi_create_array_with_length(env, value_list.size(), &result)) { - return nullptr; - } - - for (size_t i = 0; i < value_list.size(); i++) { - napi_value item; - NAPI_GUARD( - napi_create_string_utf8(env, value_list[i].data(), value_list[i].length(), &item)) { - return nullptr; - } - NAPI_GUARD(napi_set_element(env, result, i, item)) { - return nullptr; - } - } - - return result; + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + if (!GetInstance(env, info)) return nullptr; + return MakeIterator(env, jsThis, ITER_VALUES); } napi_value URLSearchParams::Entries(napi_env env, napi_callback_info info) { - NAPI_PREAMBLE - URLSearchParams *instance = GetInstance(env, info); - if (!instance) return nullptr; - - auto keys = instance->GetURLSearchParams()->get_keys(); - std::vector> entries; - - while (keys.has_next()) { - if (auto key = keys.next()) { - if (auto value = instance->GetURLSearchParams()->get(key.value())) { - entries.emplace_back(key.value(), value.value()); - } - } - } - - napi_value result; - NAPI_GUARD(napi_create_array_with_length(env, entries.size(), &result)) { - return nullptr; - } - - for (size_t i = 0; i < entries.size(); i++) { - napi_value entry; - NAPI_GUARD(napi_create_array_with_length(env, 2, &entry)) { - return nullptr; - } - - napi_value key, value; - NAPI_GUARD(napi_create_string_utf8(env, entries[i].first.data(), entries[i].first.length(), - &key)) { - return nullptr; - } - NAPI_GUARD( - napi_create_string_utf8(env, entries[i].second.data(), entries[i].second.length(), - &value)) { - return nullptr; - } - - NAPI_GUARD(napi_set_element(env, entry, 0, key)) { - return nullptr; - } - NAPI_GUARD(napi_set_element(env, entry, 1, value)) { - return nullptr; - } - NAPI_GUARD(napi_set_element(env, result, i, entry)) { - return nullptr; - } - } - - return result; + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + if (!GetInstance(env, info)) return nullptr; + return MakeIterator(env, jsThis, ITER_ENTRIES); } napi_value URLSearchParams::ForEach(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN(1) + NAPI_CALLBACK_BEGIN(2) URLSearchParams *instance = GetInstance(env, info); if (!instance) return nullptr; - if (argc < 1) return nullptr; + if (argc < 1 || !napi_util::is_of_type(env, argv[0], napi_function)) { + ThrowTypeError(env, "URLSearchParams.forEach requires a callback function"); + return nullptr; + } napi_value callback = argv[0]; napi_value thisArg = argc >= 2 ? argv[1] : nullptr; - auto keys = instance->GetURLSearchParams()->get_keys(); - while (keys.has_next()) { - if (auto key = keys.next()) { - if (auto value = instance->GetURLSearchParams()->get(key.value())) { - napi_value args[3]; - NAPI_GUARD( - napi_create_string_utf8(env, value.value().data(), value.value().length(), - &args[0])) { - return nullptr; - } - NAPI_GUARD(napi_create_string_utf8(env, key.value().data(), key.value().length(), - &args[1])) { - return nullptr; - } - args[2] = jsThis; - - napi_value global; - NAPI_GUARD(napi_get_global(env, &global)) { - return nullptr; - } - - napi_value result; - NAPI_GUARD(napi_call_function(env, thisArg ? thisArg : global, callback, 3, args, - &result)) { - return nullptr; - } + napi_value global; + napi_get_global(env, &global); + + // Use get_entries() so duplicate keys (e.g. ?a=1&a=2) each yield their own value. + auto entries = instance->GetURLSearchParams()->get_entries(); + while (entries.has_next()) { + if (auto entry = entries.next()) { + auto &[key, value] = entry.value(); + // Per spec, forEach callback receives (value, key, searchParams). + napi_value args[3] = {js_str(env, value), js_str(env, key), jsThis}; + napi_value result; + if (napi_call_function(env, thisArg ? thisArg : global, callback, 3, args, &result) != + napi_ok) { + // If the callback throws, stop iteration. + return nullptr; } } } @@ -480,6 +653,13 @@ void URLSearchParams::Init(napi_env env) { return; } + // Make prototype[Symbol.iterator] === entries (spec: default iterator is entries()). + napi_value proto, entriesFn; + if (napi_get_named_property(env, ctor, "prototype", &proto) == napi_ok && + napi_get_named_property(env, proto, "entries", &entriesFn) == napi_ok) { + napi_set_property(env, proto, SymbolIterator(env), entriesFn); + } + napi_value global; NAPI_GUARD(napi_get_global(env, &global)) { return; @@ -488,4 +668,4 @@ void URLSearchParams::Init(napi_env env) { NAPI_GUARD(napi_set_named_property(env, global, "URLSearchParams", ctor)) { return; } -} \ No newline at end of file +} diff --git a/test-app/runtime/src/main/cpp/modules/url/ada/ada.cpp b/test-app/runtime/src/main/cpp/modules/url/ada/ada.cpp index 8237277f..72f0e316 100644 --- a/test-app/runtime/src/main/cpp/modules/url/ada/ada.cpp +++ b/test-app/runtime/src/main/cpp/modules/url/ada/ada.cpp @@ -1,28 +1,29 @@ -/* auto-generated on 2024-07-06 17:38:56 -0400. Do not edit! */ +/* auto-generated on 2025-09-23 12:57:35 -0400. Do not edit! */ /* begin file src/ada.cpp */ #include "ada.h" /* begin file src/checkers.cpp */ #include +#include +#include namespace ada::checkers { -ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { +ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept { // The string is not empty and does not contain upper case ASCII characters. // // Optimization. To be considered as a possible ipv4, the string must end // with 'x' or a lowercase hex character. // Most of the time, this will be false so this simple check will save a lot // of effort. - char last_char = view.back(); // If the address ends with a dot, we need to prune it (special case). - if (last_char == '.') { + if (view.ends_with('.')) { view.remove_suffix(1); if (view.empty()) { return false; } - last_char = view.back(); } + char last_char = view.back(); bool possible_ipv4 = (last_char >= '0' && last_char <= '9') || (last_char >= 'a' && last_char <= 'f') || last_char == 'x'; @@ -38,7 +39,7 @@ ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { /** Optimization opportunity: we have basically identified the last number of the ipv4 if we return true here. We might as well parse it and have at least one number parsed when we get to parse_ipv4. */ - if (std::all_of(view.begin(), view.end(), ada::checkers::is_digit)) { + if (std::ranges::all_of(view, ada::checkers::is_digit)) { return true; } // It could be hex (0x), but not if there is a single character. @@ -46,7 +47,7 @@ ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { return false; } // It must start with 0x. - if (!std::equal(view.begin(), view.begin() + 2, "0x")) { + if (!view.starts_with("0x")) { return false; } // We must allow "0x". @@ -55,32 +56,32 @@ ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { } // We have 0x followed by some characters, we need to check that they are // hexadecimals. - return std::all_of(view.begin() + 2, view.end(), - ada::unicode::is_lowercase_hex); + view.remove_prefix(2); + return std::ranges::all_of(view, ada::unicode::is_lowercase_hex); } // for use with path_signature, we include all characters that need percent // encoding. static constexpr std::array path_signature_table = - []() constexpr { - std::array result{}; - for (size_t i = 0; i < 256; i++) { - if (i <= 0x20 || i == 0x22 || i == 0x23 || i == 0x3c || i == 0x3e || - i == 0x3f || i == 0x60 || i == 0x7b || i == 0x7d || i > 0x7e) { - result[i] = 1; - } else if (i == 0x25) { - result[i] = 8; - } else if (i == 0x2e) { - result[i] = 4; - } else if (i == 0x5c) { - result[i] = 2; - } else { - result[i] = 0; - } - } - return result; -} -(); + []() consteval { + std::array result{}; + for (size_t i = 0; i < 256; i++) { + if (i <= 0x20 || i == 0x22 || i == 0x23 || i == 0x3c || i == 0x3e || + i == 0x3f || i == 0x5e || i == 0x60 || i == 0x7b || i == 0x7d || + i > 0x7e) { + result[i] = 1; + } else if (i == 0x25) { + result[i] = 8; + } else if (i == 0x2e) { + result[i] = 4; + } else if (i == 0x5c) { + result[i] = 2; + } else { + result[i] = 0; + } + } + return result; + }(); ada_really_inline constexpr uint8_t path_signature( std::string_view input) noexcept { @@ -134,7 +135,7 @@ ada_really_inline constexpr bool verify_dns_length( ADA_PUSH_DISABLE_ALL_WARNINGS /* begin file src/ada_idna.cpp */ -/* auto-generated on 2023-09-19 15:58:51 -0400. Do not edit! */ +/* auto-generated on 2025-06-26 23:04:30 -0300. Do not edit! */ /* begin file src/idna.cpp */ /* begin file src/unicode_transcoding.cpp */ @@ -147,7 +148,7 @@ namespace ada::idna { size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { const uint8_t* data = reinterpret_cast(buf); size_t pos = 0; - char32_t* start{utf32_output}; + const char32_t* start{utf32_output}; while (pos < len) { // try to convert the next block of 16 ASCII bytes if (pos + 16 <= len) { // if it is safe to read 16 more @@ -266,7 +267,7 @@ size_t utf32_length_from_utf8(const char* buf, size_t len) { size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { const uint32_t* data = reinterpret_cast(buf); size_t pos = 0; - char* start{utf8_output}; + const char* start{utf8_output}; while (pos < len) { // try to convert the next block of 2 ASCII characters if (pos + 2 <= len) { // if it is safe to read 8 more @@ -326,7 +327,7 @@ size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { #include /* begin file src/mapping_tables.cpp */ -// IDNA 15.0.0 +// IDNA 16.0.0 // clang-format off #ifndef ADA_IDNA_TABLES_H @@ -335,7 +336,7 @@ size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { namespace ada::idna { -const uint32_t mappings[5164] = +const uint32_t mappings[5236] = { 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 32, 32, 776, 32, 772, 50, 51, 32, 769, @@ -364,360 +365,366 @@ const uint32_t mappings[5164] = 1125, 1127, 1129, 1131, 1133, 1135, 1137, 1139, 1141, 1143, 1145, 1147, 1149, 1151, 1153, 1163, 1165, 1167, 1169, 1171, 1173, 1175, 1177, 1179, 1181, 1183, 1185, 1187, 1189, 1191, 1193, 1195, 1197, 1199, 1201, 1203, 1205, 1207, 1209, 1211, 1213, 1215, - 1218, 1220, 1222, 1224, 1226, 1228, 1230, 1233, 1235, 1237, 1239, 1241, 1243, 1245, - 1247, 1249, 1251, 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1271, 1273, - 1275, 1277, 1279, 1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, 1297, 1299, 1301, - 1303, 1305, 1307, 1309, 1311, 1313, 1315, 1317, 1319, 1321, 1323, 1325, 1327, 1377, - 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, - 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, - 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1381, 1410, 1575, 1652, 1608, - 1652, 1735, 1652, 1610, 1652, 2325, 2364, 2326, 2364, 2327, 2364, 2332, 2364, 2337, - 2364, 2338, 2364, 2347, 2364, 2351, 2364, 2465, 2492, 2466, 2492, 2479, 2492, 2610, - 2620, 2616, 2620, 2582, 2620, 2583, 2620, 2588, 2620, 2603, 2620, 2849, 2876, 2850, - 2876, 3661, 3634, 3789, 3762, 3755, 3737, 3755, 3745, 3851, 3906, 4023, 3916, 4023, - 3921, 4023, 3926, 4023, 3931, 4023, 3904, 4021, 3953, 3954, 3953, 3956, 4018, 3968, - 4018, 3953, 3968, 4019, 3968, 4019, 3953, 3968, 3986, 4023, 3996, 4023, 4001, 4023, - 4006, 4023, 4011, 4023, 3984, 4021, 11559, 11565, 4316, 5104, 5105, 5106, 5107, - 5108, 5109, 42571, 4304, 4305, 4306, 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, - 4315, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, - 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343, - 4344, 4345, 4346, 4349, 4350, 4351, 592, 593, 7426, 604, 7446, 7447, 7453, 7461, - 594, 597, 607, 609, 613, 618, 7547, 669, 621, 7557, 671, 625, 624, 627, 628, 632, - 642, 427, 7452, 656, 657, 7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, - 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715, 7717, 7719, 7721, 7723, 7725, - 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751, 7753, - 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, - 7783, 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, - 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, 97, 702, 115, 115, 7841, - 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, 7863, 7865, 7867, 7869, - 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895, 7897, - 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, - 7927, 7929, 7931, 7933, 7935, 7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943, 7952, - 7953, 7954, 7955, 7956, 7957, 7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975, 7984, - 7985, 7986, 7987, 7988, 7989, 7990, 7991, 8000, 8001, 8002, 8003, 8004, 8005, 8017, - 8019, 8021, 8023, 8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 7936, 953, 7937, - 953, 7938, 953, 7939, 953, 7940, 953, 7941, 953, 7942, 953, 7943, 953, 7968, 953, - 7969, 953, 7970, 953, 7971, 953, 7972, 953, 7973, 953, 7974, 953, 7975, 953, 8032, - 953, 8033, 953, 8034, 953, 8035, 953, 8036, 953, 8037, 953, 8038, 953, 8039, 953, - 8048, 953, 945, 953, 940, 953, 8118, 953, 8112, 8113, 32, 787, 32, 834, 32, 776, - 834, 8052, 953, 951, 953, 942, 953, 8134, 953, 8050, 32, 787, 768, 32, 787, 769, - 32, 787, 834, 912, 8144, 8145, 8054, 32, 788, 768, 32, 788, 769, 32, 788, 834, 944, - 8160, 8161, 8058, 8165, 32, 776, 768, 96, 8060, 953, 969, 953, 974, 953, 8182, 953, - 8056, 8208, 32, 819, 8242, 8242, 8242, 8242, 8242, 8245, 8245, 8245, 8245, 8245, - 33, 33, 32, 773, 63, 63, 63, 33, 33, 63, 48, 53, 54, 55, 56, 57, 43, 8722, 61, 40, - 41, 97, 47, 99, 97, 47, 115, 176, 99, 99, 47, 111, 99, 47, 117, 176, 102, 115, 109, - 116, 101, 108, 116, 109, 1488, 1489, 1490, 1491, 102, 97, 120, 8721, 49, 8260, 55, - 49, 8260, 57, 49, 8260, 49, 48, 49, 8260, 51, 50, 8260, 51, 49, 8260, 53, 50, 8260, - 53, 51, 8260, 53, 52, 8260, 53, 49, 8260, 54, 53, 8260, 54, 49, 8260, 56, 51, 8260, - 56, 53, 8260, 56, 55, 8260, 56, 105, 105, 105, 105, 105, 105, 118, 118, 105, 118, - 105, 105, 118, 105, 105, 105, 105, 120, 120, 105, 120, 105, 105, 48, 8260, 51, 8747, - 8747, 8747, 8747, 8747, 8750, 8750, 8750, 8750, 8750, 12296, 12297, 49, 50, 49, - 51, 49, 52, 49, 53, 49, 54, 49, 55, 49, 56, 49, 57, 50, 48, 40, 49, 41, 40, 50, - 41, 40, 51, 41, 40, 52, 41, 40, 53, 41, 40, 54, 41, 40, 55, 41, 40, 56, 41, 40, - 57, 41, 40, 49, 48, 41, 40, 49, 49, 41, 40, 49, 50, 41, 40, 49, 51, 41, 40, 49, - 52, 41, 40, 49, 53, 41, 40, 49, 54, 41, 40, 49, 55, 41, 40, 49, 56, 41, 40, 49, - 57, 41, 40, 50, 48, 41, 40, 97, 41, 40, 98, 41, 40, 99, 41, 40, 100, 41, 40, 101, - 41, 40, 102, 41, 40, 103, 41, 40, 104, 41, 40, 105, 41, 40, 106, 41, 40, 107, 41, - 40, 108, 41, 40, 109, 41, 40, 110, 41, 40, 111, 41, 40, 112, 41, 40, 113, 41, 40, - 114, 41, 40, 115, 41, 40, 116, 41, 40, 117, 41, 40, 118, 41, 40, 119, 41, 40, 120, - 41, 40, 121, 41, 40, 122, 41, 58, 58, 61, 61, 61, 10973, 824, 11312, 11313, 11314, - 11315, 11316, 11317, 11318, 11319, 11320, 11321, 11322, 11323, 11324, 11325, 11326, - 11327, 11328, 11329, 11330, 11331, 11332, 11333, 11334, 11335, 11336, 11337, 11338, - 11339, 11340, 11341, 11342, 11343, 11344, 11345, 11346, 11347, 11348, 11349, 11350, - 11351, 11352, 11353, 11354, 11355, 11356, 11357, 11358, 11359, 11361, 619, 7549, - 637, 11368, 11370, 11372, 11379, 11382, 575, 576, 11393, 11395, 11397, 11399, 11401, - 11403, 11405, 11407, 11409, 11411, 11413, 11415, 11417, 11419, 11421, 11423, 11425, - 11427, 11429, 11431, 11433, 11435, 11437, 11439, 11441, 11443, 11445, 11447, 11449, - 11451, 11453, 11455, 11457, 11459, 11461, 11463, 11465, 11467, 11469, 11471, 11473, - 11475, 11477, 11479, 11481, 11483, 11485, 11487, 11489, 11491, 11500, 11502, 11507, - 11617, 27597, 40863, 19968, 20008, 20022, 20031, 20057, 20101, 20108, 20128, 20154, - 20799, 20837, 20843, 20866, 20886, 20907, 20960, 20981, 20992, 21147, 21241, 21269, - 21274, 21304, 21313, 21340, 21353, 21378, 21430, 21448, 21475, 22231, 22303, 22763, - 22786, 22794, 22805, 22823, 22899, 23376, 23424, 23544, 23567, 23586, 23608, 23662, - 23665, 24027, 24037, 24049, 24062, 24178, 24186, 24191, 24308, 24318, 24331, 24339, - 24400, 24417, 24435, 24515, 25096, 25142, 25163, 25903, 25908, 25991, 26007, 26020, - 26041, 26080, 26085, 26352, 26376, 26408, 27424, 27490, 27513, 27571, 27595, 27604, - 27611, 27663, 27668, 27700, 28779, 29226, 29238, 29243, 29247, 29255, 29273, 29275, - 29356, 29572, 29577, 29916, 29926, 29976, 29983, 29992, 30000, 30091, 30098, 30326, - 30333, 30382, 30399, 30446, 30683, 30690, 30707, 31034, 31160, 31166, 31348, 31435, - 31481, 31859, 31992, 32566, 32593, 32650, 32701, 32769, 32780, 32786, 32819, 32895, - 32905, 33251, 33258, 33267, 33276, 33292, 33307, 33311, 33390, 33394, 33400, 34381, - 34411, 34880, 34892, 34915, 35198, 35211, 35282, 35328, 35895, 35910, 35925, 35960, - 35997, 36196, 36208, 36275, 36523, 36554, 36763, 36784, 36789, 37009, 37193, 37318, - 37324, 37329, 38263, 38272, 38428, 38582, 38585, 38632, 38737, 38750, 38754, 38761, - 38859, 38893, 38899, 38913, 39080, 39131, 39135, 39318, 39321, 39340, 39592, 39640, - 39647, 39717, 39727, 39730, 39740, 39770, 40165, 40565, 40575, 40613, 40635, 40643, - 40653, 40657, 40697, 40701, 40718, 40723, 40736, 40763, 40778, 40786, 40845, 40860, - 40864, 46, 12306, 21316, 21317, 32, 12441, 32, 12442, 12424, 12426, 12467, 12488, - 4352, 4353, 4522, 4354, 4524, 4525, 4355, 4356, 4357, 4528, 4529, 4530, 4531, 4532, - 4533, 4378, 4358, 4359, 4360, 4385, 4361, 4362, 4363, 4364, 4365, 4366, 4367, 4368, - 4369, 4370, 4449, 4450, 4451, 4452, 4453, 4454, 4455, 4456, 4457, 4458, 4459, 4460, - 4461, 4462, 4463, 4464, 4465, 4466, 4467, 4468, 4469, 4372, 4373, 4551, 4552, 4556, - 4558, 4563, 4567, 4569, 4380, 4573, 4575, 4381, 4382, 4384, 4386, 4387, 4391, 4393, - 4395, 4396, 4397, 4398, 4399, 4402, 4406, 4416, 4423, 4428, 4593, 4594, 4439, 4440, - 4441, 4484, 4485, 4488, 4497, 4498, 4500, 4510, 4513, 19977, 22235, 19978, 20013, - 19979, 30002, 19993, 19969, 22825, 22320, 40, 4352, 41, 40, 4354, 41, 40, 4355, - 41, 40, 4357, 41, 40, 4358, 41, 40, 4359, 41, 40, 4361, 41, 40, 4363, 41, 40, 4364, - 41, 40, 4366, 41, 40, 4367, 41, 40, 4368, 41, 40, 4369, 41, 40, 4370, 41, 40, 44032, - 41, 40, 45208, 41, 40, 45796, 41, 40, 46972, 41, 40, 47560, 41, 40, 48148, 41, 40, - 49324, 41, 40, 50500, 41, 40, 51088, 41, 40, 52264, 41, 40, 52852, 41, 40, 53440, - 41, 40, 54028, 41, 40, 54616, 41, 40, 51452, 41, 40, 50724, 51204, 41, 40, 50724, - 54980, 41, 40, 19968, 41, 40, 20108, 41, 40, 19977, 41, 40, 22235, 41, 40, 20116, - 41, 40, 20845, 41, 40, 19971, 41, 40, 20843, 41, 40, 20061, 41, 40, 21313, 41, 40, - 26376, 41, 40, 28779, 41, 40, 27700, 41, 40, 26408, 41, 40, 37329, 41, 40, 22303, - 41, 40, 26085, 41, 40, 26666, 41, 40, 26377, 41, 40, 31038, 41, 40, 21517, 41, 40, - 29305, 41, 40, 36001, 41, 40, 31069, 41, 40, 21172, 41, 40, 20195, 41, 40, 21628, - 41, 40, 23398, 41, 40, 30435, 41, 40, 20225, 41, 40, 36039, 41, 40, 21332, 41, 40, - 31085, 41, 40, 20241, 41, 40, 33258, 41, 40, 33267, 41, 21839, 24188, 31631, 112, - 116, 101, 50, 50, 50, 52, 50, 53, 50, 54, 50, 55, 50, 56, 50, 57, 51, 48, 51, 51, - 51, 52, 51, 53, 52280, 44256, 51452, 51032, 50864, 31192, 30007, 36969, 20778, 21360, - 27880, 38917, 20889, 27491, 24038, 21491, 21307, 23447, 22812, 51, 54, 51, 55, 51, - 56, 51, 57, 52, 48, 52, 52, 52, 53, 52, 54, 52, 55, 52, 56, 52, 57, 53, 48, 49, - 26376, 50, 26376, 51, 26376, 52, 26376, 53, 26376, 54, 26376, 55, 26376, 56, 26376, - 57, 26376, 49, 48, 26376, 49, 49, 26376, 49, 50, 26376, 104, 103, 101, 114, 103, - 101, 118, 108, 116, 100, 12450, 12452, 12454, 12456, 12458, 12459, 12461, 12463, - 12465, 12469, 12471, 12473, 12475, 12477, 12479, 12481, 12484, 12486, 12490, 12491, - 12492, 12493, 12494, 12495, 12498, 12501, 12504, 12507, 12510, 12511, 12512, 12513, - 12514, 12516, 12518, 12520, 12521, 12522, 12523, 12524, 12525, 12527, 12528, 12529, - 12530, 20196, 21644, 12450, 12497, 12540, 12488, 12450, 12523, 12501, 12449, 12450, - 12531, 12506, 12450, 12450, 12540, 12523, 12452, 12491, 12531, 12464, 12452, 12531, - 12481, 12454, 12457, 12531, 12456, 12473, 12463, 12540, 12489, 12456, 12540, 12459, - 12540, 12458, 12531, 12473, 12458, 12540, 12512, 12459, 12452, 12522, 12459, 12521, - 12483, 12488, 12459, 12525, 12522, 12540, 12460, 12525, 12531, 12460, 12531, 12510, - 12462, 12460, 12462, 12491, 12540, 12461, 12517, 12522, 12540, 12462, 12523, 12480, - 12540, 12461, 12525, 12461, 12525, 12464, 12521, 12512, 12461, 12525, 12513, 12540, - 12488, 12523, 12461, 12525, 12527, 12483, 12488, 12464, 12521, 12512, 12488, 12531, - 12463, 12523, 12476, 12452, 12525, 12463, 12525, 12540, 12493, 12465, 12540, 12473, - 12467, 12523, 12490, 12467, 12540, 12509, 12469, 12452, 12463, 12523, 12469, 12531, - 12481, 12540, 12512, 12471, 12522, 12531, 12464, 12475, 12531, 12481, 12475, 12531, - 12488, 12480, 12540, 12473, 12487, 12471, 12489, 12523, 12490, 12494, 12494, 12483, - 12488, 12495, 12452, 12484, 12497, 12540, 12475, 12531, 12488, 12497, 12540, 12484, - 12496, 12540, 12524, 12523, 12500, 12450, 12473, 12488, 12523, 12500, 12463, 12523, - 12500, 12467, 12499, 12523, 12501, 12449, 12521, 12483, 12489, 12501, 12451, 12540, - 12488, 12502, 12483, 12471, 12455, 12523, 12501, 12521, 12531, 12504, 12463, 12479, - 12540, 12523, 12506, 12477, 12506, 12491, 12498, 12504, 12523, 12484, 12506, 12531, - 12473, 12506, 12540, 12472, 12505, 12540, 12479, 12509, 12452, 12531, 12488, 12508, - 12523, 12488, 12507, 12531, 12509, 12531, 12489, 12507, 12540, 12523, 12507, 12540, - 12531, 12510, 12452, 12463, 12525, 12510, 12452, 12523, 12510, 12483, 12495, 12510, - 12523, 12463, 12510, 12531, 12471, 12519, 12531, 12511, 12463, 12525, 12531, 12511, - 12522, 12511, 12522, 12496, 12540, 12523, 12513, 12460, 12513, 12460, 12488, 12531, - 12516, 12540, 12489, 12516, 12540, 12523, 12518, 12450, 12531, 12522, 12483, 12488, - 12523, 12522, 12521, 12523, 12500, 12540, 12523, 12540, 12502, 12523, 12524, 12512, - 12524, 12531, 12488, 12466, 12531, 48, 28857, 49, 28857, 50, 28857, 51, 28857, 52, - 28857, 53, 28857, 54, 28857, 55, 28857, 56, 28857, 57, 28857, 49, 48, 28857, 49, - 49, 28857, 49, 50, 28857, 49, 51, 28857, 49, 52, 28857, 49, 53, 28857, 49, 54, 28857, - 49, 55, 28857, 49, 56, 28857, 49, 57, 28857, 50, 48, 28857, 50, 49, 28857, 50, 50, - 28857, 50, 51, 28857, 50, 52, 28857, 104, 112, 97, 100, 97, 97, 117, 98, 97, 114, - 111, 118, 112, 99, 100, 109, 100, 109, 50, 100, 109, 51, 105, 117, 24179, 25104, - 26157, 21644, 22823, 27491, 26126, 27835, 26666, 24335, 20250, 31038, 110, 97, 956, - 97, 109, 97, 107, 97, 107, 98, 109, 98, 103, 98, 99, 97, 108, 107, 99, 97, 108, - 112, 102, 110, 102, 956, 102, 956, 103, 109, 103, 107, 103, 104, 122, 107, 104, - 122, 109, 104, 122, 116, 104, 122, 956, 108, 109, 108, 100, 108, 102, 109, 110, - 109, 956, 109, 109, 109, 99, 109, 107, 109, 109, 109, 50, 99, 109, 50, 107, 109, - 50, 109, 109, 51, 99, 109, 51, 107, 109, 51, 109, 8725, 115, 109, 8725, 115, 50, - 107, 112, 97, 109, 112, 97, 103, 112, 97, 114, 97, 100, 114, 97, 100, 8725, 115, - 114, 97, 100, 8725, 115, 50, 112, 115, 110, 115, 956, 115, 109, 115, 112, 118, 110, - 118, 956, 118, 109, 118, 107, 118, 112, 119, 110, 119, 956, 119, 109, 119, 107, - 119, 107, 969, 109, 969, 98, 113, 99, 8725, 107, 103, 100, 98, 103, 121, 104, 97, - 105, 110, 107, 107, 107, 116, 108, 110, 108, 111, 103, 108, 120, 109, 105, 108, - 109, 111, 108, 112, 104, 112, 112, 109, 112, 114, 115, 118, 119, 98, 118, 8725, - 109, 97, 8725, 109, 49, 26085, 50, 26085, 51, 26085, 52, 26085, 53, 26085, 54, 26085, - 55, 26085, 56, 26085, 57, 26085, 49, 48, 26085, 49, 49, 26085, 49, 50, 26085, 49, - 51, 26085, 49, 52, 26085, 49, 53, 26085, 49, 54, 26085, 49, 55, 26085, 49, 56, 26085, - 49, 57, 26085, 50, 48, 26085, 50, 49, 26085, 50, 50, 26085, 50, 51, 26085, 50, 52, - 26085, 50, 53, 26085, 50, 54, 26085, 50, 55, 26085, 50, 56, 26085, 50, 57, 26085, - 51, 48, 26085, 51, 49, 26085, 103, 97, 108, 42561, 42563, 42565, 42567, 42569, 42573, - 42575, 42577, 42579, 42581, 42583, 42585, 42587, 42589, 42591, 42593, 42595, 42597, - 42599, 42601, 42603, 42605, 42625, 42627, 42629, 42631, 42633, 42635, 42637, 42639, - 42641, 42643, 42645, 42647, 42649, 42651, 42787, 42789, 42791, 42793, 42795, 42797, - 42799, 42803, 42805, 42807, 42809, 42811, 42813, 42815, 42817, 42819, 42821, 42823, - 42825, 42827, 42829, 42831, 42833, 42835, 42837, 42839, 42841, 42843, 42845, 42847, - 42849, 42851, 42853, 42855, 42857, 42859, 42861, 42863, 42874, 42876, 7545, 42879, - 42881, 42883, 42885, 42887, 42892, 42897, 42899, 42903, 42905, 42907, 42909, 42911, - 42913, 42915, 42917, 42919, 42921, 620, 670, 647, 43859, 42933, 42935, 42937, 42939, - 42941, 42943, 42945, 42947, 42900, 7566, 42952, 42954, 42961, 42967, 42969, 42998, - 43831, 43858, 653, 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, 35912, - 26356, 36040, 28369, 20018, 21477, 22865, 21895, 22856, 25078, 30313, 32645, 34367, - 34746, 35064, 37007, 27138, 27931, 28889, 29662, 33853, 37226, 39409, 20098, 21365, - 27396, 29211, 34349, 40478, 23888, 28651, 34253, 35172, 25289, 33240, 34847, 24266, - 26391, 28010, 29436, 37070, 20358, 20919, 21214, 25796, 27347, 29200, 30439, 34310, - 34396, 36335, 38706, 39791, 40442, 30860, 31103, 32160, 33737, 37636, 35542, 22751, - 24324, 31840, 32894, 29282, 30922, 36034, 38647, 22744, 23650, 27155, 28122, 28431, - 32047, 32311, 38475, 21202, 32907, 20956, 20940, 31260, 32190, 33777, 38517, 35712, - 25295, 35582, 20025, 23527, 24594, 29575, 30064, 21271, 30971, 20415, 24489, 19981, - 27852, 25976, 32034, 21443, 22622, 30465, 33865, 35498, 27578, 27784, 25342, 33509, - 25504, 30053, 20142, 20841, 20937, 26753, 31975, 33391, 35538, 37327, 21237, 21570, - 24300, 26053, 28670, 31018, 38317, 39530, 40599, 40654, 26310, 27511, 36706, 24180, - 24976, 25088, 25754, 28451, 29001, 29833, 31178, 32244, 32879, 36646, 34030, 36899, - 37706, 21015, 21155, 21693, 28872, 35010, 24265, 24565, 25467, 27566, 31806, 29557, - 22265, 23994, 24604, 29618, 29801, 32666, 32838, 37428, 38646, 38728, 38936, 20363, - 31150, 37300, 38584, 24801, 20102, 20698, 23534, 23615, 26009, 29134, 30274, 34044, - 36988, 26248, 38446, 21129, 26491, 26611, 27969, 28316, 29705, 30041, 30827, 32016, - 39006, 25134, 38520, 20523, 23833, 28138, 36650, 24459, 24900, 26647, 38534, 21033, - 21519, 23653, 26131, 26446, 26792, 27877, 29702, 30178, 32633, 35023, 35041, 38626, - 21311, 28346, 21533, 29136, 29848, 34298, 38563, 40023, 40607, 26519, 28107, 33256, - 31520, 31890, 29376, 28825, 35672, 20160, 33590, 21050, 20999, 24230, 25299, 31958, - 23429, 27934, 26292, 36667, 38477, 24275, 20800, 21952, 22618, 26228, 20958, 29482, - 30410, 31036, 31070, 31077, 31119, 38742, 31934, 34322, 35576, 36920, 37117, 39151, - 39164, 39208, 40372, 37086, 38583, 20398, 20711, 20813, 21193, 21220, 21329, 21917, - 22022, 22120, 22592, 22696, 23652, 24724, 24936, 24974, 25074, 25935, 26082, 26257, - 26757, 28023, 28186, 28450, 29038, 29227, 29730, 30865, 31049, 31048, 31056, 31062, - 31117, 31118, 31296, 31361, 31680, 32265, 32321, 32626, 32773, 33261, 33401, 33879, - 35088, 35222, 35585, 35641, 36051, 36104, 36790, 38627, 38911, 38971, 24693, 148206, - 33304, 20006, 20917, 20840, 20352, 20805, 20864, 21191, 21242, 21845, 21913, 21986, - 22707, 22852, 22868, 23138, 23336, 24274, 24281, 24425, 24493, 24792, 24910, 24840, - 24928, 25140, 25540, 25628, 25682, 25942, 26395, 26454, 28379, 28363, 28702, 30631, - 29237, 29359, 29809, 29958, 30011, 30237, 30239, 30427, 30452, 30538, 30528, 30924, - 31409, 31867, 32091, 32574, 33618, 33775, 34681, 35137, 35206, 35519, 35531, 35565, - 35722, 36664, 36978, 37273, 37494, 38524, 38875, 38923, 39698, 141386, 141380, 144341, - 15261, 16408, 16441, 152137, 154832, 163539, 40771, 40846, 102, 102, 102, 105, 102, - 108, 102, 102, 108, 1396, 1398, 1396, 1381, 1396, 1387, 1406, 1398, 1396, 1389, - 1497, 1460, 1522, 1463, 1506, 1492, 1499, 1500, 1501, 1512, 1514, 1513, 1473, 1513, - 1474, 1513, 1468, 1473, 1513, 1468, 1474, 1488, 1463, 1488, 1464, 1488, 1468, 1489, - 1468, 1490, 1468, 1491, 1468, 1492, 1468, 1493, 1468, 1494, 1468, 1496, 1468, 1497, - 1468, 1498, 1468, 1499, 1468, 1500, 1468, 1502, 1468, 1504, 1468, 1505, 1468, 1507, - 1468, 1508, 1468, 1510, 1468, 1511, 1468, 1512, 1468, 1514, 1468, 1493, 1465, 1489, - 1471, 1499, 1471, 1508, 1471, 1488, 1500, 1649, 1659, 1662, 1664, 1658, 1663, 1657, - 1700, 1702, 1668, 1667, 1670, 1671, 1677, 1676, 1678, 1672, 1688, 1681, 1705, 1711, - 1715, 1713, 1722, 1723, 1728, 1729, 1726, 1746, 1747, 1709, 1734, 1736, 1739, 1733, - 1737, 1744, 1609, 1574, 1575, 1574, 1749, 1574, 1608, 1574, 1735, 1574, 1734, 1574, - 1736, 1574, 1744, 1574, 1609, 1740, 1574, 1580, 1574, 1581, 1574, 1605, 1574, 1610, - 1576, 1580, 1576, 1581, 1576, 1582, 1576, 1605, 1576, 1609, 1576, 1610, 1578, 1580, - 1578, 1581, 1578, 1582, 1578, 1605, 1578, 1609, 1578, 1610, 1579, 1580, 1579, 1605, - 1579, 1609, 1579, 1610, 1580, 1581, 1580, 1605, 1581, 1605, 1582, 1580, 1582, 1581, - 1582, 1605, 1587, 1580, 1587, 1581, 1587, 1582, 1587, 1605, 1589, 1581, 1589, 1605, - 1590, 1580, 1590, 1581, 1590, 1582, 1590, 1605, 1591, 1581, 1591, 1605, 1592, 1605, - 1593, 1580, 1593, 1605, 1594, 1580, 1594, 1605, 1601, 1580, 1601, 1581, 1601, 1582, - 1601, 1605, 1601, 1609, 1601, 1610, 1602, 1581, 1602, 1605, 1602, 1609, 1602, 1610, - 1603, 1575, 1603, 1580, 1603, 1581, 1603, 1582, 1603, 1604, 1603, 1605, 1603, 1609, - 1603, 1610, 1604, 1580, 1604, 1581, 1604, 1582, 1604, 1605, 1604, 1609, 1604, 1610, - 1605, 1580, 1605, 1605, 1605, 1609, 1605, 1610, 1606, 1580, 1606, 1581, 1606, 1582, - 1606, 1605, 1606, 1609, 1606, 1610, 1607, 1580, 1607, 1605, 1607, 1609, 1607, 1610, - 1610, 1581, 1610, 1582, 1610, 1609, 1584, 1648, 1585, 1648, 1609, 1648, 32, 1612, - 1617, 32, 1613, 1617, 32, 1614, 1617, 32, 1615, 1617, 32, 1616, 1617, 32, 1617, - 1648, 1574, 1585, 1574, 1586, 1574, 1606, 1576, 1585, 1576, 1586, 1576, 1606, 1578, - 1585, 1578, 1586, 1578, 1606, 1579, 1585, 1579, 1586, 1579, 1606, 1605, 1575, 1606, - 1585, 1606, 1586, 1606, 1606, 1610, 1585, 1610, 1586, 1574, 1582, 1574, 1607, 1576, - 1607, 1578, 1607, 1589, 1582, 1604, 1607, 1606, 1607, 1607, 1648, 1579, 1607, 1587, - 1607, 1588, 1605, 1588, 1607, 1600, 1614, 1617, 1600, 1615, 1617, 1600, 1616, 1617, - 1591, 1609, 1591, 1610, 1593, 1609, 1593, 1610, 1594, 1609, 1594, 1610, 1587, 1609, - 1587, 1610, 1588, 1609, 1588, 1610, 1581, 1609, 1580, 1609, 1580, 1610, 1582, 1609, - 1589, 1609, 1589, 1610, 1590, 1609, 1590, 1610, 1588, 1580, 1588, 1581, 1588, 1582, - 1588, 1585, 1587, 1585, 1589, 1585, 1590, 1585, 1575, 1611, 1578, 1580, 1605, 1578, - 1581, 1580, 1578, 1581, 1605, 1578, 1582, 1605, 1578, 1605, 1580, 1578, 1605, 1581, - 1578, 1605, 1582, 1581, 1605, 1610, 1581, 1605, 1609, 1587, 1581, 1580, 1587, 1580, - 1581, 1587, 1580, 1609, 1587, 1605, 1581, 1587, 1605, 1580, 1587, 1605, 1605, 1589, - 1581, 1581, 1589, 1605, 1605, 1588, 1581, 1605, 1588, 1580, 1610, 1588, 1605, 1582, - 1588, 1605, 1605, 1590, 1581, 1609, 1590, 1582, 1605, 1591, 1605, 1581, 1591, 1605, - 1605, 1591, 1605, 1610, 1593, 1580, 1605, 1593, 1605, 1605, 1593, 1605, 1609, 1594, - 1605, 1605, 1594, 1605, 1610, 1594, 1605, 1609, 1601, 1582, 1605, 1602, 1605, 1581, - 1602, 1605, 1605, 1604, 1581, 1605, 1604, 1581, 1610, 1604, 1581, 1609, 1604, 1580, - 1580, 1604, 1582, 1605, 1604, 1605, 1581, 1605, 1581, 1580, 1605, 1581, 1610, 1605, - 1580, 1581, 1605, 1582, 1605, 1605, 1580, 1582, 1607, 1605, 1580, 1607, 1605, 1605, - 1606, 1581, 1605, 1606, 1581, 1609, 1606, 1580, 1605, 1606, 1580, 1609, 1606, 1605, - 1610, 1606, 1605, 1609, 1610, 1605, 1605, 1576, 1582, 1610, 1578, 1580, 1610, 1578, - 1580, 1609, 1578, 1582, 1610, 1578, 1582, 1609, 1578, 1605, 1610, 1578, 1605, 1609, - 1580, 1605, 1610, 1580, 1581, 1609, 1580, 1605, 1609, 1587, 1582, 1609, 1589, 1581, - 1610, 1588, 1581, 1610, 1590, 1581, 1610, 1604, 1580, 1610, 1604, 1605, 1610, 1610, - 1580, 1610, 1610, 1605, 1610, 1605, 1605, 1610, 1602, 1605, 1610, 1606, 1581, 1610, - 1593, 1605, 1610, 1603, 1605, 1610, 1606, 1580, 1581, 1605, 1582, 1610, 1604, 1580, - 1605, 1603, 1605, 1605, 1580, 1581, 1610, 1581, 1580, 1610, 1605, 1580, 1610, 1601, - 1605, 1610, 1576, 1581, 1610, 1587, 1582, 1610, 1606, 1580, 1610, 1589, 1604, 1746, - 1602, 1604, 1746, 1575, 1604, 1604, 1607, 1575, 1603, 1576, 1585, 1605, 1581, 1605, - 1583, 1589, 1604, 1593, 1605, 1585, 1587, 1608, 1604, 1593, 1604, 1610, 1607, 1608, - 1587, 1604, 1605, 1589, 1604, 1609, 1589, 1604, 1609, 32, 1575, 1604, 1604, 1607, - 32, 1593, 1604, 1610, 1607, 32, 1608, 1587, 1604, 1605, 1580, 1604, 32, 1580, 1604, - 1575, 1604, 1607, 1585, 1740, 1575, 1604, 44, 12289, 12310, 12311, 8212, 8211, 95, - 123, 125, 12308, 12309, 12304, 12305, 12298, 12299, 12300, 12301, 12302, 12303, - 91, 93, 35, 38, 42, 45, 60, 62, 92, 36, 37, 64, 32, 1611, 1600, 1611, 1600, 1617, - 32, 1618, 1600, 1618, 1569, 1570, 1571, 1572, 1573, 1577, 1604, 1570, 1604, 1571, - 1604, 1573, 34, 39, 94, 124, 126, 10629, 10630, 12539, 12453, 12515, 162, 163, 172, - 166, 165, 8361, 9474, 8592, 8593, 8594, 8595, 9632, 9675, 66600, 66601, 66602, 66603, - 66604, 66605, 66606, 66607, 66608, 66609, 66610, 66611, 66612, 66613, 66614, 66615, - 66616, 66617, 66618, 66619, 66620, 66621, 66622, 66623, 66624, 66625, 66626, 66627, - 66628, 66629, 66630, 66631, 66632, 66633, 66634, 66635, 66636, 66637, 66638, 66639, - 66776, 66777, 66778, 66779, 66780, 66781, 66782, 66783, 66784, 66785, 66786, 66787, - 66788, 66789, 66790, 66791, 66792, 66793, 66794, 66795, 66796, 66797, 66798, 66799, - 66800, 66801, 66802, 66803, 66804, 66805, 66806, 66807, 66808, 66809, 66810, 66811, - 66967, 66968, 66969, 66970, 66971, 66972, 66973, 66974, 66975, 66976, 66977, 66979, - 66980, 66981, 66982, 66983, 66984, 66985, 66986, 66987, 66988, 66989, 66990, 66991, - 66992, 66993, 66995, 66996, 66997, 66998, 66999, 67000, 67001, 67003, 67004, 720, - 721, 665, 675, 43878, 677, 676, 7569, 600, 606, 681, 612, 610, 667, 668, 615, 644, - 682, 683, 122628, 42894, 622, 122629, 654, 122630, 630, 631, 634, 122632, 638, 680, - 678, 43879, 679, 11377, 655, 673, 674, 664, 448, 449, 450, 122634, 122654, 68800, - 68801, 68802, 68803, 68804, 68805, 68806, 68807, 68808, 68809, 68810, 68811, 68812, - 68813, 68814, 68815, 68816, 68817, 68818, 68819, 68820, 68821, 68822, 68823, 68824, - 68825, 68826, 68827, 68828, 68829, 68830, 68831, 68832, 68833, 68834, 68835, 68836, - 68837, 68838, 68839, 68840, 68841, 68842, 68843, 68844, 68845, 68846, 68847, 68848, - 68849, 68850, 71872, 71873, 71874, 71875, 71876, 71877, 71878, 71879, 71880, 71881, - 71882, 71883, 71884, 71885, 71886, 71887, 71888, 71889, 71890, 71891, 71892, 71893, - 71894, 71895, 71896, 71897, 71898, 71899, 71900, 71901, 71902, 71903, 93792, 93793, - 93794, 93795, 93796, 93797, 93798, 93799, 93800, 93801, 93802, 93803, 93804, 93805, - 93806, 93807, 93808, 93809, 93810, 93811, 93812, 93813, 93814, 93815, 93816, 93817, - 93818, 93819, 93820, 93821, 93822, 93823, 119127, 119141, 119128, 119141, 119128, - 119141, 119150, 119128, 119141, 119151, 119128, 119141, 119152, 119128, 119141, - 119153, 119128, 119141, 119154, 119225, 119141, 119226, 119141, 119225, 119141, - 119150, 119226, 119141, 119150, 119225, 119141, 119151, 119226, 119141, 119151, - 305, 567, 8711, 8706, 1231, 125218, 125219, 125220, 125221, 125222, 125223, 125224, - 125225, 125226, 125227, 125228, 125229, 125230, 125231, 125232, 125233, 125234, - 125235, 125236, 125237, 125238, 125239, 125240, 125241, 125242, 125243, 125244, - 125245, 125246, 125247, 125248, 125249, 125250, 125251, 1646, 1697, 1647, 48, 44, - 49, 44, 50, 44, 51, 44, 52, 44, 53, 44, 54, 44, 55, 44, 56, 44, 57, 44, 12308, 115, - 12309, 119, 122, 104, 118, 115, 100, 112, 112, 118, 119, 99, 109, 114, 100, 106, - 12411, 12363, 12467, 12467, 23383, 21452, 22810, 35299, 20132, 26144, 28961, 21069, - 24460, 20877, 26032, 21021, 32066, 36009, 22768, 21561, 28436, 25237, 25429, 36938, - 25351, 25171, 31105, 31354, 21512, 28288, 30003, 21106, 21942, 37197, 12308, 26412, - 12309, 12308, 19977, 12309, 12308, 20108, 12309, 12308, 23433, 12309, 12308, 28857, - 12309, 12308, 25171, 12309, 12308, 30423, 12309, 12308, 21213, 12309, 12308, 25943, - 12309, 24471, 21487, 20029, 20024, 20033, 131362, 20320, 20411, 20482, 20602, 20633, - 20687, 13470, 132666, 20820, 20836, 20855, 132380, 13497, 20839, 132427, 20887, - 20900, 20172, 20908, 168415, 20995, 13535, 21051, 21062, 21111, 13589, 21253, 21254, - 21321, 21338, 21363, 21373, 21375, 133676, 28784, 21450, 21471, 133987, 21483, 21489, - 21510, 21662, 21560, 21576, 21608, 21666, 21750, 21776, 21843, 21859, 21892, 21931, - 21939, 21954, 22294, 22295, 22097, 22132, 22766, 22478, 22516, 22541, 22411, 22578, - 22577, 22700, 136420, 22770, 22775, 22790, 22818, 22882, 136872, 136938, 23020, - 23067, 23079, 23000, 23142, 14062, 23304, 23358, 137672, 23491, 23512, 23539, 138008, - 23551, 23558, 14209, 23648, 23744, 23693, 138724, 23875, 138726, 23918, 23915, 23932, - 24033, 24034, 14383, 24061, 24104, 24125, 24169, 14434, 139651, 14460, 24240, 24243, - 24246, 172946, 140081, 33281, 24354, 14535, 144056, 156122, 24418, 24427, 14563, - 24474, 24525, 24535, 24569, 24705, 14650, 14620, 141012, 24775, 24904, 24908, 24954, - 25010, 24996, 25007, 25054, 25115, 25181, 25265, 25300, 25424, 142092, 25405, 25340, - 25448, 25475, 25572, 142321, 25634, 25541, 25513, 14894, 25705, 25726, 25757, 25719, - 14956, 25964, 143370, 26083, 26360, 26185, 15129, 15112, 15076, 20882, 20885, 26368, - 26268, 32941, 17369, 26401, 26462, 26451, 144323, 15177, 26618, 26501, 26706, 144493, - 26766, 26655, 26900, 26946, 27043, 27114, 27304, 145059, 27355, 15384, 27425, 145575, - 27476, 15438, 27506, 27551, 27579, 146061, 138507, 146170, 27726, 146620, 27839, - 27853, 27751, 27926, 27966, 28009, 28024, 28037, 146718, 27956, 28207, 28270, 15667, - 28359, 147153, 28153, 28526, 147294, 147342, 28614, 28729, 28699, 15766, 28746, - 28797, 28791, 28845, 132389, 28997, 148067, 29084, 29224, 29264, 149000, 29312, - 29333, 149301, 149524, 29562, 29579, 16044, 29605, 16056, 29767, 29788, 29829, 29898, - 16155, 29988, 150582, 30014, 150674, 139679, 30224, 151457, 151480, 151620, 16380, - 16392, 151795, 151794, 151833, 151859, 30494, 30495, 30603, 16454, 16534, 152605, - 30798, 16611, 153126, 153242, 153285, 31211, 16687, 31306, 31311, 153980, 154279, - 16898, 154539, 31686, 31689, 16935, 154752, 31954, 17056, 31976, 31971, 32000, 155526, - 32099, 17153, 32199, 32258, 32325, 17204, 156200, 156231, 17241, 156377, 32634, - 156478, 32661, 32762, 156890, 156963, 32864, 157096, 32880, 144223, 17365, 32946, - 33027, 17419, 33086, 23221, 157607, 157621, 144275, 144284, 33284, 36766, 17515, - 33425, 33419, 33437, 21171, 33457, 33459, 33469, 33510, 158524, 33565, 33635, 33709, - 33571, 33725, 33767, 33619, 33738, 33740, 33756, 158774, 159083, 158933, 17707, - 34033, 34035, 34070, 160714, 34148, 159532, 17757, 17761, 159665, 159954, 17771, - 34384, 34407, 34409, 34473, 34440, 34574, 34530, 34600, 34667, 34694, 34785, 34817, - 17913, 34912, 161383, 35031, 35038, 17973, 35066, 13499, 161966, 162150, 18110, - 18119, 35488, 162984, 36011, 36033, 36123, 36215, 163631, 133124, 36299, 36284, - 36336, 133342, 36564, 165330, 165357, 37012, 37105, 37137, 165678, 37147, 37432, - 37591, 37592, 37500, 37881, 37909, 166906, 38283, 18837, 38327, 167287, 18918, 38595, - 23986, 38691, 168261, 168474, 19054, 19062, 38880, 168970, 19122, 169110, 38953, - 169398, 39138, 19251, 39209, 39335, 39362, 39422, 19406, 170800, 40000, 40189, 19662, - 19693, 40295, 172238, 19704, 172293, 172558, 172689, 19798, 40702, 40709, 40719, - 40726, 173568, + 1231, 1218, 1220, 1222, 1224, 1226, 1228, 1230, 1233, 1235, 1237, 1239, 1241, 1243, + 1245, 1247, 1249, 1251, 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1271, + 1273, 1275, 1277, 1279, 1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, 1297, 1299, + 1301, 1303, 1305, 1307, 1309, 1311, 1313, 1315, 1317, 1319, 1321, 1323, 1325, 1327, + 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, + 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, + 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1381, 1410, 1575, 1652, + 1608, 1652, 1735, 1652, 1610, 1652, 2325, 2364, 2326, 2364, 2327, 2364, 2332, 2364, + 2337, 2364, 2338, 2364, 2347, 2364, 2351, 2364, 2465, 2492, 2466, 2492, 2479, 2492, + 2610, 2620, 2616, 2620, 2582, 2620, 2583, 2620, 2588, 2620, 2603, 2620, 2849, 2876, + 2850, 2876, 3661, 3634, 3789, 3762, 3755, 3737, 3755, 3745, 3851, 3906, 4023, 3916, + 4023, 3921, 4023, 3926, 4023, 3931, 4023, 3904, 4021, 3953, 3954, 3953, 3956, 4018, + 3968, 4018, 3953, 3968, 4019, 3968, 4019, 3953, 3968, 3986, 4023, 3996, 4023, 4001, + 4023, 4006, 4023, 4011, 4023, 3984, 4021, 11520, 11521, 11522, 11523, 11524, 11525, + 11526, 11527, 11528, 11529, 11530, 11531, 11532, 11533, 11534, 11535, 11536, 11537, + 11538, 11539, 11540, 11541, 11542, 11543, 11544, 11545, 11546, 11547, 11548, 11549, + 11550, 11551, 11552, 11553, 11554, 11555, 11556, 11557, 11559, 11565, 4316, 5104, + 5105, 5106, 5107, 5108, 5109, 42571, 7306, 4304, 4305, 4306, 4307, 4308, 4309, 4310, + 4311, 4312, 4313, 4314, 4315, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, + 4326, 4327, 4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, 4339, + 4340, 4341, 4342, 4343, 4344, 4345, 4346, 4349, 4350, 4351, 592, 593, 7426, 604, + 7446, 7447, 7453, 7461, 594, 597, 607, 609, 613, 618, 7547, 669, 621, 7557, 671, + 625, 624, 627, 628, 632, 642, 427, 7452, 656, 657, 7681, 7683, 7685, 7687, 7689, + 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715, 7717, + 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, + 7747, 7749, 7751, 7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, + 7775, 7777, 7779, 7781, 7783, 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, + 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, + 97, 702, 223, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, + 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, + 7891, 7893, 7895, 7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, + 7919, 7921, 7923, 7925, 7927, 7929, 7931, 7933, 7935, 7936, 7937, 7938, 7939, 7940, + 7941, 7942, 7943, 7952, 7953, 7954, 7955, 7956, 7957, 7968, 7969, 7970, 7971, 7972, + 7973, 7974, 7975, 7984, 7985, 7986, 7987, 7988, 7989, 7990, 7991, 8000, 8001, 8002, + 8003, 8004, 8005, 8017, 8019, 8021, 8023, 8032, 8033, 8034, 8035, 8036, 8037, 8038, + 8039, 7936, 953, 7937, 953, 7938, 953, 7939, 953, 7940, 953, 7941, 953, 7942, 953, + 7943, 953, 7968, 953, 7969, 953, 7970, 953, 7971, 953, 7972, 953, 7973, 953, 7974, + 953, 7975, 953, 8032, 953, 8033, 953, 8034, 953, 8035, 953, 8036, 953, 8037, 953, + 8038, 953, 8039, 953, 8048, 953, 945, 953, 940, 953, 8118, 953, 8112, 8113, 32, + 787, 32, 834, 32, 776, 834, 8052, 953, 951, 953, 942, 953, 8134, 953, 8050, 32, + 787, 768, 32, 787, 769, 32, 787, 834, 912, 8144, 8145, 8054, 32, 788, 768, 32, 788, + 769, 32, 788, 834, 944, 8160, 8161, 8058, 8165, 32, 776, 768, 96, 8060, 953, 969, + 953, 974, 953, 8182, 953, 8056, 8208, 32, 819, 8242, 8242, 8242, 8242, 8242, 8245, + 8245, 8245, 8245, 8245, 33, 33, 32, 773, 63, 63, 63, 33, 33, 63, 48, 53, 54, 55, + 56, 57, 43, 8722, 61, 40, 41, 97, 47, 99, 97, 47, 115, 176, 99, 99, 47, 111, 99, + 47, 117, 176, 102, 115, 109, 116, 101, 108, 116, 109, 8526, 1488, 1489, 1490, 1491, + 102, 97, 120, 8721, 49, 8260, 55, 49, 8260, 57, 49, 8260, 49, 48, 49, 8260, 51, + 50, 8260, 51, 49, 8260, 53, 50, 8260, 53, 51, 8260, 53, 52, 8260, 53, 49, 8260, + 54, 53, 8260, 54, 49, 8260, 56, 51, 8260, 56, 53, 8260, 56, 55, 8260, 56, 105, 105, + 105, 105, 105, 105, 118, 118, 105, 118, 105, 105, 118, 105, 105, 105, 105, 120, + 120, 105, 120, 105, 105, 8580, 48, 8260, 51, 8747, 8747, 8747, 8747, 8747, 8750, + 8750, 8750, 8750, 8750, 12296, 12297, 49, 50, 49, 51, 49, 52, 49, 53, 49, 54, 49, + 55, 49, 56, 49, 57, 50, 48, 40, 49, 41, 40, 50, 41, 40, 51, 41, 40, 52, 41, 40, + 53, 41, 40, 54, 41, 40, 55, 41, 40, 56, 41, 40, 57, 41, 40, 49, 48, 41, 40, 49, + 49, 41, 40, 49, 50, 41, 40, 49, 51, 41, 40, 49, 52, 41, 40, 49, 53, 41, 40, 49, + 54, 41, 40, 49, 55, 41, 40, 49, 56, 41, 40, 49, 57, 41, 40, 50, 48, 41, 40, 97, + 41, 40, 98, 41, 40, 99, 41, 40, 100, 41, 40, 101, 41, 40, 102, 41, 40, 103, 41, + 40, 104, 41, 40, 105, 41, 40, 106, 41, 40, 107, 41, 40, 108, 41, 40, 109, 41, 40, + 110, 41, 40, 111, 41, 40, 112, 41, 40, 113, 41, 40, 114, 41, 40, 115, 41, 40, 116, + 41, 40, 117, 41, 40, 118, 41, 40, 119, 41, 40, 120, 41, 40, 121, 41, 40, 122, 41, + 58, 58, 61, 61, 61, 10973, 824, 11312, 11313, 11314, 11315, 11316, 11317, 11318, + 11319, 11320, 11321, 11322, 11323, 11324, 11325, 11326, 11327, 11328, 11329, 11330, + 11331, 11332, 11333, 11334, 11335, 11336, 11337, 11338, 11339, 11340, 11341, 11342, + 11343, 11344, 11345, 11346, 11347, 11348, 11349, 11350, 11351, 11352, 11353, 11354, + 11355, 11356, 11357, 11358, 11359, 11361, 619, 7549, 637, 11368, 11370, 11372, 11379, + 11382, 575, 576, 11393, 11395, 11397, 11399, 11401, 11403, 11405, 11407, 11409, + 11411, 11413, 11415, 11417, 11419, 11421, 11423, 11425, 11427, 11429, 11431, 11433, + 11435, 11437, 11439, 11441, 11443, 11445, 11447, 11449, 11451, 11453, 11455, 11457, + 11459, 11461, 11463, 11465, 11467, 11469, 11471, 11473, 11475, 11477, 11479, 11481, + 11483, 11485, 11487, 11489, 11491, 11500, 11502, 11507, 11617, 27597, 40863, 19968, + 20008, 20022, 20031, 20057, 20101, 20108, 20128, 20154, 20799, 20837, 20843, 20866, + 20886, 20907, 20960, 20981, 20992, 21147, 21241, 21269, 21274, 21304, 21313, 21340, + 21353, 21378, 21430, 21448, 21475, 22231, 22303, 22763, 22786, 22794, 22805, 22823, + 22899, 23376, 23424, 23544, 23567, 23586, 23608, 23662, 23665, 24027, 24037, 24049, + 24062, 24178, 24186, 24191, 24308, 24318, 24331, 24339, 24400, 24417, 24435, 24515, + 25096, 25142, 25163, 25903, 25908, 25991, 26007, 26020, 26041, 26080, 26085, 26352, + 26376, 26408, 27424, 27490, 27513, 27571, 27595, 27604, 27611, 27663, 27668, 27700, + 28779, 29226, 29238, 29243, 29247, 29255, 29273, 29275, 29356, 29572, 29577, 29916, + 29926, 29976, 29983, 29992, 30000, 30091, 30098, 30326, 30333, 30382, 30399, 30446, + 30683, 30690, 30707, 31034, 31160, 31166, 31348, 31435, 31481, 31859, 31992, 32566, + 32593, 32650, 32701, 32769, 32780, 32786, 32819, 32895, 32905, 33251, 33258, 33267, + 33276, 33292, 33307, 33311, 33390, 33394, 33400, 34381, 34411, 34880, 34892, 34915, + 35198, 35211, 35282, 35328, 35895, 35910, 35925, 35960, 35997, 36196, 36208, 36275, + 36523, 36554, 36763, 36784, 36789, 37009, 37193, 37318, 37324, 37329, 38263, 38272, + 38428, 38582, 38585, 38632, 38737, 38750, 38754, 38761, 38859, 38893, 38899, 38913, + 39080, 39131, 39135, 39318, 39321, 39340, 39592, 39640, 39647, 39717, 39727, 39730, + 39740, 39770, 40165, 40565, 40575, 40613, 40635, 40643, 40653, 40657, 40697, 40701, + 40718, 40723, 40736, 40763, 40778, 40786, 40845, 40860, 40864, 46, 12306, 21316, + 21317, 32, 12441, 32, 12442, 12424, 12426, 12467, 12488, 4352, 4353, 4522, 4354, + 4524, 4525, 4355, 4356, 4357, 4528, 4529, 4530, 4531, 4532, 4533, 4378, 4358, 4359, + 4360, 4385, 4361, 4362, 4363, 4364, 4365, 4366, 4367, 4368, 4369, 4370, 4449, 4450, + 4451, 4452, 4453, 4454, 4455, 4456, 4457, 4458, 4459, 4460, 4461, 4462, 4463, 4464, + 4465, 4466, 4467, 4468, 4469, 4372, 4373, 4551, 4552, 4556, 4558, 4563, 4567, 4569, + 4380, 4573, 4575, 4381, 4382, 4384, 4386, 4387, 4391, 4393, 4395, 4396, 4397, 4398, + 4399, 4402, 4406, 4416, 4423, 4428, 4593, 4594, 4439, 4440, 4441, 4484, 4485, 4488, + 4497, 4498, 4500, 4510, 4513, 19977, 22235, 19978, 20013, 19979, 30002, 19993, 19969, + 22825, 22320, 40, 4352, 41, 40, 4354, 41, 40, 4355, 41, 40, 4357, 41, 40, 4358, + 41, 40, 4359, 41, 40, 4361, 41, 40, 4363, 41, 40, 4364, 41, 40, 4366, 41, 40, 4367, + 41, 40, 4368, 41, 40, 4369, 41, 40, 4370, 41, 40, 44032, 41, 40, 45208, 41, 40, + 45796, 41, 40, 46972, 41, 40, 47560, 41, 40, 48148, 41, 40, 49324, 41, 40, 50500, + 41, 40, 51088, 41, 40, 52264, 41, 40, 52852, 41, 40, 53440, 41, 40, 54028, 41, 40, + 54616, 41, 40, 51452, 41, 40, 50724, 51204, 41, 40, 50724, 54980, 41, 40, 19968, + 41, 40, 20108, 41, 40, 19977, 41, 40, 22235, 41, 40, 20116, 41, 40, 20845, 41, 40, + 19971, 41, 40, 20843, 41, 40, 20061, 41, 40, 21313, 41, 40, 26376, 41, 40, 28779, + 41, 40, 27700, 41, 40, 26408, 41, 40, 37329, 41, 40, 22303, 41, 40, 26085, 41, 40, + 26666, 41, 40, 26377, 41, 40, 31038, 41, 40, 21517, 41, 40, 29305, 41, 40, 36001, + 41, 40, 31069, 41, 40, 21172, 41, 40, 20195, 41, 40, 21628, 41, 40, 23398, 41, 40, + 30435, 41, 40, 20225, 41, 40, 36039, 41, 40, 21332, 41, 40, 31085, 41, 40, 20241, + 41, 40, 33258, 41, 40, 33267, 41, 21839, 24188, 31631, 112, 116, 101, 50, 50, 50, + 52, 50, 53, 50, 54, 50, 55, 50, 56, 50, 57, 51, 48, 51, 51, 51, 52, 51, 53, 52280, + 44256, 51452, 51032, 50864, 31192, 30007, 36969, 20778, 21360, 27880, 38917, 20889, + 27491, 24038, 21491, 21307, 23447, 22812, 51, 54, 51, 55, 51, 56, 51, 57, 52, 48, + 52, 52, 52, 53, 52, 54, 52, 55, 52, 56, 52, 57, 53, 48, 49, 26376, 50, 26376, 51, + 26376, 52, 26376, 53, 26376, 54, 26376, 55, 26376, 56, 26376, 57, 26376, 49, 48, + 26376, 49, 49, 26376, 49, 50, 26376, 104, 103, 101, 114, 103, 101, 118, 108, 116, + 100, 12450, 12452, 12454, 12456, 12458, 12459, 12461, 12463, 12465, 12469, 12471, + 12473, 12475, 12477, 12479, 12481, 12484, 12486, 12490, 12491, 12492, 12493, 12494, + 12495, 12498, 12501, 12504, 12507, 12510, 12511, 12512, 12513, 12514, 12516, 12518, + 12520, 12521, 12522, 12523, 12524, 12525, 12527, 12528, 12529, 12530, 20196, 21644, + 12450, 12497, 12540, 12488, 12450, 12523, 12501, 12449, 12450, 12531, 12506, 12450, + 12450, 12540, 12523, 12452, 12491, 12531, 12464, 12452, 12531, 12481, 12454, 12457, + 12531, 12456, 12473, 12463, 12540, 12489, 12456, 12540, 12459, 12540, 12458, 12531, + 12473, 12458, 12540, 12512, 12459, 12452, 12522, 12459, 12521, 12483, 12488, 12459, + 12525, 12522, 12540, 12460, 12525, 12531, 12460, 12531, 12510, 12462, 12460, 12462, + 12491, 12540, 12461, 12517, 12522, 12540, 12462, 12523, 12480, 12540, 12461, 12525, + 12461, 12525, 12464, 12521, 12512, 12461, 12525, 12513, 12540, 12488, 12523, 12461, + 12525, 12527, 12483, 12488, 12464, 12521, 12512, 12488, 12531, 12463, 12523, 12476, + 12452, 12525, 12463, 12525, 12540, 12493, 12465, 12540, 12473, 12467, 12523, 12490, + 12467, 12540, 12509, 12469, 12452, 12463, 12523, 12469, 12531, 12481, 12540, 12512, + 12471, 12522, 12531, 12464, 12475, 12531, 12481, 12475, 12531, 12488, 12480, 12540, + 12473, 12487, 12471, 12489, 12523, 12490, 12494, 12494, 12483, 12488, 12495, 12452, + 12484, 12497, 12540, 12475, 12531, 12488, 12497, 12540, 12484, 12496, 12540, 12524, + 12523, 12500, 12450, 12473, 12488, 12523, 12500, 12463, 12523, 12500, 12467, 12499, + 12523, 12501, 12449, 12521, 12483, 12489, 12501, 12451, 12540, 12488, 12502, 12483, + 12471, 12455, 12523, 12501, 12521, 12531, 12504, 12463, 12479, 12540, 12523, 12506, + 12477, 12506, 12491, 12498, 12504, 12523, 12484, 12506, 12531, 12473, 12506, 12540, + 12472, 12505, 12540, 12479, 12509, 12452, 12531, 12488, 12508, 12523, 12488, 12507, + 12531, 12509, 12531, 12489, 12507, 12540, 12523, 12507, 12540, 12531, 12510, 12452, + 12463, 12525, 12510, 12452, 12523, 12510, 12483, 12495, 12510, 12523, 12463, 12510, + 12531, 12471, 12519, 12531, 12511, 12463, 12525, 12531, 12511, 12522, 12511, 12522, + 12496, 12540, 12523, 12513, 12460, 12513, 12460, 12488, 12531, 12516, 12540, 12489, + 12516, 12540, 12523, 12518, 12450, 12531, 12522, 12483, 12488, 12523, 12522, 12521, + 12523, 12500, 12540, 12523, 12540, 12502, 12523, 12524, 12512, 12524, 12531, 12488, + 12466, 12531, 48, 28857, 49, 28857, 50, 28857, 51, 28857, 52, 28857, 53, 28857, + 54, 28857, 55, 28857, 56, 28857, 57, 28857, 49, 48, 28857, 49, 49, 28857, 49, 50, + 28857, 49, 51, 28857, 49, 52, 28857, 49, 53, 28857, 49, 54, 28857, 49, 55, 28857, + 49, 56, 28857, 49, 57, 28857, 50, 48, 28857, 50, 49, 28857, 50, 50, 28857, 50, 51, + 28857, 50, 52, 28857, 104, 112, 97, 100, 97, 97, 117, 98, 97, 114, 111, 118, 112, + 99, 100, 109, 100, 109, 50, 100, 109, 51, 105, 117, 24179, 25104, 26157, 21644, + 22823, 27491, 26126, 27835, 26666, 24335, 20250, 31038, 110, 97, 956, 97, 109, 97, + 107, 97, 107, 98, 109, 98, 103, 98, 99, 97, 108, 107, 99, 97, 108, 112, 102, 110, + 102, 956, 102, 956, 103, 109, 103, 107, 103, 104, 122, 107, 104, 122, 109, 104, + 122, 116, 104, 122, 956, 108, 109, 108, 100, 108, 102, 109, 110, 109, 956, 109, + 109, 109, 99, 109, 107, 109, 109, 109, 50, 99, 109, 50, 107, 109, 50, 109, 109, + 51, 99, 109, 51, 107, 109, 51, 109, 8725, 115, 109, 8725, 115, 50, 107, 112, 97, + 109, 112, 97, 103, 112, 97, 114, 97, 100, 114, 97, 100, 8725, 115, 114, 97, 100, + 8725, 115, 50, 112, 115, 110, 115, 956, 115, 109, 115, 112, 118, 110, 118, 956, + 118, 109, 118, 107, 118, 112, 119, 110, 119, 956, 119, 109, 119, 107, 119, 107, + 969, 109, 969, 98, 113, 99, 8725, 107, 103, 100, 98, 103, 121, 104, 97, 105, 110, + 107, 107, 107, 116, 108, 110, 108, 111, 103, 108, 120, 109, 105, 108, 109, 111, + 108, 112, 104, 112, 112, 109, 112, 114, 115, 118, 119, 98, 118, 8725, 109, 97, 8725, + 109, 49, 26085, 50, 26085, 51, 26085, 52, 26085, 53, 26085, 54, 26085, 55, 26085, + 56, 26085, 57, 26085, 49, 48, 26085, 49, 49, 26085, 49, 50, 26085, 49, 51, 26085, + 49, 52, 26085, 49, 53, 26085, 49, 54, 26085, 49, 55, 26085, 49, 56, 26085, 49, 57, + 26085, 50, 48, 26085, 50, 49, 26085, 50, 50, 26085, 50, 51, 26085, 50, 52, 26085, + 50, 53, 26085, 50, 54, 26085, 50, 55, 26085, 50, 56, 26085, 50, 57, 26085, 51, 48, + 26085, 51, 49, 26085, 103, 97, 108, 42561, 42563, 42565, 42567, 42569, 42573, 42575, + 42577, 42579, 42581, 42583, 42585, 42587, 42589, 42591, 42593, 42595, 42597, 42599, + 42601, 42603, 42605, 42625, 42627, 42629, 42631, 42633, 42635, 42637, 42639, 42641, + 42643, 42645, 42647, 42649, 42651, 42787, 42789, 42791, 42793, 42795, 42797, 42799, + 42803, 42805, 42807, 42809, 42811, 42813, 42815, 42817, 42819, 42821, 42823, 42825, + 42827, 42829, 42831, 42833, 42835, 42837, 42839, 42841, 42843, 42845, 42847, 42849, + 42851, 42853, 42855, 42857, 42859, 42861, 42863, 42874, 42876, 7545, 42879, 42881, + 42883, 42885, 42887, 42892, 42897, 42899, 42903, 42905, 42907, 42909, 42911, 42913, + 42915, 42917, 42919, 42921, 620, 670, 647, 43859, 42933, 42935, 42937, 42939, 42941, + 42943, 42945, 42947, 42900, 7566, 42952, 42954, 612, 42957, 42961, 42967, 42969, + 42971, 411, 42998, 43831, 43858, 653, 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, 35912, 26356, 36040, 28369, 20018, 21477, 22865, 21895, 22856, + 25078, 30313, 32645, 34367, 34746, 35064, 37007, 27138, 27931, 28889, 29662, 33853, + 37226, 39409, 20098, 21365, 27396, 29211, 34349, 40478, 23888, 28651, 34253, 35172, + 25289, 33240, 34847, 24266, 26391, 28010, 29436, 37070, 20358, 20919, 21214, 25796, + 27347, 29200, 30439, 34310, 34396, 36335, 38706, 39791, 40442, 30860, 31103, 32160, + 33737, 37636, 35542, 22751, 24324, 31840, 32894, 29282, 30922, 36034, 38647, 22744, + 23650, 27155, 28122, 28431, 32047, 32311, 38475, 21202, 32907, 20956, 20940, 31260, + 32190, 33777, 38517, 35712, 25295, 35582, 20025, 23527, 24594, 29575, 30064, 21271, + 30971, 20415, 24489, 19981, 27852, 25976, 32034, 21443, 22622, 30465, 33865, 35498, + 27578, 27784, 25342, 33509, 25504, 30053, 20142, 20841, 20937, 26753, 31975, 33391, + 35538, 37327, 21237, 21570, 24300, 26053, 28670, 31018, 38317, 39530, 40599, 40654, + 26310, 27511, 36706, 24180, 24976, 25088, 25754, 28451, 29001, 29833, 31178, 32244, + 32879, 36646, 34030, 36899, 37706, 21015, 21155, 21693, 28872, 35010, 24265, 24565, + 25467, 27566, 31806, 29557, 22265, 23994, 24604, 29618, 29801, 32666, 32838, 37428, + 38646, 38728, 38936, 20363, 31150, 37300, 38584, 24801, 20102, 20698, 23534, 23615, + 26009, 29134, 30274, 34044, 36988, 26248, 38446, 21129, 26491, 26611, 27969, 28316, + 29705, 30041, 30827, 32016, 39006, 25134, 38520, 20523, 23833, 28138, 36650, 24459, + 24900, 26647, 38534, 21033, 21519, 23653, 26131, 26446, 26792, 27877, 29702, 30178, + 32633, 35023, 35041, 38626, 21311, 28346, 21533, 29136, 29848, 34298, 38563, 40023, + 40607, 26519, 28107, 33256, 31520, 31890, 29376, 28825, 35672, 20160, 33590, 21050, + 20999, 24230, 25299, 31958, 23429, 27934, 26292, 36667, 38477, 24275, 20800, 21952, + 22618, 26228, 20958, 29482, 30410, 31036, 31070, 31077, 31119, 38742, 31934, 34322, + 35576, 36920, 37117, 39151, 39164, 39208, 40372, 37086, 38583, 20398, 20711, 20813, + 21193, 21220, 21329, 21917, 22022, 22120, 22592, 22696, 23652, 24724, 24936, 24974, + 25074, 25935, 26082, 26257, 26757, 28023, 28186, 28450, 29038, 29227, 29730, 30865, + 31049, 31048, 31056, 31062, 31117, 31118, 31296, 31361, 31680, 32265, 32321, 32626, + 32773, 33261, 33401, 33879, 35088, 35222, 35585, 35641, 36051, 36104, 36790, 38627, + 38911, 38971, 24693, 148206, 33304, 20006, 20917, 20840, 20352, 20805, 20864, 21191, + 21242, 21845, 21913, 21986, 22707, 22852, 22868, 23138, 23336, 24274, 24281, 24425, + 24493, 24792, 24910, 24840, 24928, 25140, 25540, 25628, 25682, 25942, 26395, 26454, + 28379, 28363, 28702, 30631, 29237, 29359, 29809, 29958, 30011, 30237, 30239, 30427, + 30452, 30538, 30528, 30924, 31409, 31867, 32091, 32574, 33618, 33775, 34681, 35137, + 35206, 35519, 35531, 35565, 35722, 36664, 36978, 37273, 37494, 38524, 38875, 38923, + 39698, 141386, 141380, 144341, 15261, 16408, 16441, 152137, 154832, 163539, 40771, + 40846, 102, 102, 102, 105, 102, 108, 102, 102, 108, 1396, 1398, 1396, 1381, 1396, + 1387, 1406, 1398, 1396, 1389, 1497, 1460, 1522, 1463, 1506, 1492, 1499, 1500, 1501, + 1512, 1514, 1513, 1473, 1513, 1474, 1513, 1468, 1473, 1513, 1468, 1474, 1488, 1463, + 1488, 1464, 1488, 1468, 1489, 1468, 1490, 1468, 1491, 1468, 1492, 1468, 1493, 1468, + 1494, 1468, 1496, 1468, 1497, 1468, 1498, 1468, 1499, 1468, 1500, 1468, 1502, 1468, + 1504, 1468, 1505, 1468, 1507, 1468, 1508, 1468, 1510, 1468, 1511, 1468, 1512, 1468, + 1514, 1468, 1493, 1465, 1489, 1471, 1499, 1471, 1508, 1471, 1488, 1500, 1649, 1659, + 1662, 1664, 1658, 1663, 1657, 1700, 1702, 1668, 1667, 1670, 1671, 1677, 1676, 1678, + 1672, 1688, 1681, 1705, 1711, 1715, 1713, 1722, 1723, 1728, 1729, 1726, 1746, 1747, + 1709, 1734, 1736, 1739, 1733, 1737, 1744, 1609, 1574, 1575, 1574, 1749, 1574, 1608, + 1574, 1735, 1574, 1734, 1574, 1736, 1574, 1744, 1574, 1609, 1740, 1574, 1580, 1574, + 1581, 1574, 1605, 1574, 1610, 1576, 1580, 1576, 1581, 1576, 1582, 1576, 1605, 1576, + 1609, 1576, 1610, 1578, 1580, 1578, 1581, 1578, 1582, 1578, 1605, 1578, 1609, 1578, + 1610, 1579, 1580, 1579, 1605, 1579, 1609, 1579, 1610, 1580, 1581, 1580, 1605, 1581, + 1605, 1582, 1580, 1582, 1581, 1582, 1605, 1587, 1580, 1587, 1581, 1587, 1582, 1587, + 1605, 1589, 1581, 1589, 1605, 1590, 1580, 1590, 1581, 1590, 1582, 1590, 1605, 1591, + 1581, 1591, 1605, 1592, 1605, 1593, 1580, 1593, 1605, 1594, 1580, 1594, 1605, 1601, + 1580, 1601, 1581, 1601, 1582, 1601, 1605, 1601, 1609, 1601, 1610, 1602, 1581, 1602, + 1605, 1602, 1609, 1602, 1610, 1603, 1575, 1603, 1580, 1603, 1581, 1603, 1582, 1603, + 1604, 1603, 1605, 1603, 1609, 1603, 1610, 1604, 1580, 1604, 1581, 1604, 1582, 1604, + 1605, 1604, 1609, 1604, 1610, 1605, 1580, 1605, 1605, 1605, 1609, 1605, 1610, 1606, + 1580, 1606, 1581, 1606, 1582, 1606, 1605, 1606, 1609, 1606, 1610, 1607, 1580, 1607, + 1605, 1607, 1609, 1607, 1610, 1610, 1581, 1610, 1582, 1610, 1609, 1584, 1648, 1585, + 1648, 1609, 1648, 32, 1612, 1617, 32, 1613, 1617, 32, 1614, 1617, 32, 1615, 1617, + 32, 1616, 1617, 32, 1617, 1648, 1574, 1585, 1574, 1586, 1574, 1606, 1576, 1585, + 1576, 1586, 1576, 1606, 1578, 1585, 1578, 1586, 1578, 1606, 1579, 1585, 1579, 1586, + 1579, 1606, 1605, 1575, 1606, 1585, 1606, 1586, 1606, 1606, 1610, 1585, 1610, 1586, + 1574, 1582, 1574, 1607, 1576, 1607, 1578, 1607, 1589, 1582, 1604, 1607, 1606, 1607, + 1607, 1648, 1579, 1607, 1587, 1607, 1588, 1605, 1588, 1607, 1600, 1614, 1617, 1600, + 1615, 1617, 1600, 1616, 1617, 1591, 1609, 1591, 1610, 1593, 1609, 1593, 1610, 1594, + 1609, 1594, 1610, 1587, 1609, 1587, 1610, 1588, 1609, 1588, 1610, 1581, 1609, 1580, + 1609, 1580, 1610, 1582, 1609, 1589, 1609, 1589, 1610, 1590, 1609, 1590, 1610, 1588, + 1580, 1588, 1581, 1588, 1582, 1588, 1585, 1587, 1585, 1589, 1585, 1590, 1585, 1575, + 1611, 1578, 1580, 1605, 1578, 1581, 1580, 1578, 1581, 1605, 1578, 1582, 1605, 1578, + 1605, 1580, 1578, 1605, 1581, 1578, 1605, 1582, 1581, 1605, 1610, 1581, 1605, 1609, + 1587, 1581, 1580, 1587, 1580, 1581, 1587, 1580, 1609, 1587, 1605, 1581, 1587, 1605, + 1580, 1587, 1605, 1605, 1589, 1581, 1581, 1589, 1605, 1605, 1588, 1581, 1605, 1588, + 1580, 1610, 1588, 1605, 1582, 1588, 1605, 1605, 1590, 1581, 1609, 1590, 1582, 1605, + 1591, 1605, 1581, 1591, 1605, 1605, 1591, 1605, 1610, 1593, 1580, 1605, 1593, 1605, + 1605, 1593, 1605, 1609, 1594, 1605, 1605, 1594, 1605, 1610, 1594, 1605, 1609, 1601, + 1582, 1605, 1602, 1605, 1581, 1602, 1605, 1605, 1604, 1581, 1605, 1604, 1581, 1610, + 1604, 1581, 1609, 1604, 1580, 1580, 1604, 1582, 1605, 1604, 1605, 1581, 1605, 1581, + 1580, 1605, 1581, 1610, 1605, 1580, 1581, 1605, 1582, 1605, 1605, 1580, 1582, 1607, + 1605, 1580, 1607, 1605, 1605, 1606, 1581, 1605, 1606, 1581, 1609, 1606, 1580, 1605, + 1606, 1580, 1609, 1606, 1605, 1610, 1606, 1605, 1609, 1610, 1605, 1605, 1576, 1582, + 1610, 1578, 1580, 1610, 1578, 1580, 1609, 1578, 1582, 1610, 1578, 1582, 1609, 1578, + 1605, 1610, 1578, 1605, 1609, 1580, 1605, 1610, 1580, 1581, 1609, 1580, 1605, 1609, + 1587, 1582, 1609, 1589, 1581, 1610, 1588, 1581, 1610, 1590, 1581, 1610, 1604, 1580, + 1610, 1604, 1605, 1610, 1610, 1580, 1610, 1610, 1605, 1610, 1605, 1605, 1610, 1602, + 1605, 1610, 1606, 1581, 1610, 1593, 1605, 1610, 1603, 1605, 1610, 1606, 1580, 1581, + 1605, 1582, 1610, 1604, 1580, 1605, 1603, 1605, 1605, 1580, 1581, 1610, 1581, 1580, + 1610, 1605, 1580, 1610, 1601, 1605, 1610, 1576, 1581, 1610, 1587, 1582, 1610, 1606, + 1580, 1610, 1589, 1604, 1746, 1602, 1604, 1746, 1575, 1604, 1604, 1607, 1575, 1603, + 1576, 1585, 1605, 1581, 1605, 1583, 1589, 1604, 1593, 1605, 1585, 1587, 1608, 1604, + 1593, 1604, 1610, 1607, 1608, 1587, 1604, 1605, 1589, 1604, 1609, 1589, 1604, 1609, + 32, 1575, 1604, 1604, 1607, 32, 1593, 1604, 1610, 1607, 32, 1608, 1587, 1604, 1605, + 1580, 1604, 32, 1580, 1604, 1575, 1604, 1607, 1585, 1740, 1575, 1604, 44, 12289, + 12310, 12311, 8212, 8211, 95, 123, 125, 12308, 12309, 12304, 12305, 12298, 12299, + 12300, 12301, 12302, 12303, 91, 93, 35, 38, 42, 45, 60, 62, 92, 36, 37, 64, 32, + 1611, 1600, 1611, 1600, 1617, 32, 1618, 1600, 1618, 1569, 1570, 1571, 1572, 1573, + 1577, 1604, 1570, 1604, 1571, 1604, 1573, 34, 39, 94, 124, 126, 10629, 10630, 12539, + 12453, 12515, 162, 163, 172, 166, 165, 8361, 9474, 8592, 8593, 8594, 8595, 9632, + 9675, 66600, 66601, 66602, 66603, 66604, 66605, 66606, 66607, 66608, 66609, 66610, + 66611, 66612, 66613, 66614, 66615, 66616, 66617, 66618, 66619, 66620, 66621, 66622, + 66623, 66624, 66625, 66626, 66627, 66628, 66629, 66630, 66631, 66632, 66633, 66634, + 66635, 66636, 66637, 66638, 66639, 66776, 66777, 66778, 66779, 66780, 66781, 66782, + 66783, 66784, 66785, 66786, 66787, 66788, 66789, 66790, 66791, 66792, 66793, 66794, + 66795, 66796, 66797, 66798, 66799, 66800, 66801, 66802, 66803, 66804, 66805, 66806, + 66807, 66808, 66809, 66810, 66811, 66967, 66968, 66969, 66970, 66971, 66972, 66973, + 66974, 66975, 66976, 66977, 66979, 66980, 66981, 66982, 66983, 66984, 66985, 66986, + 66987, 66988, 66989, 66990, 66991, 66992, 66993, 66995, 66996, 66997, 66998, 66999, + 67000, 67001, 67003, 67004, 720, 721, 665, 675, 43878, 677, 676, 7569, 600, 606, + 681, 610, 667, 668, 615, 644, 682, 683, 122628, 42894, 622, 122629, 654, 122630, + 630, 631, 634, 122632, 638, 680, 678, 43879, 679, 11377, 655, 673, 674, 664, 448, + 449, 450, 122634, 122654, 68800, 68801, 68802, 68803, 68804, 68805, 68806, 68807, + 68808, 68809, 68810, 68811, 68812, 68813, 68814, 68815, 68816, 68817, 68818, 68819, + 68820, 68821, 68822, 68823, 68824, 68825, 68826, 68827, 68828, 68829, 68830, 68831, + 68832, 68833, 68834, 68835, 68836, 68837, 68838, 68839, 68840, 68841, 68842, 68843, + 68844, 68845, 68846, 68847, 68848, 68849, 68850, 68976, 68977, 68978, 68979, 68980, + 68981, 68982, 68983, 68984, 68985, 68986, 68987, 68988, 68989, 68990, 68991, 68992, + 68993, 68994, 68995, 68996, 68997, 71872, 71873, 71874, 71875, 71876, 71877, 71878, + 71879, 71880, 71881, 71882, 71883, 71884, 71885, 71886, 71887, 71888, 71889, 71890, + 71891, 71892, 71893, 71894, 71895, 71896, 71897, 71898, 71899, 71900, 71901, 71902, + 71903, 93792, 93793, 93794, 93795, 93796, 93797, 93798, 93799, 93800, 93801, 93802, + 93803, 93804, 93805, 93806, 93807, 93808, 93809, 93810, 93811, 93812, 93813, 93814, + 93815, 93816, 93817, 93818, 93819, 93820, 93821, 93822, 93823, 119127, 119141, 119128, + 119141, 119128, 119141, 119150, 119128, 119141, 119151, 119128, 119141, 119152, + 119128, 119141, 119153, 119128, 119141, 119154, 119225, 119141, 119226, 119141, + 119225, 119141, 119150, 119226, 119141, 119150, 119225, 119141, 119151, 119226, + 119141, 119151, 305, 567, 8711, 8706, 125218, 125219, 125220, 125221, 125222, 125223, + 125224, 125225, 125226, 125227, 125228, 125229, 125230, 125231, 125232, 125233, + 125234, 125235, 125236, 125237, 125238, 125239, 125240, 125241, 125242, 125243, + 125244, 125245, 125246, 125247, 125248, 125249, 125250, 125251, 1646, 1697, 1647, + 48, 44, 49, 44, 50, 44, 51, 44, 52, 44, 53, 44, 54, 44, 55, 44, 56, 44, 57, 44, + 12308, 115, 12309, 119, 122, 104, 118, 115, 100, 115, 115, 112, 112, 118, 119, 99, + 109, 114, 100, 106, 12411, 12363, 12467, 12467, 23383, 21452, 22810, 35299, 20132, + 26144, 28961, 21069, 24460, 20877, 26032, 21021, 32066, 36009, 22768, 21561, 28436, + 25237, 25429, 36938, 25351, 25171, 31105, 31354, 21512, 28288, 30003, 21106, 21942, + 37197, 12308, 26412, 12309, 12308, 19977, 12309, 12308, 20108, 12309, 12308, 23433, + 12309, 12308, 28857, 12309, 12308, 25171, 12309, 12308, 30423, 12309, 12308, 21213, + 12309, 12308, 25943, 12309, 24471, 21487, 20029, 20024, 20033, 131362, 20320, 20411, + 20482, 20602, 20633, 20687, 13470, 132666, 20820, 20836, 20855, 132380, 13497, 20839, + 132427, 20887, 20900, 20172, 20908, 168415, 20995, 13535, 21051, 21062, 21111, 13589, + 21253, 21254, 21321, 21338, 21363, 21373, 21375, 133676, 28784, 21450, 21471, 133987, + 21483, 21489, 21510, 21662, 21560, 21576, 21608, 21666, 21750, 21776, 21843, 21859, + 21892, 21931, 21939, 21954, 22294, 22295, 22097, 22132, 22766, 22478, 22516, 22541, + 22411, 22578, 22577, 22700, 136420, 22770, 22775, 22790, 22818, 22882, 136872, 136938, + 23020, 23067, 23079, 23000, 23142, 14062, 14076, 23304, 23358, 137672, 23491, 23512, + 23539, 138008, 23551, 23558, 24403, 14209, 23648, 23744, 23693, 138724, 23875, 138726, + 23918, 23915, 23932, 24033, 24034, 14383, 24061, 24104, 24125, 24169, 14434, 139651, + 14460, 24240, 24243, 24246, 172946, 140081, 33281, 24354, 14535, 144056, 156122, + 24418, 24427, 14563, 24474, 24525, 24535, 24569, 24705, 14650, 14620, 141012, 24775, + 24904, 24908, 24954, 25010, 24996, 25007, 25054, 25115, 25181, 25265, 25300, 25424, + 142092, 25405, 25340, 25448, 25475, 25572, 142321, 25634, 25541, 25513, 14894, 25705, + 25726, 25757, 25719, 14956, 25964, 143370, 26083, 26360, 26185, 15129, 15112, 15076, + 20882, 20885, 26368, 26268, 32941, 17369, 26401, 26462, 26451, 144323, 15177, 26618, + 26501, 26706, 144493, 26766, 26655, 26900, 26946, 27043, 27114, 27304, 145059, 27355, + 15384, 27425, 145575, 27476, 15438, 27506, 27551, 27579, 146061, 138507, 146170, + 27726, 146620, 27839, 27853, 27751, 27926, 27966, 28009, 28024, 28037, 146718, 27956, + 28207, 28270, 15667, 28359, 147153, 28153, 28526, 147294, 147342, 28614, 28729, + 28699, 15766, 28746, 28797, 28791, 28845, 132389, 28997, 148067, 29084, 148395, + 29224, 29264, 149000, 29312, 29333, 149301, 149524, 29562, 29579, 16044, 29605, + 16056, 29767, 29788, 29829, 29898, 16155, 29988, 150582, 30014, 150674, 139679, + 30224, 151457, 151480, 151620, 16380, 16392, 151795, 151794, 151833, 151859, 30494, + 30495, 30603, 16454, 16534, 152605, 30798, 16611, 153126, 153242, 153285, 31211, + 16687, 31306, 31311, 153980, 154279, 31470, 16898, 154539, 31686, 31689, 16935, + 154752, 31954, 17056, 31976, 31971, 32000, 155526, 32099, 17153, 32199, 32258, 32325, + 17204, 156200, 156231, 17241, 156377, 32634, 156478, 32661, 32762, 156890, 156963, + 32864, 157096, 32880, 144223, 17365, 32946, 33027, 17419, 33086, 23221, 157607, + 157621, 144275, 144284, 33284, 36766, 17515, 33425, 33419, 33437, 21171, 33457, + 33459, 33469, 33510, 158524, 33565, 33635, 33709, 33571, 33725, 33767, 33619, 33738, + 33740, 33756, 158774, 159083, 158933, 17707, 34033, 34035, 34070, 160714, 34148, + 159532, 17757, 17761, 159665, 159954, 17771, 34384, 34407, 34409, 34473, 34440, + 34574, 34530, 34600, 34667, 34694, 17879, 34785, 34817, 17913, 34912, 161383, 35031, + 35038, 17973, 35066, 13499, 161966, 162150, 18110, 18119, 35488, 162984, 36011, + 36033, 36123, 36215, 163631, 133124, 36299, 36284, 36336, 133342, 36564, 165330, + 165357, 37012, 37105, 37137, 165678, 37147, 37432, 37591, 37592, 37500, 37881, 37909, + 166906, 38283, 18837, 38327, 167287, 18918, 38595, 23986, 38691, 168261, 168474, + 19054, 19062, 38880, 168970, 19122, 169110, 38953, 169398, 39138, 19251, 39209, + 39335, 39362, 39422, 19406, 170800, 40000, 40189, 19662, 19693, 40295, 172238, 19704, + 172293, 172558, 172689, 19798, 40702, 40709, 40719, 40726, 173568, }; -const uint32_t table[8000][2] = +const uint32_t table[8150][2] = { {0, 1}, {65, 16777219}, {66, 16777475}, {67, 16777731}, {68, 16777987}, {69, 16778243}, {70, 16778499}, {71, 16778755}, @@ -884,73 +891,73 @@ const uint32_t table[8000][2] = {1204, 16884483}, {1205, 1}, {1206, 16884739}, {1207, 1}, {1208, 16884995}, {1209, 1}, {1210, 16885251}, {1211, 1}, {1212, 16885507}, {1213, 1}, {1214, 16885763}, {1215, 1}, - {1216, 2}, {1217, 16886019}, {1218, 1}, {1219, 16886275}, - {1220, 1}, {1221, 16886531}, {1222, 1}, {1223, 16886787}, - {1224, 1}, {1225, 16887043}, {1226, 1}, {1227, 16887299}, - {1228, 1}, {1229, 16887555}, {1230, 1}, {1232, 16887811}, - {1233, 1}, {1234, 16888067}, {1235, 1}, {1236, 16888323}, - {1237, 1}, {1238, 16888579}, {1239, 1}, {1240, 16888835}, - {1241, 1}, {1242, 16889091}, {1243, 1}, {1244, 16889347}, - {1245, 1}, {1246, 16889603}, {1247, 1}, {1248, 16889859}, - {1249, 1}, {1250, 16890115}, {1251, 1}, {1252, 16890371}, - {1253, 1}, {1254, 16890627}, {1255, 1}, {1256, 16890883}, - {1257, 1}, {1258, 16891139}, {1259, 1}, {1260, 16891395}, - {1261, 1}, {1262, 16891651}, {1263, 1}, {1264, 16891907}, - {1265, 1}, {1266, 16892163}, {1267, 1}, {1268, 16892419}, - {1269, 1}, {1270, 16892675}, {1271, 1}, {1272, 16892931}, - {1273, 1}, {1274, 16893187}, {1275, 1}, {1276, 16893443}, - {1277, 1}, {1278, 16893699}, {1279, 1}, {1280, 16893955}, - {1281, 1}, {1282, 16894211}, {1283, 1}, {1284, 16894467}, - {1285, 1}, {1286, 16894723}, {1287, 1}, {1288, 16894979}, - {1289, 1}, {1290, 16895235}, {1291, 1}, {1292, 16895491}, - {1293, 1}, {1294, 16895747}, {1295, 1}, {1296, 16896003}, - {1297, 1}, {1298, 16896259}, {1299, 1}, {1300, 16896515}, - {1301, 1}, {1302, 16896771}, {1303, 1}, {1304, 16897027}, - {1305, 1}, {1306, 16897283}, {1307, 1}, {1308, 16897539}, - {1309, 1}, {1310, 16897795}, {1311, 1}, {1312, 16898051}, - {1313, 1}, {1314, 16898307}, {1315, 1}, {1316, 16898563}, - {1317, 1}, {1318, 16898819}, {1319, 1}, {1320, 16899075}, - {1321, 1}, {1322, 16899331}, {1323, 1}, {1324, 16899587}, - {1325, 1}, {1326, 16899843}, {1327, 1}, {1328, 2}, - {1329, 16900099}, {1330, 16900355}, {1331, 16900611}, {1332, 16900867}, - {1333, 16901123}, {1334, 16901379}, {1335, 16901635}, {1336, 16901891}, - {1337, 16902147}, {1338, 16902403}, {1339, 16902659}, {1340, 16902915}, - {1341, 16903171}, {1342, 16903427}, {1343, 16903683}, {1344, 16903939}, - {1345, 16904195}, {1346, 16904451}, {1347, 16904707}, {1348, 16904963}, - {1349, 16905219}, {1350, 16905475}, {1351, 16905731}, {1352, 16905987}, - {1353, 16906243}, {1354, 16906499}, {1355, 16906755}, {1356, 16907011}, - {1357, 16907267}, {1358, 16907523}, {1359, 16907779}, {1360, 16908035}, - {1361, 16908291}, {1362, 16908547}, {1363, 16908803}, {1364, 16909059}, - {1365, 16909315}, {1366, 16909571}, {1367, 2}, {1369, 1}, - {1415, 33687043}, {1416, 1}, {1419, 2}, {1421, 1}, + {1216, 16886019}, {1217, 16886275}, {1218, 1}, {1219, 16886531}, + {1220, 1}, {1221, 16886787}, {1222, 1}, {1223, 16887043}, + {1224, 1}, {1225, 16887299}, {1226, 1}, {1227, 16887555}, + {1228, 1}, {1229, 16887811}, {1230, 1}, {1232, 16888067}, + {1233, 1}, {1234, 16888323}, {1235, 1}, {1236, 16888579}, + {1237, 1}, {1238, 16888835}, {1239, 1}, {1240, 16889091}, + {1241, 1}, {1242, 16889347}, {1243, 1}, {1244, 16889603}, + {1245, 1}, {1246, 16889859}, {1247, 1}, {1248, 16890115}, + {1249, 1}, {1250, 16890371}, {1251, 1}, {1252, 16890627}, + {1253, 1}, {1254, 16890883}, {1255, 1}, {1256, 16891139}, + {1257, 1}, {1258, 16891395}, {1259, 1}, {1260, 16891651}, + {1261, 1}, {1262, 16891907}, {1263, 1}, {1264, 16892163}, + {1265, 1}, {1266, 16892419}, {1267, 1}, {1268, 16892675}, + {1269, 1}, {1270, 16892931}, {1271, 1}, {1272, 16893187}, + {1273, 1}, {1274, 16893443}, {1275, 1}, {1276, 16893699}, + {1277, 1}, {1278, 16893955}, {1279, 1}, {1280, 16894211}, + {1281, 1}, {1282, 16894467}, {1283, 1}, {1284, 16894723}, + {1285, 1}, {1286, 16894979}, {1287, 1}, {1288, 16895235}, + {1289, 1}, {1290, 16895491}, {1291, 1}, {1292, 16895747}, + {1293, 1}, {1294, 16896003}, {1295, 1}, {1296, 16896259}, + {1297, 1}, {1298, 16896515}, {1299, 1}, {1300, 16896771}, + {1301, 1}, {1302, 16897027}, {1303, 1}, {1304, 16897283}, + {1305, 1}, {1306, 16897539}, {1307, 1}, {1308, 16897795}, + {1309, 1}, {1310, 16898051}, {1311, 1}, {1312, 16898307}, + {1313, 1}, {1314, 16898563}, {1315, 1}, {1316, 16898819}, + {1317, 1}, {1318, 16899075}, {1319, 1}, {1320, 16899331}, + {1321, 1}, {1322, 16899587}, {1323, 1}, {1324, 16899843}, + {1325, 1}, {1326, 16900099}, {1327, 1}, {1328, 2}, + {1329, 16900355}, {1330, 16900611}, {1331, 16900867}, {1332, 16901123}, + {1333, 16901379}, {1334, 16901635}, {1335, 16901891}, {1336, 16902147}, + {1337, 16902403}, {1338, 16902659}, {1339, 16902915}, {1340, 16903171}, + {1341, 16903427}, {1342, 16903683}, {1343, 16903939}, {1344, 16904195}, + {1345, 16904451}, {1346, 16904707}, {1347, 16904963}, {1348, 16905219}, + {1349, 16905475}, {1350, 16905731}, {1351, 16905987}, {1352, 16906243}, + {1353, 16906499}, {1354, 16906755}, {1355, 16907011}, {1356, 16907267}, + {1357, 16907523}, {1358, 16907779}, {1359, 16908035}, {1360, 16908291}, + {1361, 16908547}, {1362, 16908803}, {1363, 16909059}, {1364, 16909315}, + {1365, 16909571}, {1366, 16909827}, {1367, 2}, {1369, 1}, + {1415, 33687299}, {1416, 1}, {1419, 2}, {1421, 1}, {1424, 2}, {1425, 1}, {1480, 2}, {1488, 1}, {1515, 2}, {1519, 1}, {1525, 2}, {1542, 1}, - {1564, 2}, {1565, 1}, {1653, 33687555}, {1654, 33688067}, - {1655, 33688579}, {1656, 33689091}, {1657, 1}, {1757, 2}, + {1564, 2}, {1565, 1}, {1653, 33687811}, {1654, 33688323}, + {1655, 33688835}, {1656, 33689347}, {1657, 1}, {1757, 2}, {1758, 1}, {1806, 2}, {1808, 1}, {1867, 2}, {1869, 1}, {1970, 2}, {1984, 1}, {2043, 2}, {2045, 1}, {2094, 2}, {2096, 1}, {2111, 2}, {2112, 1}, {2140, 2}, {2142, 1}, {2143, 2}, {2144, 1}, {2155, 2}, {2160, 1}, {2191, 2}, - {2200, 1}, {2274, 2}, {2275, 1}, {2392, 33689603}, - {2393, 33690115}, {2394, 33690627}, {2395, 33691139}, {2396, 33691651}, - {2397, 33692163}, {2398, 33692675}, {2399, 33693187}, {2400, 1}, + {2199, 1}, {2274, 2}, {2275, 1}, {2392, 33689859}, + {2393, 33690371}, {2394, 33690883}, {2395, 33691395}, {2396, 33691907}, + {2397, 33692419}, {2398, 33692931}, {2399, 33693443}, {2400, 1}, {2436, 2}, {2437, 1}, {2445, 2}, {2447, 1}, {2449, 2}, {2451, 1}, {2473, 2}, {2474, 1}, {2481, 2}, {2482, 1}, {2483, 2}, {2486, 1}, {2490, 2}, {2492, 1}, {2501, 2}, {2503, 1}, {2505, 2}, {2507, 1}, {2511, 2}, {2519, 1}, - {2520, 2}, {2524, 33693699}, {2525, 33694211}, {2526, 2}, - {2527, 33694723}, {2528, 1}, {2532, 2}, {2534, 1}, + {2520, 2}, {2524, 33693955}, {2525, 33694467}, {2526, 2}, + {2527, 33694979}, {2528, 1}, {2532, 2}, {2534, 1}, {2559, 2}, {2561, 1}, {2564, 2}, {2565, 1}, {2571, 2}, {2575, 1}, {2577, 2}, {2579, 1}, {2601, 2}, {2602, 1}, {2609, 2}, {2610, 1}, - {2611, 33695235}, {2612, 2}, {2613, 1}, {2614, 33695747}, + {2611, 33695491}, {2612, 2}, {2613, 1}, {2614, 33696003}, {2615, 2}, {2616, 1}, {2618, 2}, {2620, 1}, {2621, 2}, {2622, 1}, {2627, 2}, {2631, 1}, {2633, 2}, {2635, 1}, {2638, 2}, {2641, 1}, - {2642, 2}, {2649, 33696259}, {2650, 33696771}, {2651, 33697283}, - {2652, 1}, {2653, 2}, {2654, 33697795}, {2655, 2}, + {2642, 2}, {2649, 33696515}, {2650, 33697027}, {2651, 33697539}, + {2652, 1}, {2653, 2}, {2654, 33698051}, {2655, 2}, {2662, 1}, {2679, 2}, {2689, 1}, {2692, 2}, {2693, 1}, {2702, 2}, {2703, 1}, {2706, 2}, {2707, 1}, {2729, 2}, {2730, 1}, {2737, 2}, @@ -964,7 +971,7 @@ const uint32_t table[8000][2] = {2866, 1}, {2868, 2}, {2869, 1}, {2874, 2}, {2876, 1}, {2885, 2}, {2887, 1}, {2889, 2}, {2891, 1}, {2894, 2}, {2901, 1}, {2904, 2}, - {2908, 33698307}, {2909, 33698819}, {2910, 2}, {2911, 1}, + {2908, 33698563}, {2909, 33699075}, {2910, 2}, {2911, 1}, {2916, 2}, {2918, 1}, {2936, 2}, {2946, 1}, {2948, 2}, {2949, 1}, {2955, 2}, {2958, 1}, {2961, 2}, {2962, 1}, {2966, 2}, {2969, 1}, @@ -996,1735 +1003,1772 @@ const uint32_t table[8000][2] = {3531, 2}, {3535, 1}, {3541, 2}, {3542, 1}, {3543, 2}, {3544, 1}, {3552, 2}, {3558, 1}, {3568, 2}, {3570, 1}, {3573, 2}, {3585, 1}, - {3635, 33699331}, {3636, 1}, {3643, 2}, {3647, 1}, + {3635, 33699587}, {3636, 1}, {3643, 2}, {3647, 1}, {3676, 2}, {3713, 1}, {3715, 2}, {3716, 1}, {3717, 2}, {3718, 1}, {3723, 2}, {3724, 1}, {3748, 2}, {3749, 1}, {3750, 2}, {3751, 1}, - {3763, 33699843}, {3764, 1}, {3774, 2}, {3776, 1}, + {3763, 33700099}, {3764, 1}, {3774, 2}, {3776, 1}, {3781, 2}, {3782, 1}, {3783, 2}, {3784, 1}, - {3791, 2}, {3792, 1}, {3802, 2}, {3804, 33700355}, - {3805, 33700867}, {3806, 1}, {3808, 2}, {3840, 1}, - {3852, 16924163}, {3853, 1}, {3907, 33701635}, {3908, 1}, - {3912, 2}, {3913, 1}, {3917, 33702147}, {3918, 1}, - {3922, 33702659}, {3923, 1}, {3927, 33703171}, {3928, 1}, - {3932, 33703683}, {3933, 1}, {3945, 33704195}, {3946, 1}, - {3949, 2}, {3953, 1}, {3955, 33704707}, {3956, 1}, - {3957, 33705219}, {3958, 33705731}, {3959, 50483459}, {3960, 33707011}, - {3961, 50484739}, {3962, 1}, {3969, 33706499}, {3970, 1}, - {3987, 33708291}, {3988, 1}, {3992, 2}, {3993, 1}, - {3997, 33708803}, {3998, 1}, {4002, 33709315}, {4003, 1}, - {4007, 33709827}, {4008, 1}, {4012, 33710339}, {4013, 1}, - {4025, 33710851}, {4026, 1}, {4029, 2}, {4030, 1}, + {3791, 2}, {3792, 1}, {3802, 2}, {3804, 33700611}, + {3805, 33701123}, {3806, 1}, {3808, 2}, {3840, 1}, + {3852, 16924419}, {3853, 1}, {3907, 33701891}, {3908, 1}, + {3912, 2}, {3913, 1}, {3917, 33702403}, {3918, 1}, + {3922, 33702915}, {3923, 1}, {3927, 33703427}, {3928, 1}, + {3932, 33703939}, {3933, 1}, {3945, 33704451}, {3946, 1}, + {3949, 2}, {3953, 1}, {3955, 33704963}, {3956, 1}, + {3957, 33705475}, {3958, 33705987}, {3959, 50483715}, {3960, 33707267}, + {3961, 50484995}, {3962, 1}, {3969, 33706755}, {3970, 1}, + {3987, 33708547}, {3988, 1}, {3992, 2}, {3993, 1}, + {3997, 33709059}, {3998, 1}, {4002, 33709571}, {4003, 1}, + {4007, 33710083}, {4008, 1}, {4012, 33710595}, {4013, 1}, + {4025, 33711107}, {4026, 1}, {4029, 2}, {4030, 1}, {4045, 2}, {4046, 1}, {4059, 2}, {4096, 1}, - {4256, 2}, {4295, 16934147}, {4296, 2}, {4301, 16934403}, - {4302, 2}, {4304, 1}, {4348, 16934659}, {4349, 1}, - {4447, 2}, {4449, 1}, {4681, 2}, {4682, 1}, - {4686, 2}, {4688, 1}, {4695, 2}, {4696, 1}, - {4697, 2}, {4698, 1}, {4702, 2}, {4704, 1}, - {4745, 2}, {4746, 1}, {4750, 2}, {4752, 1}, - {4785, 2}, {4786, 1}, {4790, 2}, {4792, 1}, - {4799, 2}, {4800, 1}, {4801, 2}, {4802, 1}, - {4806, 2}, {4808, 1}, {4823, 2}, {4824, 1}, - {4881, 2}, {4882, 1}, {4886, 2}, {4888, 1}, - {4955, 2}, {4957, 1}, {4989, 2}, {4992, 1}, - {5018, 2}, {5024, 1}, {5110, 2}, {5112, 16934915}, - {5113, 16935171}, {5114, 16935427}, {5115, 16935683}, {5116, 16935939}, - {5117, 16936195}, {5118, 2}, {5120, 1}, {5760, 2}, - {5761, 1}, {5789, 2}, {5792, 1}, {5881, 2}, - {5888, 1}, {5910, 2}, {5919, 1}, {5943, 2}, - {5952, 1}, {5972, 2}, {5984, 1}, {5997, 2}, - {5998, 1}, {6001, 2}, {6002, 1}, {6004, 2}, - {6016, 1}, {6068, 2}, {6070, 1}, {6110, 2}, - {6112, 1}, {6122, 2}, {6128, 1}, {6138, 2}, - {6144, 1}, {6150, 2}, {6151, 1}, {6155, 0}, - {6158, 2}, {6159, 0}, {6160, 1}, {6170, 2}, - {6176, 1}, {6265, 2}, {6272, 1}, {6315, 2}, - {6320, 1}, {6390, 2}, {6400, 1}, {6431, 2}, - {6432, 1}, {6444, 2}, {6448, 1}, {6460, 2}, - {6464, 1}, {6465, 2}, {6468, 1}, {6510, 2}, - {6512, 1}, {6517, 2}, {6528, 1}, {6572, 2}, - {6576, 1}, {6602, 2}, {6608, 1}, {6619, 2}, - {6622, 1}, {6684, 2}, {6686, 1}, {6751, 2}, - {6752, 1}, {6781, 2}, {6783, 1}, {6794, 2}, - {6800, 1}, {6810, 2}, {6816, 1}, {6830, 2}, - {6832, 1}, {6863, 2}, {6912, 1}, {6989, 2}, - {6992, 1}, {7039, 2}, {7040, 1}, {7156, 2}, + {4256, 16934403}, {4257, 16934659}, {4258, 16934915}, {4259, 16935171}, + {4260, 16935427}, {4261, 16935683}, {4262, 16935939}, {4263, 16936195}, + {4264, 16936451}, {4265, 16936707}, {4266, 16936963}, {4267, 16937219}, + {4268, 16937475}, {4269, 16937731}, {4270, 16937987}, {4271, 16938243}, + {4272, 16938499}, {4273, 16938755}, {4274, 16939011}, {4275, 16939267}, + {4276, 16939523}, {4277, 16939779}, {4278, 16940035}, {4279, 16940291}, + {4280, 16940547}, {4281, 16940803}, {4282, 16941059}, {4283, 16941315}, + {4284, 16941571}, {4285, 16941827}, {4286, 16942083}, {4287, 16942339}, + {4288, 16942595}, {4289, 16942851}, {4290, 16943107}, {4291, 16943363}, + {4292, 16943619}, {4293, 16943875}, {4294, 2}, {4295, 16944131}, + {4296, 2}, {4301, 16944387}, {4302, 2}, {4304, 1}, + {4348, 16944643}, {4349, 1}, {4447, 0}, {4449, 1}, + {4681, 2}, {4682, 1}, {4686, 2}, {4688, 1}, + {4695, 2}, {4696, 1}, {4697, 2}, {4698, 1}, + {4702, 2}, {4704, 1}, {4745, 2}, {4746, 1}, + {4750, 2}, {4752, 1}, {4785, 2}, {4786, 1}, + {4790, 2}, {4792, 1}, {4799, 2}, {4800, 1}, + {4801, 2}, {4802, 1}, {4806, 2}, {4808, 1}, + {4823, 2}, {4824, 1}, {4881, 2}, {4882, 1}, + {4886, 2}, {4888, 1}, {4955, 2}, {4957, 1}, + {4989, 2}, {4992, 1}, {5018, 2}, {5024, 1}, + {5110, 2}, {5112, 16944899}, {5113, 16945155}, {5114, 16945411}, + {5115, 16945667}, {5116, 16945923}, {5117, 16946179}, {5118, 2}, + {5120, 1}, {5760, 2}, {5761, 1}, {5789, 2}, + {5792, 1}, {5881, 2}, {5888, 1}, {5910, 2}, + {5919, 1}, {5943, 2}, {5952, 1}, {5972, 2}, + {5984, 1}, {5997, 2}, {5998, 1}, {6001, 2}, + {6002, 1}, {6004, 2}, {6016, 1}, {6068, 0}, + {6070, 1}, {6110, 2}, {6112, 1}, {6122, 2}, + {6128, 1}, {6138, 2}, {6144, 1}, {6155, 0}, + {6160, 1}, {6170, 2}, {6176, 1}, {6265, 2}, + {6272, 1}, {6315, 2}, {6320, 1}, {6390, 2}, + {6400, 1}, {6431, 2}, {6432, 1}, {6444, 2}, + {6448, 1}, {6460, 2}, {6464, 1}, {6465, 2}, + {6468, 1}, {6510, 2}, {6512, 1}, {6517, 2}, + {6528, 1}, {6572, 2}, {6576, 1}, {6602, 2}, + {6608, 1}, {6619, 2}, {6622, 1}, {6684, 2}, + {6686, 1}, {6751, 2}, {6752, 1}, {6781, 2}, + {6783, 1}, {6794, 2}, {6800, 1}, {6810, 2}, + {6816, 1}, {6830, 2}, {6832, 1}, {6863, 2}, + {6912, 1}, {6989, 2}, {6990, 1}, {7156, 2}, {7164, 1}, {7224, 2}, {7227, 1}, {7242, 2}, {7245, 1}, {7296, 16867075}, {7297, 16867587}, {7298, 16870147}, {7299, 16870915}, {7300, 16871171}, {7302, 16873219}, {7303, 16875011}, - {7304, 16936451}, {7305, 2}, {7312, 16936707}, {7313, 16936963}, - {7314, 16937219}, {7315, 16937475}, {7316, 16937731}, {7317, 16937987}, - {7318, 16938243}, {7319, 16938499}, {7320, 16938755}, {7321, 16939011}, - {7322, 16939267}, {7323, 16939523}, {7324, 16934659}, {7325, 16939779}, - {7326, 16940035}, {7327, 16940291}, {7328, 16940547}, {7329, 16940803}, - {7330, 16941059}, {7331, 16941315}, {7332, 16941571}, {7333, 16941827}, - {7334, 16942083}, {7335, 16942339}, {7336, 16942595}, {7337, 16942851}, - {7338, 16943107}, {7339, 16943363}, {7340, 16943619}, {7341, 16943875}, - {7342, 16944131}, {7343, 16944387}, {7344, 16944643}, {7345, 16944899}, - {7346, 16945155}, {7347, 16945411}, {7348, 16945667}, {7349, 16945923}, - {7350, 16946179}, {7351, 16946435}, {7352, 16946691}, {7353, 16946947}, - {7354, 16947203}, {7355, 2}, {7357, 16947459}, {7358, 16947715}, - {7359, 16947971}, {7360, 1}, {7368, 2}, {7376, 1}, - {7419, 2}, {7424, 1}, {7468, 16777219}, {7469, 16791043}, - {7470, 16777475}, {7471, 1}, {7472, 16777987}, {7473, 16778243}, - {7474, 16816131}, {7475, 16778755}, {7476, 16779011}, {7477, 16779267}, - {7478, 16779523}, {7479, 16779779}, {7480, 16780035}, {7481, 16780291}, - {7482, 16780547}, {7483, 1}, {7484, 16780803}, {7485, 16835843}, - {7486, 16781059}, {7487, 16781571}, {7488, 16782083}, {7489, 16782339}, - {7490, 16782851}, {7491, 16777219}, {7492, 16948227}, {7493, 16948483}, - {7494, 16948739}, {7495, 16777475}, {7496, 16777987}, {7497, 16778243}, - {7498, 16816387}, {7499, 16816643}, {7500, 16948995}, {7501, 16778755}, - {7502, 1}, {7503, 16779779}, {7504, 16780291}, {7505, 16807171}, - {7506, 16780803}, {7507, 16814851}, {7508, 16949251}, {7509, 16949507}, - {7510, 16781059}, {7511, 16782083}, {7512, 16782339}, {7513, 16949763}, - {7514, 16818435}, {7515, 16782595}, {7516, 16950019}, {7517, 16851971}, - {7518, 16852227}, {7519, 16852483}, {7520, 16856323}, {7521, 16856579}, - {7522, 16779267}, {7523, 16781571}, {7524, 16782339}, {7525, 16782595}, - {7526, 16851971}, {7527, 16852227}, {7528, 16855299}, {7529, 16856323}, - {7530, 16856579}, {7531, 1}, {7544, 16869891}, {7545, 1}, - {7579, 16950275}, {7580, 16777731}, {7581, 16950531}, {7582, 16793603}, - {7583, 16948995}, {7584, 16778499}, {7585, 16950787}, {7586, 16951043}, - {7587, 16951299}, {7588, 16817923}, {7589, 16817667}, {7590, 16951555}, - {7591, 16951811}, {7592, 16952067}, {7593, 16952323}, {7594, 16952579}, - {7595, 16952835}, {7596, 16953091}, {7597, 16953347}, {7598, 16818691}, - {7599, 16953603}, {7600, 16953859}, {7601, 16818947}, {7602, 16954115}, - {7603, 16954371}, {7604, 16820483}, {7605, 16954627}, {7606, 16839683}, - {7607, 16821507}, {7608, 16954883}, {7609, 16821763}, {7610, 16839939}, - {7611, 16783619}, {7612, 16955139}, {7613, 16955395}, {7614, 16822531}, - {7615, 16853507}, {7616, 1}, {7680, 16955651}, {7681, 1}, - {7682, 16955907}, {7683, 1}, {7684, 16956163}, {7685, 1}, - {7686, 16956419}, {7687, 1}, {7688, 16956675}, {7689, 1}, - {7690, 16956931}, {7691, 1}, {7692, 16957187}, {7693, 1}, - {7694, 16957443}, {7695, 1}, {7696, 16957699}, {7697, 1}, - {7698, 16957955}, {7699, 1}, {7700, 16958211}, {7701, 1}, - {7702, 16958467}, {7703, 1}, {7704, 16958723}, {7705, 1}, - {7706, 16958979}, {7707, 1}, {7708, 16959235}, {7709, 1}, - {7710, 16959491}, {7711, 1}, {7712, 16959747}, {7713, 1}, - {7714, 16960003}, {7715, 1}, {7716, 16960259}, {7717, 1}, - {7718, 16960515}, {7719, 1}, {7720, 16960771}, {7721, 1}, - {7722, 16961027}, {7723, 1}, {7724, 16961283}, {7725, 1}, - {7726, 16961539}, {7727, 1}, {7728, 16961795}, {7729, 1}, - {7730, 16962051}, {7731, 1}, {7732, 16962307}, {7733, 1}, - {7734, 16962563}, {7735, 1}, {7736, 16962819}, {7737, 1}, - {7738, 16963075}, {7739, 1}, {7740, 16963331}, {7741, 1}, - {7742, 16963587}, {7743, 1}, {7744, 16963843}, {7745, 1}, - {7746, 16964099}, {7747, 1}, {7748, 16964355}, {7749, 1}, - {7750, 16964611}, {7751, 1}, {7752, 16964867}, {7753, 1}, - {7754, 16965123}, {7755, 1}, {7756, 16965379}, {7757, 1}, - {7758, 16965635}, {7759, 1}, {7760, 16965891}, {7761, 1}, - {7762, 16966147}, {7763, 1}, {7764, 16966403}, {7765, 1}, - {7766, 16966659}, {7767, 1}, {7768, 16966915}, {7769, 1}, - {7770, 16967171}, {7771, 1}, {7772, 16967427}, {7773, 1}, - {7774, 16967683}, {7775, 1}, {7776, 16967939}, {7777, 1}, - {7778, 16968195}, {7779, 1}, {7780, 16968451}, {7781, 1}, - {7782, 16968707}, {7783, 1}, {7784, 16968963}, {7785, 1}, - {7786, 16969219}, {7787, 1}, {7788, 16969475}, {7789, 1}, - {7790, 16969731}, {7791, 1}, {7792, 16969987}, {7793, 1}, - {7794, 16970243}, {7795, 1}, {7796, 16970499}, {7797, 1}, - {7798, 16970755}, {7799, 1}, {7800, 16971011}, {7801, 1}, - {7802, 16971267}, {7803, 1}, {7804, 16971523}, {7805, 1}, - {7806, 16971779}, {7807, 1}, {7808, 16972035}, {7809, 1}, - {7810, 16972291}, {7811, 1}, {7812, 16972547}, {7813, 1}, - {7814, 16972803}, {7815, 1}, {7816, 16973059}, {7817, 1}, - {7818, 16973315}, {7819, 1}, {7820, 16973571}, {7821, 1}, - {7822, 16973827}, {7823, 1}, {7824, 16974083}, {7825, 1}, - {7826, 16974339}, {7827, 1}, {7828, 16974595}, {7829, 1}, - {7834, 33752067}, {7835, 16967939}, {7836, 1}, {7838, 33752579}, - {7839, 1}, {7840, 16975875}, {7841, 1}, {7842, 16976131}, - {7843, 1}, {7844, 16976387}, {7845, 1}, {7846, 16976643}, - {7847, 1}, {7848, 16976899}, {7849, 1}, {7850, 16977155}, - {7851, 1}, {7852, 16977411}, {7853, 1}, {7854, 16977667}, - {7855, 1}, {7856, 16977923}, {7857, 1}, {7858, 16978179}, - {7859, 1}, {7860, 16978435}, {7861, 1}, {7862, 16978691}, - {7863, 1}, {7864, 16978947}, {7865, 1}, {7866, 16979203}, - {7867, 1}, {7868, 16979459}, {7869, 1}, {7870, 16979715}, - {7871, 1}, {7872, 16979971}, {7873, 1}, {7874, 16980227}, - {7875, 1}, {7876, 16980483}, {7877, 1}, {7878, 16980739}, - {7879, 1}, {7880, 16980995}, {7881, 1}, {7882, 16981251}, - {7883, 1}, {7884, 16981507}, {7885, 1}, {7886, 16981763}, - {7887, 1}, {7888, 16982019}, {7889, 1}, {7890, 16982275}, - {7891, 1}, {7892, 16982531}, {7893, 1}, {7894, 16982787}, - {7895, 1}, {7896, 16983043}, {7897, 1}, {7898, 16983299}, - {7899, 1}, {7900, 16983555}, {7901, 1}, {7902, 16983811}, - {7903, 1}, {7904, 16984067}, {7905, 1}, {7906, 16984323}, - {7907, 1}, {7908, 16984579}, {7909, 1}, {7910, 16984835}, - {7911, 1}, {7912, 16985091}, {7913, 1}, {7914, 16985347}, - {7915, 1}, {7916, 16985603}, {7917, 1}, {7918, 16985859}, - {7919, 1}, {7920, 16986115}, {7921, 1}, {7922, 16986371}, - {7923, 1}, {7924, 16986627}, {7925, 1}, {7926, 16986883}, - {7927, 1}, {7928, 16987139}, {7929, 1}, {7930, 16987395}, - {7931, 1}, {7932, 16987651}, {7933, 1}, {7934, 16987907}, - {7935, 1}, {7944, 16988163}, {7945, 16988419}, {7946, 16988675}, - {7947, 16988931}, {7948, 16989187}, {7949, 16989443}, {7950, 16989699}, - {7951, 16989955}, {7952, 1}, {7958, 2}, {7960, 16990211}, - {7961, 16990467}, {7962, 16990723}, {7963, 16990979}, {7964, 16991235}, - {7965, 16991491}, {7966, 2}, {7968, 1}, {7976, 16991747}, - {7977, 16992003}, {7978, 16992259}, {7979, 16992515}, {7980, 16992771}, - {7981, 16993027}, {7982, 16993283}, {7983, 16993539}, {7984, 1}, - {7992, 16993795}, {7993, 16994051}, {7994, 16994307}, {7995, 16994563}, - {7996, 16994819}, {7997, 16995075}, {7998, 16995331}, {7999, 16995587}, - {8000, 1}, {8006, 2}, {8008, 16995843}, {8009, 16996099}, - {8010, 16996355}, {8011, 16996611}, {8012, 16996867}, {8013, 16997123}, - {8014, 2}, {8016, 1}, {8024, 2}, {8025, 16997379}, - {8026, 2}, {8027, 16997635}, {8028, 2}, {8029, 16997891}, - {8030, 2}, {8031, 16998147}, {8032, 1}, {8040, 16998403}, - {8041, 16998659}, {8042, 16998915}, {8043, 16999171}, {8044, 16999427}, - {8045, 16999683}, {8046, 16999939}, {8047, 17000195}, {8048, 1}, - {8049, 16849923}, {8050, 1}, {8051, 16850179}, {8052, 1}, - {8053, 16850435}, {8054, 1}, {8055, 16850691}, {8056, 1}, - {8057, 16850947}, {8058, 1}, {8059, 16851203}, {8060, 1}, - {8061, 16851459}, {8062, 2}, {8064, 33777667}, {8065, 33778179}, - {8066, 33778691}, {8067, 33779203}, {8068, 33779715}, {8069, 33780227}, - {8070, 33780739}, {8071, 33781251}, {8072, 33777667}, {8073, 33778179}, - {8074, 33778691}, {8075, 33779203}, {8076, 33779715}, {8077, 33780227}, - {8078, 33780739}, {8079, 33781251}, {8080, 33781763}, {8081, 33782275}, - {8082, 33782787}, {8083, 33783299}, {8084, 33783811}, {8085, 33784323}, - {8086, 33784835}, {8087, 33785347}, {8088, 33781763}, {8089, 33782275}, - {8090, 33782787}, {8091, 33783299}, {8092, 33783811}, {8093, 33784323}, - {8094, 33784835}, {8095, 33785347}, {8096, 33785859}, {8097, 33786371}, - {8098, 33786883}, {8099, 33787395}, {8100, 33787907}, {8101, 33788419}, - {8102, 33788931}, {8103, 33789443}, {8104, 33785859}, {8105, 33786371}, - {8106, 33786883}, {8107, 33787395}, {8108, 33787907}, {8109, 33788419}, - {8110, 33788931}, {8111, 33789443}, {8112, 1}, {8114, 33789955}, - {8115, 33790467}, {8116, 33790979}, {8117, 2}, {8118, 1}, - {8119, 33791491}, {8120, 17014787}, {8121, 17015043}, {8122, 17012739}, - {8123, 16849923}, {8124, 33790467}, {8125, 33792515}, {8126, 16846851}, - {8127, 33792515}, {8128, 33793027}, {8129, 50570755}, {8130, 33794307}, - {8131, 33794819}, {8132, 33795331}, {8133, 2}, {8134, 1}, - {8135, 33795843}, {8136, 17019139}, {8137, 16850179}, {8138, 17017091}, - {8139, 16850435}, {8140, 33794819}, {8141, 50573827}, {8142, 50574595}, - {8143, 50575363}, {8144, 1}, {8147, 17021699}, {8148, 2}, - {8150, 1}, {8152, 17021955}, {8153, 17022211}, {8154, 17022467}, - {8155, 16850691}, {8156, 2}, {8157, 50577155}, {8158, 50577923}, - {8159, 50578691}, {8160, 1}, {8163, 17025027}, {8164, 1}, - {8168, 17025283}, {8169, 17025539}, {8170, 17025795}, {8171, 16851203}, - {8172, 17026051}, {8173, 50580739}, {8174, 50403587}, {8175, 17027075}, - {8176, 2}, {8178, 33804547}, {8179, 33805059}, {8180, 33805571}, - {8181, 2}, {8182, 1}, {8183, 33806083}, {8184, 17029379}, - {8185, 16850947}, {8186, 17027331}, {8187, 16851459}, {8188, 33805059}, - {8189, 33562883}, {8190, 33799939}, {8191, 2}, {8192, 16783875}, - {8203, 0}, {8204, 1}, {8206, 2}, {8208, 1}, - {8209, 17029635}, {8210, 1}, {8215, 33807107}, {8216, 1}, - {8228, 2}, {8231, 1}, {8232, 2}, {8239, 16783875}, - {8240, 1}, {8243, 33807619}, {8244, 50585347}, {8245, 1}, - {8246, 33808899}, {8247, 50586627}, {8248, 1}, {8252, 33810179}, - {8253, 1}, {8254, 33810691}, {8255, 1}, {8263, 33811203}, - {8264, 33811715}, {8265, 33812227}, {8266, 1}, {8279, 67362051}, - {8280, 1}, {8287, 16783875}, {8288, 0}, {8289, 2}, - {8292, 0}, {8293, 2}, {8304, 17035523}, {8305, 16779267}, - {8306, 2}, {8308, 16787715}, {8309, 17035779}, {8310, 17036035}, - {8311, 17036291}, {8312, 17036547}, {8313, 17036803}, {8314, 17037059}, - {8315, 17037315}, {8316, 17037571}, {8317, 17037827}, {8318, 17038083}, - {8319, 16780547}, {8320, 17035523}, {8321, 16786947}, {8322, 16785155}, - {8323, 16785411}, {8324, 16787715}, {8325, 17035779}, {8326, 17036035}, - {8327, 17036291}, {8328, 17036547}, {8329, 17036803}, {8330, 17037059}, - {8331, 17037315}, {8332, 17037571}, {8333, 17037827}, {8334, 17038083}, - {8335, 2}, {8336, 16777219}, {8337, 16778243}, {8338, 16780803}, - {8339, 16783107}, {8340, 16816387}, {8341, 16779011}, {8342, 16779779}, - {8343, 16780035}, {8344, 16780291}, {8345, 16780547}, {8346, 16781059}, - {8347, 16781827}, {8348, 16782083}, {8349, 2}, {8352, 1}, - {8360, 33558787}, {8361, 1}, {8385, 2}, {8400, 1}, - {8433, 2}, {8448, 50592771}, {8449, 50593539}, {8450, 16777731}, - {8451, 33817091}, {8452, 1}, {8453, 50594819}, {8454, 50595587}, - {8455, 16816643}, {8456, 1}, {8457, 33819139}, {8458, 16778755}, - {8459, 16779011}, {8463, 16802051}, {8464, 16779267}, {8466, 16780035}, - {8468, 1}, {8469, 16780547}, {8470, 33557763}, {8471, 1}, - {8473, 16781059}, {8474, 16781315}, {8475, 16781571}, {8478, 1}, - {8480, 33819651}, {8481, 50597379}, {8482, 33820931}, {8483, 1}, - {8484, 16783619}, {8485, 1}, {8486, 16857091}, {8487, 1}, - {8488, 16783619}, {8489, 1}, {8490, 16779779}, {8491, 16790787}, - {8492, 16777475}, {8493, 16777731}, {8494, 1}, {8495, 16778243}, - {8497, 16778499}, {8498, 2}, {8499, 16780291}, {8500, 16780803}, - {8501, 17044227}, {8502, 17044483}, {8503, 17044739}, {8504, 17044995}, - {8505, 16779267}, {8506, 1}, {8507, 50599683}, {8508, 16855043}, - {8509, 16852227}, {8511, 16855043}, {8512, 17046019}, {8513, 1}, - {8517, 16777987}, {8519, 16778243}, {8520, 16779267}, {8521, 16779523}, - {8522, 1}, {8528, 50600707}, {8529, 50601475}, {8530, 67379459}, - {8531, 50603267}, {8532, 50604035}, {8533, 50604803}, {8534, 50605571}, - {8535, 50606339}, {8536, 50607107}, {8537, 50607875}, {8538, 50608643}, - {8539, 50609411}, {8540, 50610179}, {8541, 50610947}, {8542, 50611715}, - {8543, 33564419}, {8544, 16779267}, {8545, 33835267}, {8546, 50612995}, - {8547, 33836547}, {8548, 16782595}, {8549, 33837059}, {8550, 50614787}, - {8551, 67392771}, {8552, 33839363}, {8553, 16783107}, {8554, 33839875}, - {8555, 50617603}, {8556, 16780035}, {8557, 16777731}, {8558, 16777987}, - {8559, 16780291}, {8560, 16779267}, {8561, 33835267}, {8562, 50612483}, - {8563, 33836547}, {8564, 16782595}, {8565, 33837059}, {8566, 50614787}, - {8567, 67392771}, {8568, 33839363}, {8569, 16783107}, {8570, 33839875}, - {8571, 50617603}, {8572, 16780035}, {8573, 16777731}, {8574, 16777987}, - {8575, 16780291}, {8576, 1}, {8579, 2}, {8580, 1}, - {8585, 50618371}, {8586, 1}, {8588, 2}, {8592, 1}, - {8748, 33841923}, {8749, 50619651}, {8750, 1}, {8751, 33843203}, - {8752, 50620931}, {8753, 1}, {9001, 17067267}, {9002, 17067523}, - {9003, 1}, {9255, 2}, {9280, 1}, {9291, 2}, - {9312, 16786947}, {9313, 16785155}, {9314, 16785411}, {9315, 16787715}, - {9316, 17035779}, {9317, 17036035}, {9318, 17036291}, {9319, 17036547}, - {9320, 17036803}, {9321, 33825539}, {9322, 33564163}, {9323, 33844995}, - {9324, 33845507}, {9325, 33846019}, {9326, 33846531}, {9327, 33847043}, - {9328, 33847555}, {9329, 33848067}, {9330, 33848579}, {9331, 33849091}, - {9332, 50626819}, {9333, 50627587}, {9334, 50628355}, {9335, 50629123}, - {9336, 50629891}, {9337, 50630659}, {9338, 50631427}, {9339, 50632195}, - {9340, 50632963}, {9341, 67410947}, {9342, 67411971}, {9343, 67412995}, - {9344, 67414019}, {9345, 67415043}, {9346, 67416067}, {9347, 67417091}, - {9348, 67418115}, {9349, 67419139}, {9350, 67420163}, {9351, 67421187}, - {9352, 2}, {9372, 50644995}, {9373, 50645763}, {9374, 50646531}, - {9375, 50647299}, {9376, 50648067}, {9377, 50648835}, {9378, 50649603}, - {9379, 50650371}, {9380, 50651139}, {9381, 50651907}, {9382, 50652675}, - {9383, 50653443}, {9384, 50654211}, {9385, 50654979}, {9386, 50655747}, - {9387, 50656515}, {9388, 50657283}, {9389, 50658051}, {9390, 50658819}, - {9391, 50659587}, {9392, 50660355}, {9393, 50661123}, {9394, 50661891}, - {9395, 50662659}, {9396, 50663427}, {9397, 50664195}, {9398, 16777219}, - {9399, 16777475}, {9400, 16777731}, {9401, 16777987}, {9402, 16778243}, - {9403, 16778499}, {9404, 16778755}, {9405, 16779011}, {9406, 16779267}, - {9407, 16779523}, {9408, 16779779}, {9409, 16780035}, {9410, 16780291}, - {9411, 16780547}, {9412, 16780803}, {9413, 16781059}, {9414, 16781315}, - {9415, 16781571}, {9416, 16781827}, {9417, 16782083}, {9418, 16782339}, - {9419, 16782595}, {9420, 16782851}, {9421, 16783107}, {9422, 16783363}, - {9423, 16783619}, {9424, 16777219}, {9425, 16777475}, {9426, 16777731}, - {9427, 16777987}, {9428, 16778243}, {9429, 16778499}, {9430, 16778755}, - {9431, 16779011}, {9432, 16779267}, {9433, 16779523}, {9434, 16779779}, - {9435, 16780035}, {9436, 16780291}, {9437, 16780547}, {9438, 16780803}, - {9439, 16781059}, {9440, 16781315}, {9441, 16781571}, {9442, 16781827}, - {9443, 16782083}, {9444, 16782339}, {9445, 16782595}, {9446, 16782851}, - {9447, 16783107}, {9448, 16783363}, {9449, 16783619}, {9450, 17035523}, - {9451, 1}, {10764, 67396355}, {10765, 1}, {10868, 50664963}, - {10869, 33888515}, {10870, 50665475}, {10871, 1}, {10972, 33889027}, - {10973, 1}, {11124, 2}, {11126, 1}, {11158, 2}, - {11159, 1}, {11264, 17112323}, {11265, 17112579}, {11266, 17112835}, - {11267, 17113091}, {11268, 17113347}, {11269, 17113603}, {11270, 17113859}, - {11271, 17114115}, {11272, 17114371}, {11273, 17114627}, {11274, 17114883}, - {11275, 17115139}, {11276, 17115395}, {11277, 17115651}, {11278, 17115907}, - {11279, 17116163}, {11280, 17116419}, {11281, 17116675}, {11282, 17116931}, - {11283, 17117187}, {11284, 17117443}, {11285, 17117699}, {11286, 17117955}, - {11287, 17118211}, {11288, 17118467}, {11289, 17118723}, {11290, 17118979}, - {11291, 17119235}, {11292, 17119491}, {11293, 17119747}, {11294, 17120003}, - {11295, 17120259}, {11296, 17120515}, {11297, 17120771}, {11298, 17121027}, - {11299, 17121283}, {11300, 17121539}, {11301, 17121795}, {11302, 17122051}, - {11303, 17122307}, {11304, 17122563}, {11305, 17122819}, {11306, 17123075}, - {11307, 17123331}, {11308, 17123587}, {11309, 17123843}, {11310, 17124099}, - {11311, 17124355}, {11312, 1}, {11360, 17124611}, {11361, 1}, - {11362, 17124867}, {11363, 17125123}, {11364, 17125379}, {11365, 1}, - {11367, 17125635}, {11368, 1}, {11369, 17125891}, {11370, 1}, - {11371, 17126147}, {11372, 1}, {11373, 16948483}, {11374, 16953091}, - {11375, 16948227}, {11376, 16950275}, {11377, 1}, {11378, 17126403}, - {11379, 1}, {11381, 17126659}, {11382, 1}, {11388, 16779523}, - {11389, 16782595}, {11390, 17126915}, {11391, 17127171}, {11392, 17127427}, - {11393, 1}, {11394, 17127683}, {11395, 1}, {11396, 17127939}, - {11397, 1}, {11398, 17128195}, {11399, 1}, {11400, 17128451}, - {11401, 1}, {11402, 17128707}, {11403, 1}, {11404, 17128963}, - {11405, 1}, {11406, 17129219}, {11407, 1}, {11408, 17129475}, - {11409, 1}, {11410, 17129731}, {11411, 1}, {11412, 17129987}, - {11413, 1}, {11414, 17130243}, {11415, 1}, {11416, 17130499}, - {11417, 1}, {11418, 17130755}, {11419, 1}, {11420, 17131011}, - {11421, 1}, {11422, 17131267}, {11423, 1}, {11424, 17131523}, - {11425, 1}, {11426, 17131779}, {11427, 1}, {11428, 17132035}, - {11429, 1}, {11430, 17132291}, {11431, 1}, {11432, 17132547}, - {11433, 1}, {11434, 17132803}, {11435, 1}, {11436, 17133059}, - {11437, 1}, {11438, 17133315}, {11439, 1}, {11440, 17133571}, - {11441, 1}, {11442, 17133827}, {11443, 1}, {11444, 17134083}, - {11445, 1}, {11446, 17134339}, {11447, 1}, {11448, 17134595}, - {11449, 1}, {11450, 17134851}, {11451, 1}, {11452, 17135107}, - {11453, 1}, {11454, 17135363}, {11455, 1}, {11456, 17135619}, - {11457, 1}, {11458, 17135875}, {11459, 1}, {11460, 17136131}, - {11461, 1}, {11462, 17136387}, {11463, 1}, {11464, 17136643}, - {11465, 1}, {11466, 17136899}, {11467, 1}, {11468, 17137155}, - {11469, 1}, {11470, 17137411}, {11471, 1}, {11472, 17137667}, - {11473, 1}, {11474, 17137923}, {11475, 1}, {11476, 17138179}, - {11477, 1}, {11478, 17138435}, {11479, 1}, {11480, 17138691}, - {11481, 1}, {11482, 17138947}, {11483, 1}, {11484, 17139203}, - {11485, 1}, {11486, 17139459}, {11487, 1}, {11488, 17139715}, - {11489, 1}, {11490, 17139971}, {11491, 1}, {11499, 17140227}, - {11500, 1}, {11501, 17140483}, {11502, 1}, {11506, 17140739}, - {11507, 1}, {11508, 2}, {11513, 1}, {11558, 2}, - {11559, 1}, {11560, 2}, {11565, 1}, {11566, 2}, - {11568, 1}, {11624, 2}, {11631, 17140995}, {11632, 1}, - {11633, 2}, {11647, 1}, {11671, 2}, {11680, 1}, - {11687, 2}, {11688, 1}, {11695, 2}, {11696, 1}, - {11703, 2}, {11704, 1}, {11711, 2}, {11712, 1}, - {11719, 2}, {11720, 1}, {11727, 2}, {11728, 1}, - {11735, 2}, {11736, 1}, {11743, 2}, {11744, 1}, - {11870, 2}, {11904, 1}, {11930, 2}, {11931, 1}, - {11935, 17141251}, {11936, 1}, {12019, 17141507}, {12020, 2}, - {12032, 17141763}, {12033, 17142019}, {12034, 17142275}, {12035, 17142531}, - {12036, 17142787}, {12037, 17143043}, {12038, 17143299}, {12039, 17143555}, - {12040, 17143811}, {12041, 17144067}, {12042, 17144323}, {12043, 17144579}, - {12044, 17144835}, {12045, 17145091}, {12046, 17145347}, {12047, 17145603}, - {12048, 17145859}, {12049, 17146115}, {12050, 17146371}, {12051, 17146627}, - {12052, 17146883}, {12053, 17147139}, {12054, 17147395}, {12055, 17147651}, - {12056, 17147907}, {12057, 17148163}, {12058, 17148419}, {12059, 17148675}, - {12060, 17148931}, {12061, 17149187}, {12062, 17149443}, {12063, 17149699}, - {12064, 17149955}, {12065, 17150211}, {12066, 17150467}, {12067, 17150723}, - {12068, 17150979}, {12069, 17151235}, {12070, 17151491}, {12071, 17151747}, - {12072, 17152003}, {12073, 17152259}, {12074, 17152515}, {12075, 17152771}, - {12076, 17153027}, {12077, 17153283}, {12078, 17153539}, {12079, 17153795}, - {12080, 17154051}, {12081, 17154307}, {12082, 17154563}, {12083, 17154819}, - {12084, 17155075}, {12085, 17155331}, {12086, 17155587}, {12087, 17155843}, - {12088, 17156099}, {12089, 17156355}, {12090, 17156611}, {12091, 17156867}, - {12092, 17157123}, {12093, 17157379}, {12094, 17157635}, {12095, 17157891}, - {12096, 17158147}, {12097, 17158403}, {12098, 17158659}, {12099, 17158915}, - {12100, 17159171}, {12101, 17159427}, {12102, 17159683}, {12103, 17159939}, - {12104, 17160195}, {12105, 17160451}, {12106, 17160707}, {12107, 17160963}, - {12108, 17161219}, {12109, 17161475}, {12110, 17161731}, {12111, 17161987}, - {12112, 17162243}, {12113, 17162499}, {12114, 17162755}, {12115, 17163011}, - {12116, 17163267}, {12117, 17163523}, {12118, 17163779}, {12119, 17164035}, - {12120, 17164291}, {12121, 17164547}, {12122, 17164803}, {12123, 17165059}, - {12124, 17165315}, {12125, 17165571}, {12126, 17165827}, {12127, 17166083}, - {12128, 17166339}, {12129, 17166595}, {12130, 17166851}, {12131, 17167107}, - {12132, 17167363}, {12133, 17167619}, {12134, 17167875}, {12135, 17168131}, - {12136, 17168387}, {12137, 17168643}, {12138, 17168899}, {12139, 17169155}, - {12140, 17169411}, {12141, 17169667}, {12142, 17169923}, {12143, 17170179}, - {12144, 17170435}, {12145, 17170691}, {12146, 17170947}, {12147, 17171203}, - {12148, 17171459}, {12149, 17171715}, {12150, 17171971}, {12151, 17172227}, - {12152, 17172483}, {12153, 17172739}, {12154, 17172995}, {12155, 17173251}, - {12156, 17173507}, {12157, 17173763}, {12158, 17174019}, {12159, 17174275}, - {12160, 17174531}, {12161, 17174787}, {12162, 17175043}, {12163, 17175299}, - {12164, 17175555}, {12165, 17175811}, {12166, 17176067}, {12167, 17176323}, - {12168, 17176579}, {12169, 17176835}, {12170, 17177091}, {12171, 17177347}, - {12172, 17177603}, {12173, 17177859}, {12174, 17178115}, {12175, 17178371}, - {12176, 17178627}, {12177, 17178883}, {12178, 17179139}, {12179, 17179395}, - {12180, 17179651}, {12181, 17179907}, {12182, 17180163}, {12183, 17180419}, - {12184, 17180675}, {12185, 17180931}, {12186, 17181187}, {12187, 17181443}, - {12188, 17181699}, {12189, 17181955}, {12190, 17182211}, {12191, 17182467}, - {12192, 17182723}, {12193, 17182979}, {12194, 17183235}, {12195, 17183491}, - {12196, 17183747}, {12197, 17184003}, {12198, 17184259}, {12199, 17184515}, - {12200, 17184771}, {12201, 17185027}, {12202, 17185283}, {12203, 17185539}, - {12204, 17185795}, {12205, 17186051}, {12206, 17186307}, {12207, 17186563}, - {12208, 17186819}, {12209, 17187075}, {12210, 17187331}, {12211, 17187587}, - {12212, 17187843}, {12213, 17188099}, {12214, 17188355}, {12215, 17188611}, - {12216, 17188867}, {12217, 17189123}, {12218, 17189379}, {12219, 17189635}, - {12220, 17189891}, {12221, 17190147}, {12222, 17190403}, {12223, 17190659}, - {12224, 17190915}, {12225, 17191171}, {12226, 17191427}, {12227, 17191683}, - {12228, 17191939}, {12229, 17192195}, {12230, 17192451}, {12231, 17192707}, - {12232, 17192963}, {12233, 17193219}, {12234, 17193475}, {12235, 17193731}, - {12236, 17193987}, {12237, 17194243}, {12238, 17194499}, {12239, 17194755}, - {12240, 17195011}, {12241, 17195267}, {12242, 17195523}, {12243, 17195779}, - {12244, 17196035}, {12245, 17196291}, {12246, 2}, {12288, 16783875}, - {12289, 1}, {12290, 17196547}, {12291, 1}, {12342, 17196803}, - {12343, 1}, {12344, 17147651}, {12345, 17197059}, {12346, 17197315}, - {12347, 1}, {12352, 2}, {12353, 1}, {12439, 2}, - {12441, 1}, {12443, 33974787}, {12444, 33975299}, {12445, 1}, - {12447, 33975811}, {12448, 1}, {12543, 33976323}, {12544, 2}, - {12549, 1}, {12592, 2}, {12593, 17199619}, {12594, 17199875}, - {12595, 17200131}, {12596, 17200387}, {12597, 17200643}, {12598, 17200899}, - {12599, 17201155}, {12600, 17201411}, {12601, 17201667}, {12602, 17201923}, - {12603, 17202179}, {12604, 17202435}, {12605, 17202691}, {12606, 17202947}, - {12607, 17203203}, {12608, 17203459}, {12609, 17203715}, {12610, 17203971}, - {12611, 17204227}, {12612, 17204483}, {12613, 17204739}, {12614, 17204995}, - {12615, 17205251}, {12616, 17205507}, {12617, 17205763}, {12618, 17206019}, - {12619, 17206275}, {12620, 17206531}, {12621, 17206787}, {12622, 17207043}, - {12623, 17207299}, {12624, 17207555}, {12625, 17207811}, {12626, 17208067}, - {12627, 17208323}, {12628, 17208579}, {12629, 17208835}, {12630, 17209091}, - {12631, 17209347}, {12632, 17209603}, {12633, 17209859}, {12634, 17210115}, - {12635, 17210371}, {12636, 17210627}, {12637, 17210883}, {12638, 17211139}, - {12639, 17211395}, {12640, 17211651}, {12641, 17211907}, {12642, 17212163}, - {12643, 17212419}, {12644, 2}, {12645, 17212675}, {12646, 17212931}, - {12647, 17213187}, {12648, 17213443}, {12649, 17213699}, {12650, 17213955}, - {12651, 17214211}, {12652, 17214467}, {12653, 17214723}, {12654, 17214979}, - {12655, 17215235}, {12656, 17215491}, {12657, 17215747}, {12658, 17216003}, - {12659, 17216259}, {12660, 17216515}, {12661, 17216771}, {12662, 17217027}, - {12663, 17217283}, {12664, 17217539}, {12665, 17217795}, {12666, 17218051}, - {12667, 17218307}, {12668, 17218563}, {12669, 17218819}, {12670, 17219075}, - {12671, 17219331}, {12672, 17219587}, {12673, 17219843}, {12674, 17220099}, - {12675, 17220355}, {12676, 17220611}, {12677, 17220867}, {12678, 17221123}, - {12679, 17221379}, {12680, 17221635}, {12681, 17221891}, {12682, 17222147}, - {12683, 17222403}, {12684, 17222659}, {12685, 17222915}, {12686, 17223171}, - {12687, 2}, {12688, 1}, {12690, 17141763}, {12691, 17143299}, - {12692, 17223427}, {12693, 17223683}, {12694, 17223939}, {12695, 17224195}, - {12696, 17224451}, {12697, 17224707}, {12698, 17142787}, {12699, 17224963}, - {12700, 17225219}, {12701, 17225475}, {12702, 17225731}, {12703, 17143811}, - {12704, 1}, {12772, 2}, {12784, 1}, {12800, 50780419}, - {12801, 50781187}, {12802, 50781955}, {12803, 50782723}, {12804, 50783491}, - {12805, 50784259}, {12806, 50785027}, {12807, 50785795}, {12808, 50786563}, - {12809, 50787331}, {12810, 50788099}, {12811, 50788867}, {12812, 50789635}, - {12813, 50790403}, {12814, 50791171}, {12815, 50791939}, {12816, 50792707}, - {12817, 50793475}, {12818, 50794243}, {12819, 50795011}, {12820, 50795779}, - {12821, 50796547}, {12822, 50797315}, {12823, 50798083}, {12824, 50798851}, - {12825, 50799619}, {12826, 50800387}, {12827, 50801155}, {12828, 50801923}, - {12829, 67579907}, {12830, 67580931}, {12831, 2}, {12832, 50804739}, - {12833, 50805507}, {12834, 50806275}, {12835, 50807043}, {12836, 50807811}, - {12837, 50808579}, {12838, 50809347}, {12839, 50810115}, {12840, 50810883}, - {12841, 50811651}, {12842, 50812419}, {12843, 50813187}, {12844, 50813955}, - {12845, 50814723}, {12846, 50815491}, {12847, 50816259}, {12848, 50817027}, - {12849, 50817795}, {12850, 50818563}, {12851, 50819331}, {12852, 50820099}, - {12853, 50820867}, {12854, 50821635}, {12855, 50822403}, {12856, 50823171}, - {12857, 50823939}, {12858, 50824707}, {12859, 50825475}, {12860, 50826243}, - {12861, 50827011}, {12862, 50827779}, {12863, 50828547}, {12864, 50829315}, - {12865, 50830083}, {12866, 50830851}, {12867, 50831619}, {12868, 17277955}, - {12869, 17278211}, {12870, 17158659}, {12871, 17278467}, {12872, 1}, - {12880, 50833155}, {12881, 33845251}, {12882, 34056707}, {12883, 33562371}, - {12884, 34057219}, {12885, 34057731}, {12886, 34058243}, {12887, 34058755}, - {12888, 34059267}, {12889, 34059779}, {12890, 34060291}, {12891, 33827331}, - {12892, 33826563}, {12893, 34060803}, {12894, 34061315}, {12895, 34061827}, - {12896, 17199619}, {12897, 17200387}, {12898, 17201155}, {12899, 17201667}, - {12900, 17203715}, {12901, 17203971}, {12902, 17204739}, {12903, 17205251}, - {12904, 17205507}, {12905, 17206019}, {12906, 17206275}, {12907, 17206531}, - {12908, 17206787}, {12909, 17207043}, {12910, 17236995}, {12911, 17237763}, - {12912, 17238531}, {12913, 17239299}, {12914, 17240067}, {12915, 17240835}, - {12916, 17241603}, {12917, 17242371}, {12918, 17243139}, {12919, 17243907}, - {12920, 17244675}, {12921, 17245443}, {12922, 17246211}, {12923, 17246979}, - {12924, 34062339}, {12925, 34062851}, {12926, 17286147}, {12927, 1}, - {12928, 17141763}, {12929, 17143299}, {12930, 17223427}, {12931, 17223683}, - {12932, 17253635}, {12933, 17254403}, {12934, 17255171}, {12935, 17144579}, - {12936, 17256707}, {12937, 17147651}, {12938, 17160451}, {12939, 17163523}, - {12940, 17163267}, {12941, 17160707}, {12942, 17184259}, {12943, 17149699}, - {12944, 17159939}, {12945, 17263619}, {12946, 17264387}, {12947, 17265155}, - {12948, 17265923}, {12949, 17266691}, {12950, 17267459}, {12951, 17268227}, - {12952, 17268995}, {12953, 17286403}, {12954, 17286659}, {12955, 17151235}, - {12956, 17286915}, {12957, 17287171}, {12958, 17287427}, {12959, 17287683}, - {12960, 17287939}, {12961, 17275907}, {12962, 17288195}, {12963, 17288451}, - {12964, 17223939}, {12965, 17224195}, {12966, 17224451}, {12967, 17288707}, - {12968, 17288963}, {12969, 17289219}, {12970, 17289475}, {12971, 17271299}, - {12972, 17272067}, {12973, 17272835}, {12974, 17273603}, {12975, 17274371}, - {12976, 17289731}, {12977, 34067203}, {12978, 34067715}, {12979, 34068227}, - {12980, 34068739}, {12981, 34069251}, {12982, 33564931}, {12983, 34057475}, - {12984, 34061571}, {12985, 34069763}, {12986, 34070275}, {12987, 34070787}, - {12988, 34071299}, {12989, 34071811}, {12990, 34072323}, {12991, 34072835}, - {12992, 34073347}, {12993, 34073859}, {12994, 34074371}, {12995, 34074883}, - {12996, 34075395}, {12997, 34075907}, {12998, 34076419}, {12999, 34076931}, - {13000, 34077443}, {13001, 50855171}, {13002, 50855939}, {13003, 50856707}, - {13004, 34080259}, {13005, 50857987}, {13006, 34081539}, {13007, 50859267}, - {13008, 17305603}, {13009, 17305859}, {13010, 17306115}, {13011, 17306371}, - {13012, 17306627}, {13013, 17306883}, {13014, 17307139}, {13015, 17307395}, - {13016, 17307651}, {13017, 17199107}, {13018, 17307907}, {13019, 17308163}, - {13020, 17308419}, {13021, 17308675}, {13022, 17308931}, {13023, 17309187}, - {13024, 17309443}, {13025, 17309699}, {13026, 17309955}, {13027, 17199363}, - {13028, 17310211}, {13029, 17310467}, {13030, 17310723}, {13031, 17310979}, - {13032, 17311235}, {13033, 17311491}, {13034, 17311747}, {13035, 17312003}, - {13036, 17312259}, {13037, 17312515}, {13038, 17312771}, {13039, 17313027}, - {13040, 17313283}, {13041, 17313539}, {13042, 17313795}, {13043, 17314051}, - {13044, 17314307}, {13045, 17314563}, {13046, 17314819}, {13047, 17315075}, - {13048, 17315331}, {13049, 17315587}, {13050, 17315843}, {13051, 17316099}, - {13052, 17316355}, {13053, 17316611}, {13054, 17316867}, {13055, 34094339}, - {13056, 67649283}, {13057, 67650307}, {13058, 67651331}, {13059, 50875139}, - {13060, 67653123}, {13061, 50876931}, {13062, 50877699}, {13063, 84432899}, - {13064, 67656963}, {13065, 50880771}, {13066, 50881539}, {13067, 50882307}, - {13068, 67660291}, {13069, 67661315}, {13070, 50885123}, {13071, 50885891}, - {13072, 34109443}, {13073, 50887171}, {13074, 67665155}, {13075, 67666179}, - {13076, 34112771}, {13077, 84444931}, {13078, 101223427}, {13079, 84447747}, - {13080, 50891011}, {13081, 84449027}, {13082, 84450307}, {13083, 67674371}, - {13084, 50898179}, {13085, 50898947}, {13086, 50899715}, {13087, 67677699}, - {13088, 84455939}, {13089, 67680003}, {13090, 50903811}, {13091, 50904579}, - {13092, 50905347}, {13093, 34128899}, {13094, 34129411}, {13095, 34118147}, - {13096, 34129923}, {13097, 50907651}, {13098, 50908419}, {13099, 84463619}, - {13100, 50910467}, {13101, 67688451}, {13102, 84466691}, {13103, 50913539}, - {13104, 34137091}, {13105, 34137603}, {13106, 84469763}, {13107, 67693827}, - {13108, 84472067}, {13109, 50918915}, {13110, 84474115}, {13111, 34143747}, - {13112, 50921475}, {13113, 50922243}, {13114, 50923011}, {13115, 50923779}, - {13116, 50924547}, {13117, 67702531}, {13118, 50926339}, {13119, 34149891}, - {13120, 50927619}, {13121, 50928387}, {13122, 50929155}, {13123, 67707139}, - {13124, 50930947}, {13125, 50931715}, {13126, 50932483}, {13127, 84487683}, - {13128, 67711747}, {13129, 34158339}, {13130, 84490499}, {13131, 34160131}, - {13132, 67715075}, {13133, 67669507}, {13134, 50938883}, {13135, 50939651}, - {13136, 50940419}, {13137, 67718403}, {13138, 34164995}, {13139, 50942723}, - {13140, 67720707}, {13141, 34167299}, {13142, 84499459}, {13143, 50893827}, - {13144, 34169091}, {13145, 34169603}, {13146, 34170115}, {13147, 34170627}, - {13148, 34171139}, {13149, 34171651}, {13150, 34172163}, {13151, 34172675}, - {13152, 34173187}, {13153, 34173699}, {13154, 50951427}, {13155, 50952195}, - {13156, 50952963}, {13157, 50953731}, {13158, 50954499}, {13159, 50955267}, - {13160, 50956035}, {13161, 50956803}, {13162, 50957571}, {13163, 50958339}, - {13164, 50959107}, {13165, 50959875}, {13166, 50960643}, {13167, 50961411}, - {13168, 50962179}, {13169, 50962947}, {13170, 34186499}, {13171, 34187011}, - {13172, 50964739}, {13173, 34188291}, {13174, 34188803}, {13175, 34189315}, - {13176, 50967043}, {13177, 50967811}, {13178, 34191363}, {13179, 34191875}, - {13180, 34192387}, {13181, 34192899}, {13182, 34193411}, {13183, 67748355}, - {13184, 34185987}, {13185, 34194947}, {13186, 34195459}, {13187, 34195971}, - {13188, 34196483}, {13189, 34196995}, {13190, 34197507}, {13191, 34198019}, - {13192, 50975747}, {13193, 67753731}, {13194, 34200323}, {13195, 34200835}, - {13196, 34201347}, {13197, 34201859}, {13198, 34202371}, {13199, 34202883}, - {13200, 34203395}, {13201, 50981123}, {13202, 50981891}, {13203, 50980355}, - {13204, 50982659}, {13205, 34206211}, {13206, 34206723}, {13207, 34207235}, - {13208, 33556995}, {13209, 34207747}, {13210, 34208259}, {13211, 34208771}, - {13212, 34209283}, {13213, 34209795}, {13214, 34210307}, {13215, 50988035}, - {13216, 50988803}, {13217, 34190083}, {13218, 50989571}, {13219, 50990339}, - {13220, 50991107}, {13221, 34190851}, {13222, 50991875}, {13223, 50992643}, - {13224, 67770627}, {13225, 34185987}, {13226, 50994435}, {13227, 50995203}, - {13228, 50995971}, {13229, 50996739}, {13230, 84551939}, {13231, 101330435}, - {13232, 34223107}, {13233, 34223619}, {13234, 34224131}, {13235, 34224643}, - {13236, 34225155}, {13237, 34225667}, {13238, 34226179}, {13239, 34226691}, - {13240, 34227203}, {13241, 34226691}, {13242, 34227715}, {13243, 34228227}, - {13244, 34228739}, {13245, 34229251}, {13246, 34229763}, {13247, 34229251}, - {13248, 34230275}, {13249, 34230787}, {13250, 2}, {13251, 34231299}, - {13252, 33817347}, {13253, 33554947}, {13254, 67786243}, {13255, 2}, - {13256, 34232835}, {13257, 34233347}, {13258, 34233859}, {13259, 34185731}, - {13260, 34234371}, {13261, 34234883}, {13262, 34210307}, {13263, 34235395}, - {13264, 33557251}, {13265, 34235907}, {13266, 51013635}, {13267, 34237187}, - {13268, 34197507}, {13269, 51014915}, {13270, 51015683}, {13271, 34239235}, - {13272, 2}, {13273, 51016963}, {13274, 34240515}, {13275, 34221315}, - {13276, 34241027}, {13277, 34241539}, {13278, 51019267}, {13279, 51020035}, - {13280, 34243587}, {13281, 34244099}, {13282, 34244611}, {13283, 34245123}, - {13284, 34245635}, {13285, 34246147}, {13286, 34246659}, {13287, 34247171}, - {13288, 34247683}, {13289, 51025411}, {13290, 51026179}, {13291, 51026947}, - {13292, 51027715}, {13293, 51028483}, {13294, 51029251}, {13295, 51030019}, - {13296, 51030787}, {13297, 51031555}, {13298, 51032323}, {13299, 51033091}, - {13300, 51033859}, {13301, 51034627}, {13302, 51035395}, {13303, 51036163}, - {13304, 51036931}, {13305, 51037699}, {13306, 51038467}, {13307, 51039235}, - {13308, 51040003}, {13309, 51040771}, {13310, 51041539}, {13311, 51042307}, - {13312, 1}, {42125, 2}, {42128, 1}, {42183, 2}, - {42192, 1}, {42540, 2}, {42560, 17488643}, {42561, 1}, - {42562, 17488899}, {42563, 1}, {42564, 17489155}, {42565, 1}, - {42566, 17489411}, {42567, 1}, {42568, 17489667}, {42569, 1}, - {42570, 16936451}, {42571, 1}, {42572, 17489923}, {42573, 1}, - {42574, 17490179}, {42575, 1}, {42576, 17490435}, {42577, 1}, - {42578, 17490691}, {42579, 1}, {42580, 17490947}, {42581, 1}, - {42582, 17491203}, {42583, 1}, {42584, 17491459}, {42585, 1}, - {42586, 17491715}, {42587, 1}, {42588, 17491971}, {42589, 1}, - {42590, 17492227}, {42591, 1}, {42592, 17492483}, {42593, 1}, - {42594, 17492739}, {42595, 1}, {42596, 17492995}, {42597, 1}, - {42598, 17493251}, {42599, 1}, {42600, 17493507}, {42601, 1}, - {42602, 17493763}, {42603, 1}, {42604, 17494019}, {42605, 1}, - {42624, 17494275}, {42625, 1}, {42626, 17494531}, {42627, 1}, - {42628, 17494787}, {42629, 1}, {42630, 17495043}, {42631, 1}, - {42632, 17495299}, {42633, 1}, {42634, 17495555}, {42635, 1}, - {42636, 17495811}, {42637, 1}, {42638, 17496067}, {42639, 1}, - {42640, 17496323}, {42641, 1}, {42642, 17496579}, {42643, 1}, - {42644, 17496835}, {42645, 1}, {42646, 17497091}, {42647, 1}, - {42648, 17497347}, {42649, 1}, {42650, 17497603}, {42651, 1}, - {42652, 16873219}, {42653, 16873731}, {42654, 1}, {42744, 2}, - {42752, 1}, {42786, 17497859}, {42787, 1}, {42788, 17498115}, - {42789, 1}, {42790, 17498371}, {42791, 1}, {42792, 17498627}, - {42793, 1}, {42794, 17498883}, {42795, 1}, {42796, 17499139}, - {42797, 1}, {42798, 17499395}, {42799, 1}, {42802, 17499651}, - {42803, 1}, {42804, 17499907}, {42805, 1}, {42806, 17500163}, - {42807, 1}, {42808, 17500419}, {42809, 1}, {42810, 17500675}, - {42811, 1}, {42812, 17500931}, {42813, 1}, {42814, 17501187}, - {42815, 1}, {42816, 17501443}, {42817, 1}, {42818, 17501699}, - {42819, 1}, {42820, 17501955}, {42821, 1}, {42822, 17502211}, - {42823, 1}, {42824, 17502467}, {42825, 1}, {42826, 17502723}, - {42827, 1}, {42828, 17502979}, {42829, 1}, {42830, 17503235}, - {42831, 1}, {42832, 17503491}, {42833, 1}, {42834, 17503747}, - {42835, 1}, {42836, 17504003}, {42837, 1}, {42838, 17504259}, - {42839, 1}, {42840, 17504515}, {42841, 1}, {42842, 17504771}, - {42843, 1}, {42844, 17505027}, {42845, 1}, {42846, 17505283}, - {42847, 1}, {42848, 17505539}, {42849, 1}, {42850, 17505795}, - {42851, 1}, {42852, 17506051}, {42853, 1}, {42854, 17506307}, - {42855, 1}, {42856, 17506563}, {42857, 1}, {42858, 17506819}, - {42859, 1}, {42860, 17507075}, {42861, 1}, {42862, 17507331}, - {42863, 1}, {42864, 17507331}, {42865, 1}, {42873, 17507587}, - {42874, 1}, {42875, 17507843}, {42876, 1}, {42877, 17508099}, - {42878, 17508355}, {42879, 1}, {42880, 17508611}, {42881, 1}, - {42882, 17508867}, {42883, 1}, {42884, 17509123}, {42885, 1}, - {42886, 17509379}, {42887, 1}, {42891, 17509635}, {42892, 1}, - {42893, 16951299}, {42894, 1}, {42896, 17509891}, {42897, 1}, - {42898, 17510147}, {42899, 1}, {42902, 17510403}, {42903, 1}, - {42904, 17510659}, {42905, 1}, {42906, 17510915}, {42907, 1}, - {42908, 17511171}, {42909, 1}, {42910, 17511427}, {42911, 1}, - {42912, 17511683}, {42913, 1}, {42914, 17511939}, {42915, 1}, - {42916, 17512195}, {42917, 1}, {42918, 17512451}, {42919, 1}, - {42920, 17512707}, {42921, 1}, {42922, 16841475}, {42923, 16948995}, - {42924, 16951043}, {42925, 17512963}, {42926, 16951555}, {42927, 1}, - {42928, 17513219}, {42929, 17513475}, {42930, 16952067}, {42931, 17513731}, - {42932, 17513987}, {42933, 1}, {42934, 17514243}, {42935, 1}, - {42936, 17514499}, {42937, 1}, {42938, 17514755}, {42939, 1}, - {42940, 17515011}, {42941, 1}, {42942, 17515267}, {42943, 1}, - {42944, 17515523}, {42945, 1}, {42946, 17515779}, {42947, 1}, - {42948, 17516035}, {42949, 16954371}, {42950, 17516291}, {42951, 17516547}, - {42952, 1}, {42953, 17516803}, {42954, 1}, {42955, 2}, - {42960, 17517059}, {42961, 1}, {42962, 2}, {42963, 1}, - {42964, 2}, {42965, 1}, {42966, 17517315}, {42967, 1}, - {42968, 17517571}, {42969, 1}, {42970, 2}, {42994, 16777731}, - {42995, 16778499}, {42996, 16781315}, {42997, 17517827}, {42998, 1}, - {43000, 16802051}, {43001, 16808195}, {43002, 1}, {43053, 2}, - {43056, 1}, {43066, 2}, {43072, 1}, {43128, 2}, - {43136, 1}, {43206, 2}, {43214, 1}, {43226, 2}, - {43232, 1}, {43348, 2}, {43359, 1}, {43389, 2}, - {43392, 1}, {43470, 2}, {43471, 1}, {43482, 2}, - {43486, 1}, {43519, 2}, {43520, 1}, {43575, 2}, - {43584, 1}, {43598, 2}, {43600, 1}, {43610, 2}, - {43612, 1}, {43715, 2}, {43739, 1}, {43767, 2}, - {43777, 1}, {43783, 2}, {43785, 1}, {43791, 2}, - {43793, 1}, {43799, 2}, {43808, 1}, {43815, 2}, - {43816, 1}, {43823, 2}, {43824, 1}, {43868, 17498371}, - {43869, 17518083}, {43870, 17124867}, {43871, 17518339}, {43872, 1}, - {43881, 17518595}, {43882, 1}, {43884, 2}, {43888, 17518851}, - {43889, 17519107}, {43890, 17519363}, {43891, 17519619}, {43892, 17519875}, - {43893, 17520131}, {43894, 17520387}, {43895, 17520643}, {43896, 17520899}, - {43897, 17521155}, {43898, 17521411}, {43899, 17521667}, {43900, 17521923}, - {43901, 17522179}, {43902, 17522435}, {43903, 17522691}, {43904, 17522947}, - {43905, 17523203}, {43906, 17523459}, {43907, 17523715}, {43908, 17523971}, - {43909, 17524227}, {43910, 17524483}, {43911, 17524739}, {43912, 17524995}, - {43913, 17525251}, {43914, 17525507}, {43915, 17525763}, {43916, 17526019}, - {43917, 17526275}, {43918, 17526531}, {43919, 17526787}, {43920, 17527043}, - {43921, 17527299}, {43922, 17527555}, {43923, 17527811}, {43924, 17528067}, - {43925, 17528323}, {43926, 17528579}, {43927, 17528835}, {43928, 17529091}, - {43929, 17529347}, {43930, 17529603}, {43931, 17529859}, {43932, 17530115}, - {43933, 17530371}, {43934, 17530627}, {43935, 17530883}, {43936, 17531139}, - {43937, 17531395}, {43938, 17531651}, {43939, 17531907}, {43940, 17532163}, - {43941, 17532419}, {43942, 17532675}, {43943, 17532931}, {43944, 17533187}, - {43945, 17533443}, {43946, 17533699}, {43947, 17533955}, {43948, 17534211}, - {43949, 17534467}, {43950, 17534723}, {43951, 17534979}, {43952, 17535235}, - {43953, 17535491}, {43954, 17535747}, {43955, 17536003}, {43956, 17536259}, - {43957, 17536515}, {43958, 17536771}, {43959, 17537027}, {43960, 17537283}, - {43961, 17537539}, {43962, 17537795}, {43963, 17538051}, {43964, 17538307}, - {43965, 17538563}, {43966, 17538819}, {43967, 17539075}, {43968, 1}, - {44014, 2}, {44016, 1}, {44026, 2}, {44032, 1}, - {55204, 2}, {55216, 1}, {55239, 2}, {55243, 1}, - {55292, 2}, {63744, 17539331}, {63745, 17539587}, {63746, 17182211}, - {63747, 17539843}, {63748, 17540099}, {63749, 17540355}, {63750, 17540611}, - {63751, 17196035}, {63753, 17540867}, {63754, 17184259}, {63755, 17541123}, - {63756, 17541379}, {63757, 17541635}, {63758, 17541891}, {63759, 17542147}, - {63760, 17542403}, {63761, 17542659}, {63762, 17542915}, {63763, 17543171}, - {63764, 17543427}, {63765, 17543683}, {63766, 17543939}, {63767, 17544195}, - {63768, 17544451}, {63769, 17544707}, {63770, 17544963}, {63771, 17545219}, - {63772, 17545475}, {63773, 17545731}, {63774, 17545987}, {63775, 17546243}, - {63776, 17546499}, {63777, 17546755}, {63778, 17547011}, {63779, 17547267}, - {63780, 17547523}, {63781, 17547779}, {63782, 17548035}, {63783, 17548291}, - {63784, 17548547}, {63785, 17548803}, {63786, 17549059}, {63787, 17549315}, - {63788, 17549571}, {63789, 17549827}, {63790, 17550083}, {63791, 17550339}, - {63792, 17550595}, {63793, 17550851}, {63794, 17551107}, {63795, 17551363}, - {63796, 17173507}, {63797, 17551619}, {63798, 17551875}, {63799, 17552131}, - {63800, 17552387}, {63801, 17552643}, {63802, 17552899}, {63803, 17553155}, - {63804, 17553411}, {63805, 17553667}, {63806, 17553923}, {63807, 17554179}, - {63808, 17192195}, {63809, 17554435}, {63810, 17554691}, {63811, 17554947}, - {63812, 17555203}, {63813, 17555459}, {63814, 17555715}, {63815, 17555971}, - {63816, 17556227}, {63817, 17556483}, {63818, 17556739}, {63819, 17556995}, - {63820, 17557251}, {63821, 17557507}, {63822, 17557763}, {63823, 17558019}, - {63824, 17558275}, {63825, 17558531}, {63826, 17558787}, {63827, 17559043}, - {63828, 17559299}, {63829, 17559555}, {63830, 17559811}, {63831, 17560067}, - {63832, 17560323}, {63833, 17560579}, {63834, 17560835}, {63835, 17561091}, - {63836, 17543427}, {63837, 17561347}, {63838, 17561603}, {63839, 17561859}, - {63840, 17562115}, {63841, 17562371}, {63842, 17562627}, {63843, 17562883}, - {63844, 17563139}, {63845, 17563395}, {63846, 17563651}, {63847, 17563907}, - {63848, 17564163}, {63849, 17564419}, {63850, 17564675}, {63851, 17564931}, - {63852, 17565187}, {63853, 17565443}, {63854, 17565699}, {63855, 17565955}, - {63856, 17566211}, {63857, 17182723}, {63858, 17566467}, {63859, 17566723}, - {63860, 17566979}, {63861, 17567235}, {63862, 17567491}, {63863, 17567747}, - {63864, 17568003}, {63865, 17568259}, {63866, 17568515}, {63867, 17568771}, - {63868, 17569027}, {63869, 17569283}, {63870, 17569539}, {63871, 17569795}, - {63872, 17570051}, {63873, 17151235}, {63874, 17570307}, {63875, 17570563}, - {63876, 17570819}, {63877, 17571075}, {63878, 17571331}, {63879, 17571587}, - {63880, 17571843}, {63881, 17572099}, {63882, 17146371}, {63883, 17572355}, - {63884, 17572611}, {63885, 17572867}, {63886, 17573123}, {63887, 17573379}, - {63888, 17573635}, {63889, 17573891}, {63890, 17574147}, {63891, 17574403}, - {63892, 17574659}, {63893, 17574915}, {63894, 17575171}, {63895, 17575427}, - {63896, 17575683}, {63897, 17575939}, {63898, 17576195}, {63899, 17576451}, - {63900, 17576707}, {63901, 17576963}, {63902, 17577219}, {63903, 17577475}, - {63904, 17577731}, {63905, 17565955}, {63906, 17577987}, {63907, 17578243}, - {63908, 17578499}, {63909, 17578755}, {63910, 17579011}, {63911, 17579267}, - {63912, 17317123}, {63913, 17579523}, {63914, 17561859}, {63915, 17579779}, - {63916, 17580035}, {63917, 17580291}, {63918, 17580547}, {63919, 17580803}, - {63920, 17581059}, {63921, 17581315}, {63922, 17581571}, {63923, 17581827}, - {63924, 17582083}, {63925, 17582339}, {63926, 17582595}, {63927, 17582851}, - {63928, 17583107}, {63929, 17583363}, {63930, 17583619}, {63931, 17583875}, - {63932, 17584131}, {63933, 17584387}, {63934, 17584643}, {63935, 17543427}, - {63936, 17584899}, {63937, 17585155}, {63938, 17585411}, {63939, 17585667}, - {63940, 17195779}, {63941, 17585923}, {63942, 17586179}, {63943, 17586435}, - {63944, 17586691}, {63945, 17586947}, {63946, 17587203}, {63947, 17587459}, - {63948, 17587715}, {63949, 17587971}, {63950, 17588227}, {63951, 17588483}, - {63952, 17588739}, {63953, 17254403}, {63954, 17588995}, {63955, 17589251}, - {63956, 17589507}, {63957, 17589763}, {63958, 17590019}, {63959, 17590275}, - {63960, 17590531}, {63961, 17590787}, {63962, 17591043}, {63963, 17562371}, - {63964, 17591299}, {63965, 17591555}, {63966, 17591811}, {63967, 17592067}, - {63968, 17592323}, {63969, 17592579}, {63970, 17592835}, {63971, 17593091}, - {63972, 17593347}, {63973, 17593603}, {63974, 17593859}, {63975, 17594115}, - {63976, 17594371}, {63977, 17184003}, {63978, 17594627}, {63979, 17594883}, - {63980, 17595139}, {63981, 17595395}, {63982, 17595651}, {63983, 17595907}, - {63984, 17596163}, {63985, 17596419}, {63986, 17596675}, {63987, 17596931}, - {63988, 17597187}, {63989, 17597443}, {63990, 17597699}, {63991, 17171459}, - {63992, 17597955}, {63993, 17598211}, {63994, 17598467}, {63995, 17598723}, - {63996, 17598979}, {63997, 17599235}, {63998, 17599491}, {63999, 17599747}, - {64000, 17600003}, {64001, 17600259}, {64002, 17600515}, {64003, 17600771}, - {64004, 17601027}, {64005, 17601283}, {64006, 17601539}, {64007, 17601795}, - {64008, 17178371}, {64009, 17602051}, {64010, 17179139}, {64011, 17602307}, - {64012, 17602563}, {64013, 17602819}, {64014, 1}, {64016, 17603075}, - {64017, 1}, {64018, 17603331}, {64019, 1}, {64021, 17603587}, - {64022, 17603843}, {64023, 17604099}, {64024, 17604355}, {64025, 17604611}, - {64026, 17604867}, {64027, 17605123}, {64028, 17605379}, {64029, 17605635}, - {64030, 17173251}, {64031, 1}, {64032, 17605891}, {64033, 1}, - {64034, 17606147}, {64035, 1}, {64037, 17606403}, {64038, 17606659}, - {64039, 1}, {64042, 17606915}, {64043, 17607171}, {64044, 17607427}, - {64045, 17607683}, {64046, 17607939}, {64047, 17608195}, {64048, 17608451}, - {64049, 17608707}, {64050, 17608963}, {64051, 17609219}, {64052, 17609475}, - {64053, 17609731}, {64054, 17609987}, {64055, 17610243}, {64056, 17610499}, - {64057, 17610755}, {64058, 17611011}, {64059, 17611267}, {64060, 17153027}, - {64061, 17611523}, {64062, 17611779}, {64063, 17612035}, {64064, 17612291}, - {64065, 17612547}, {64066, 17612803}, {64067, 17613059}, {64068, 17613315}, - {64069, 17613571}, {64070, 17613827}, {64071, 17614083}, {64072, 17614339}, - {64073, 17614595}, {64074, 17614851}, {64075, 17615107}, {64076, 17265155}, - {64077, 17615363}, {64078, 17615619}, {64079, 17615875}, {64080, 17616131}, - {64081, 17268227}, {64082, 17616387}, {64083, 17616643}, {64084, 17616899}, - {64085, 17617155}, {64086, 17617411}, {64087, 17575171}, {64088, 17617667}, - {64089, 17617923}, {64090, 17618179}, {64091, 17618435}, {64092, 17618691}, - {64093, 17618947}, {64095, 17619203}, {64096, 17619459}, {64097, 17619715}, - {64098, 17619971}, {64099, 17620227}, {64100, 17620483}, {64101, 17620739}, - {64102, 17620995}, {64103, 17606403}, {64104, 17621251}, {64105, 17621507}, - {64106, 17621763}, {64107, 17622019}, {64108, 17622275}, {64109, 17622531}, - {64110, 2}, {64112, 17622787}, {64113, 17623043}, {64114, 17623299}, - {64115, 17623555}, {64116, 17623811}, {64117, 17624067}, {64118, 17624323}, - {64119, 17624579}, {64120, 17609987}, {64121, 17624835}, {64122, 17625091}, - {64123, 17625347}, {64124, 17603075}, {64125, 17625603}, {64126, 17625859}, - {64127, 17626115}, {64128, 17626371}, {64129, 17626627}, {64130, 17626883}, - {64131, 17627139}, {64132, 17627395}, {64133, 17627651}, {64134, 17627907}, - {64135, 17628163}, {64136, 17628419}, {64137, 17612035}, {64138, 17628675}, - {64139, 17612291}, {64140, 17628931}, {64141, 17629187}, {64142, 17629443}, - {64143, 17629699}, {64144, 17629955}, {64145, 17603331}, {64146, 17548803}, - {64147, 17630211}, {64148, 17630467}, {64149, 17161475}, {64150, 17566211}, - {64151, 17587203}, {64152, 17630723}, {64153, 17630979}, {64154, 17614083}, - {64155, 17631235}, {64156, 17614339}, {64157, 17631491}, {64158, 17631747}, - {64159, 17632003}, {64160, 17603843}, {64161, 17632259}, {64162, 17632515}, - {64163, 17632771}, {64164, 17633027}, {64165, 17633283}, {64166, 17604099}, - {64167, 17633539}, {64168, 17633795}, {64169, 17634051}, {64170, 17634307}, - {64171, 17634563}, {64172, 17634819}, {64173, 17617411}, {64174, 17635075}, - {64175, 17635331}, {64176, 17575171}, {64177, 17635587}, {64178, 17618435}, - {64179, 17635843}, {64180, 17636099}, {64181, 17636355}, {64182, 17636611}, - {64183, 17636867}, {64184, 17619715}, {64185, 17637123}, {64186, 17606147}, - {64187, 17637379}, {64188, 17619971}, {64189, 17561347}, {64190, 17637635}, - {64191, 17620227}, {64192, 17637891}, {64193, 17620739}, {64194, 17638147}, - {64195, 17638403}, {64196, 17638659}, {64197, 17638915}, {64198, 17639171}, - {64199, 17621251}, {64200, 17605379}, {64201, 17639427}, {64202, 17621507}, - {64203, 17639683}, {64204, 17621763}, {64205, 17639939}, {64206, 17196035}, - {64207, 17640195}, {64208, 17640451}, {64209, 17640707}, {64210, 17640963}, - {64211, 17641219}, {64212, 17641475}, {64213, 17641731}, {64214, 17641987}, - {64215, 17642243}, {64216, 17642499}, {64217, 17642755}, {64218, 2}, - {64256, 34420227}, {64257, 34420739}, {64258, 34421251}, {64259, 51197699}, - {64260, 51198979}, {64261, 33559043}, {64263, 2}, {64275, 34422531}, - {64276, 34423043}, {64277, 34423555}, {64278, 34424067}, {64279, 34424579}, - {64280, 2}, {64285, 34425091}, {64286, 1}, {64287, 34425603}, - {64288, 17648899}, {64289, 17044227}, {64290, 17044995}, {64291, 17649155}, - {64292, 17649411}, {64293, 17649667}, {64294, 17649923}, {64295, 17650179}, - {64296, 17650435}, {64297, 17037059}, {64298, 34427907}, {64299, 34428419}, - {64300, 51206147}, {64301, 51206915}, {64302, 34430467}, {64303, 34430979}, - {64304, 34431491}, {64305, 34432003}, {64306, 34432515}, {64307, 34433027}, - {64308, 34433539}, {64309, 34434051}, {64310, 34434563}, {64311, 2}, - {64312, 34435075}, {64313, 34435587}, {64314, 34436099}, {64315, 34436611}, - {64316, 34437123}, {64317, 2}, {64318, 34437635}, {64319, 2}, - {64320, 34438147}, {64321, 34438659}, {64322, 2}, {64323, 34439171}, - {64324, 34439683}, {64325, 2}, {64326, 34440195}, {64327, 34440707}, - {64328, 34441219}, {64329, 34428931}, {64330, 34441731}, {64331, 34442243}, - {64332, 34442755}, {64333, 34443267}, {64334, 34443779}, {64335, 34444291}, - {64336, 17667587}, {64338, 17667843}, {64342, 17668099}, {64346, 17668355}, - {64350, 17668611}, {64354, 17668867}, {64358, 17669123}, {64362, 17669379}, - {64366, 17669635}, {64370, 17669891}, {64374, 17670147}, {64378, 17670403}, - {64382, 17670659}, {64386, 17670915}, {64388, 17671171}, {64390, 17671427}, - {64392, 17671683}, {64394, 17671939}, {64396, 17672195}, {64398, 17672451}, - {64402, 17672707}, {64406, 17672963}, {64410, 17673219}, {64414, 17673475}, - {64416, 17673731}, {64420, 17673987}, {64422, 17674243}, {64426, 17674499}, - {64430, 17674755}, {64432, 17675011}, {64434, 1}, {64451, 2}, - {64467, 17675267}, {64471, 16911363}, {64473, 17675523}, {64475, 17675779}, - {64477, 33688579}, {64478, 17676035}, {64480, 17676291}, {64482, 17676547}, - {64484, 17676803}, {64488, 17677059}, {64490, 34454531}, {64492, 34455043}, - {64494, 34455555}, {64496, 34456067}, {64498, 34456579}, {64500, 34457091}, - {64502, 34457603}, {64505, 34458115}, {64508, 17681411}, {64512, 34458883}, - {64513, 34459395}, {64514, 34459907}, {64515, 34458115}, {64516, 34460419}, - {64517, 34460931}, {64518, 34461443}, {64519, 34461955}, {64520, 34462467}, - {64521, 34462979}, {64522, 34463491}, {64523, 34464003}, {64524, 34464515}, - {64525, 34465027}, {64526, 34465539}, {64527, 34466051}, {64528, 34466563}, - {64529, 34467075}, {64530, 34467587}, {64531, 34468099}, {64532, 34468611}, - {64533, 34469123}, {64534, 34469635}, {64535, 34469379}, {64536, 34470147}, - {64537, 34470659}, {64538, 34471171}, {64539, 34471683}, {64540, 34472195}, - {64541, 34472707}, {64542, 34473219}, {64543, 34473731}, {64544, 34474243}, - {64545, 34474755}, {64546, 34475267}, {64547, 34475779}, {64548, 34476291}, - {64549, 34476803}, {64550, 34477315}, {64551, 34477827}, {64552, 34478339}, - {64553, 34478851}, {64554, 34479363}, {64555, 34479875}, {64556, 34480387}, - {64557, 34480899}, {64558, 34481411}, {64559, 34481923}, {64560, 34482435}, - {64561, 34482947}, {64562, 34483459}, {64563, 34483971}, {64564, 34484483}, - {64565, 34484995}, {64566, 34485507}, {64567, 34486019}, {64568, 34486531}, - {64569, 34487043}, {64570, 34487555}, {64571, 34488067}, {64572, 34488579}, - {64573, 34489091}, {64574, 34489603}, {64575, 34490115}, {64576, 34490627}, - {64577, 34491139}, {64578, 34491651}, {64579, 34492163}, {64580, 34492675}, - {64581, 34493187}, {64582, 34469891}, {64583, 34470403}, {64584, 34493699}, - {64585, 34494211}, {64586, 34494723}, {64587, 34495235}, {64588, 34495747}, - {64589, 34496259}, {64590, 34496771}, {64591, 34497283}, {64592, 34497795}, - {64593, 34498307}, {64594, 34498819}, {64595, 34499331}, {64596, 34499843}, - {64597, 34468867}, {64598, 34500355}, {64599, 34500867}, {64600, 34492931}, - {64601, 34501379}, {64602, 34500099}, {64603, 34501891}, {64604, 34502403}, - {64605, 34502915}, {64606, 51280643}, {64607, 51281411}, {64608, 51282179}, - {64609, 51282947}, {64610, 51283715}, {64611, 51284483}, {64612, 34508035}, - {64613, 34508547}, {64614, 34459907}, {64615, 34509059}, {64616, 34458115}, - {64617, 34460419}, {64618, 34509571}, {64619, 34510083}, {64620, 34462467}, - {64621, 34510595}, {64622, 34462979}, {64623, 34463491}, {64624, 34511107}, - {64625, 34511619}, {64626, 34465539}, {64627, 34512131}, {64628, 34466051}, - {64629, 34466563}, {64630, 34512643}, {64631, 34513155}, {64632, 34467587}, - {64633, 34513667}, {64634, 34468099}, {64635, 34468611}, {64636, 34482947}, - {64637, 34483459}, {64638, 34484995}, {64639, 34485507}, {64640, 34486019}, - {64641, 34488067}, {64642, 34488579}, {64643, 34489091}, {64644, 34489603}, - {64645, 34491651}, {64646, 34492163}, {64647, 34492675}, {64648, 34514179}, - {64649, 34493699}, {64650, 34514691}, {64651, 34515203}, {64652, 34496771}, - {64653, 34515715}, {64654, 34497283}, {64655, 34497795}, {64656, 34502915}, - {64657, 34516227}, {64658, 34516739}, {64659, 34492931}, {64660, 34494979}, - {64661, 34501379}, {64662, 34500099}, {64663, 34458883}, {64664, 34459395}, - {64665, 34517251}, {64666, 34459907}, {64667, 34517763}, {64668, 34460931}, - {64669, 34461443}, {64670, 34461955}, {64671, 34462467}, {64672, 34518275}, - {64673, 34464003}, {64674, 34464515}, {64675, 34465027}, {64676, 34465539}, - {64677, 34518787}, {64678, 34467587}, {64679, 34469123}, {64680, 34469635}, - {64681, 34469379}, {64682, 34470147}, {64683, 34470659}, {64684, 34471683}, - {64685, 34472195}, {64686, 34472707}, {64687, 34473219}, {64688, 34473731}, - {64689, 34474243}, {64690, 34519299}, {64691, 34474755}, {64692, 34475267}, - {64693, 34475779}, {64694, 34476291}, {64695, 34476803}, {64696, 34477315}, - {64697, 34478339}, {64698, 34478851}, {64699, 34479363}, {64700, 34479875}, - {64701, 34480387}, {64702, 34480899}, {64703, 34481411}, {64704, 34481923}, - {64705, 34482435}, {64706, 34483971}, {64707, 34484483}, {64708, 34486531}, - {64709, 34487043}, {64710, 34487555}, {64711, 34488067}, {64712, 34488579}, - {64713, 34490115}, {64714, 34490627}, {64715, 34491139}, {64716, 34491651}, - {64717, 34519811}, {64718, 34493187}, {64719, 34469891}, {64720, 34470403}, - {64721, 34493699}, {64722, 34495235}, {64723, 34495747}, {64724, 34496259}, - {64725, 34496771}, {64726, 34520323}, {64727, 34498307}, {64728, 34498819}, - {64729, 34520835}, {64730, 34468867}, {64731, 34500355}, {64732, 34500867}, - {64733, 34492931}, {64734, 34498051}, {64735, 34459907}, {64736, 34517763}, - {64737, 34462467}, {64738, 34518275}, {64739, 34465539}, {64740, 34518787}, - {64741, 34467587}, {64742, 34521347}, {64743, 34473731}, {64744, 34521859}, - {64745, 34522371}, {64746, 34522883}, {64747, 34488067}, {64748, 34488579}, - {64749, 34491651}, {64750, 34496771}, {64751, 34520323}, {64752, 34492931}, - {64753, 34498051}, {64754, 51300611}, {64755, 51301379}, {64756, 51302147}, - {64757, 34525699}, {64758, 34526211}, {64759, 34526723}, {64760, 34527235}, - {64761, 34527747}, {64762, 34528259}, {64763, 34528771}, {64764, 34529283}, - {64765, 34529795}, {64766, 34530307}, {64767, 34530819}, {64768, 34500611}, - {64769, 34531331}, {64770, 34531843}, {64771, 34532355}, {64772, 34501123}, - {64773, 34532867}, {64774, 34533379}, {64775, 34533891}, {64776, 34534403}, - {64777, 34534915}, {64778, 34535427}, {64779, 34535939}, {64780, 34522371}, - {64781, 34536451}, {64782, 34536963}, {64783, 34537475}, {64784, 34537987}, - {64785, 34525699}, {64786, 34526211}, {64787, 34526723}, {64788, 34527235}, - {64789, 34527747}, {64790, 34528259}, {64791, 34528771}, {64792, 34529283}, - {64793, 34529795}, {64794, 34530307}, {64795, 34530819}, {64796, 34500611}, - {64797, 34531331}, {64798, 34531843}, {64799, 34532355}, {64800, 34501123}, - {64801, 34532867}, {64802, 34533379}, {64803, 34533891}, {64804, 34534403}, - {64805, 34534915}, {64806, 34535427}, {64807, 34535939}, {64808, 34522371}, - {64809, 34536451}, {64810, 34536963}, {64811, 34537475}, {64812, 34537987}, - {64813, 34534915}, {64814, 34535427}, {64815, 34535939}, {64816, 34522371}, - {64817, 34521859}, {64818, 34522883}, {64819, 34477827}, {64820, 34472195}, - {64821, 34472707}, {64822, 34473219}, {64823, 34534915}, {64824, 34535427}, - {64825, 34535939}, {64826, 34477827}, {64827, 34478339}, {64828, 34538499}, - {64830, 1}, {64848, 51316227}, {64849, 51316995}, {64851, 51317763}, - {64852, 51318531}, {64853, 51319299}, {64854, 51320067}, {64855, 51320835}, - {64856, 51246851}, {64858, 51321603}, {64859, 51322371}, {64860, 51323139}, - {64861, 51323907}, {64862, 51324675}, {64863, 51325443}, {64865, 51326211}, - {64866, 51326979}, {64868, 51327747}, {64870, 51328515}, {64871, 51329283}, - {64873, 51330051}, {64874, 51330819}, {64876, 51331587}, {64878, 51332355}, - {64879, 51333123}, {64881, 51333891}, {64883, 51334659}, {64884, 51335427}, - {64885, 51336195}, {64886, 51336963}, {64888, 51337731}, {64889, 51338499}, - {64890, 51339267}, {64891, 51340035}, {64892, 51340803}, {64894, 51341571}, - {64895, 51342339}, {64896, 51343107}, {64897, 51343875}, {64898, 51344643}, - {64899, 51345411}, {64901, 51346179}, {64903, 51346947}, {64905, 51347715}, - {64906, 51247107}, {64907, 51348483}, {64908, 51349251}, {64909, 51270403}, - {64910, 51247619}, {64911, 51350019}, {64912, 2}, {64914, 51350787}, - {64915, 51351555}, {64916, 51352323}, {64917, 51353091}, {64918, 51353859}, - {64919, 51354627}, {64921, 51355395}, {64922, 51356163}, {64923, 51356931}, - {64924, 51357699}, {64926, 51358467}, {64927, 51359235}, {64928, 51360003}, - {64929, 51360771}, {64930, 51361539}, {64931, 51362307}, {64932, 51363075}, - {64933, 51363843}, {64934, 51364611}, {64935, 51365379}, {64936, 51366147}, - {64937, 51366915}, {64938, 51367683}, {64939, 51368451}, {64940, 51369219}, - {64941, 51369987}, {64942, 51277571}, {64943, 51370755}, {64944, 51371523}, - {64945, 51372291}, {64946, 51373059}, {64947, 51373827}, {64948, 51341571}, - {64949, 51343107}, {64950, 51374595}, {64951, 51375363}, {64952, 51376131}, - {64953, 51376899}, {64954, 51377667}, {64955, 51378435}, {64956, 51377667}, - {64957, 51376131}, {64958, 51379203}, {64959, 51379971}, {64960, 51380739}, - {64961, 51381507}, {64962, 51382275}, {64963, 51378435}, {64964, 51336195}, - {64965, 51328515}, {64966, 51383043}, {64967, 51383811}, {64968, 2}, - {64975, 1}, {64976, 2}, {65008, 51384579}, {65009, 51385347}, - {65010, 68163331}, {65011, 68164355}, {65012, 68165379}, {65013, 68166403}, - {65014, 68167427}, {65015, 68168451}, {65016, 68169475}, {65017, 51393283}, - {65018, 303052291}, {65019, 135284739}, {65020, 68177923}, {65021, 1}, - {65024, 0}, {65040, 17847299}, {65041, 17847555}, {65042, 2}, - {65043, 17110531}, {65044, 16848643}, {65045, 17032963}, {65046, 17033987}, - {65047, 17847811}, {65048, 17848067}, {65049, 2}, {65056, 1}, - {65072, 2}, {65073, 17848323}, {65074, 17848579}, {65075, 17848835}, - {65077, 17037827}, {65078, 17038083}, {65079, 17849091}, {65080, 17849347}, - {65081, 17849603}, {65082, 17849859}, {65083, 17850115}, {65084, 17850371}, - {65085, 17850627}, {65086, 17850883}, {65087, 17067267}, {65088, 17067523}, - {65089, 17851139}, {65090, 17851395}, {65091, 17851651}, {65092, 17851907}, - {65093, 1}, {65095, 17852163}, {65096, 17852419}, {65097, 33810691}, - {65101, 17848835}, {65104, 17847299}, {65105, 17847555}, {65106, 2}, - {65108, 16848643}, {65109, 17110531}, {65110, 17033987}, {65111, 17032963}, - {65112, 17848323}, {65113, 17037827}, {65114, 17038083}, {65115, 17849091}, - {65116, 17849347}, {65117, 17849603}, {65118, 17849859}, {65119, 17852675}, - {65120, 17852931}, {65121, 17853187}, {65122, 17037059}, {65123, 17853443}, - {65124, 17853699}, {65125, 17853955}, {65126, 17037571}, {65127, 2}, - {65128, 17854211}, {65129, 17854467}, {65130, 17854723}, {65131, 17854979}, - {65132, 2}, {65136, 34632451}, {65137, 34632963}, {65138, 34503427}, - {65139, 1}, {65140, 34504195}, {65141, 2}, {65142, 34504963}, - {65143, 34523395}, {65144, 34505731}, {65145, 34524163}, {65146, 34506499}, - {65147, 34524931}, {65148, 34507267}, {65149, 34633475}, {65150, 34633987}, - {65151, 34634499}, {65152, 17857795}, {65153, 17858051}, {65155, 17858307}, - {65157, 17858563}, {65159, 17858819}, {65161, 17677315}, {65165, 16910339}, - {65167, 17683715}, {65171, 17859075}, {65173, 17686787}, {65177, 17689859}, - {65181, 17681923}, {65185, 17682435}, {65189, 17684995}, {65193, 17834499}, - {65195, 17724675}, {65197, 17725187}, {65199, 17731587}, {65201, 17694979}, - {65205, 17745155}, {65209, 17697027}, {65213, 17698051}, {65217, 17700099}, - {65221, 17701123}, {65225, 17701635}, {65229, 17702659}, {65233, 17703683}, - {65237, 17706755}, {65241, 17708803}, {65245, 17711107}, {65249, 17682947}, - {65253, 17718019}, {65257, 17721091}, {65261, 16910851}, {65263, 17677059}, - {65265, 16911875}, {65269, 34636547}, {65271, 34637059}, {65273, 34637571}, - {65275, 34622467}, {65277, 2}, {65279, 0}, {65280, 2}, - {65281, 17032963}, {65282, 17860867}, {65283, 17852675}, {65284, 17854467}, - {65285, 17854723}, {65286, 17852931}, {65287, 17861123}, {65288, 17037827}, - {65289, 17038083}, {65290, 17853187}, {65291, 17037059}, {65292, 17847299}, - {65293, 17853443}, {65294, 17196547}, {65295, 17038595}, {65296, 17035523}, - {65297, 16786947}, {65298, 16785155}, {65299, 16785411}, {65300, 16787715}, - {65301, 17035779}, {65302, 17036035}, {65303, 17036291}, {65304, 17036547}, - {65305, 17036803}, {65306, 17110531}, {65307, 16848643}, {65308, 17853699}, - {65309, 17037571}, {65310, 17853955}, {65311, 17033987}, {65312, 17854979}, - {65313, 16777219}, {65314, 16777475}, {65315, 16777731}, {65316, 16777987}, - {65317, 16778243}, {65318, 16778499}, {65319, 16778755}, {65320, 16779011}, - {65321, 16779267}, {65322, 16779523}, {65323, 16779779}, {65324, 16780035}, - {65325, 16780291}, {65326, 16780547}, {65327, 16780803}, {65328, 16781059}, - {65329, 16781315}, {65330, 16781571}, {65331, 16781827}, {65332, 16782083}, - {65333, 16782339}, {65334, 16782595}, {65335, 16782851}, {65336, 16783107}, - {65337, 16783363}, {65338, 16783619}, {65339, 17852163}, {65340, 17854211}, - {65341, 17852419}, {65342, 17861379}, {65343, 17848835}, {65344, 17027075}, - {65345, 16777219}, {65346, 16777475}, {65347, 16777731}, {65348, 16777987}, - {65349, 16778243}, {65350, 16778499}, {65351, 16778755}, {65352, 16779011}, - {65353, 16779267}, {65354, 16779523}, {65355, 16779779}, {65356, 16780035}, - {65357, 16780291}, {65358, 16780547}, {65359, 16780803}, {65360, 16781059}, - {65361, 16781315}, {65362, 16781571}, {65363, 16781827}, {65364, 16782083}, - {65365, 16782339}, {65366, 16782595}, {65367, 16782851}, {65368, 16783107}, - {65369, 16783363}, {65370, 16783619}, {65371, 17849091}, {65372, 17861635}, - {65373, 17849347}, {65374, 17861891}, {65375, 17862147}, {65376, 17862403}, - {65377, 17196547}, {65378, 17851139}, {65379, 17851395}, {65380, 17847555}, - {65381, 17862659}, {65382, 17316867}, {65383, 17319427}, {65384, 17362435}, - {65385, 17862915}, {65386, 17363971}, {65387, 17323523}, {65388, 17863171}, - {65389, 17333763}, {65390, 17379587}, {65391, 17329155}, {65392, 17318147}, - {65393, 17305603}, {65394, 17305859}, {65395, 17306115}, {65396, 17306371}, - {65397, 17306627}, {65398, 17306883}, {65399, 17307139}, {65400, 17307395}, - {65401, 17307651}, {65402, 17199107}, {65403, 17307907}, {65404, 17308163}, - {65405, 17308419}, {65406, 17308675}, {65407, 17308931}, {65408, 17309187}, - {65409, 17309443}, {65410, 17309699}, {65411, 17309955}, {65412, 17199363}, - {65413, 17310211}, {65414, 17310467}, {65415, 17310723}, {65416, 17310979}, - {65417, 17311235}, {65418, 17311491}, {65419, 17311747}, {65420, 17312003}, - {65421, 17312259}, {65422, 17312515}, {65423, 17312771}, {65424, 17313027}, - {65425, 17313283}, {65426, 17313539}, {65427, 17313795}, {65428, 17314051}, - {65429, 17314307}, {65430, 17314563}, {65431, 17314819}, {65432, 17315075}, - {65433, 17315331}, {65434, 17315587}, {65435, 17315843}, {65436, 17316099}, - {65437, 17319939}, {65438, 17197827}, {65439, 17198339}, {65440, 2}, - {65441, 17199619}, {65442, 17199875}, {65443, 17200131}, {65444, 17200387}, - {65445, 17200643}, {65446, 17200899}, {65447, 17201155}, {65448, 17201411}, - {65449, 17201667}, {65450, 17201923}, {65451, 17202179}, {65452, 17202435}, - {65453, 17202691}, {65454, 17202947}, {65455, 17203203}, {65456, 17203459}, - {65457, 17203715}, {65458, 17203971}, {65459, 17204227}, {65460, 17204483}, - {65461, 17204739}, {65462, 17204995}, {65463, 17205251}, {65464, 17205507}, - {65465, 17205763}, {65466, 17206019}, {65467, 17206275}, {65468, 17206531}, - {65469, 17206787}, {65470, 17207043}, {65471, 2}, {65474, 17207299}, - {65475, 17207555}, {65476, 17207811}, {65477, 17208067}, {65478, 17208323}, - {65479, 17208579}, {65480, 2}, {65482, 17208835}, {65483, 17209091}, - {65484, 17209347}, {65485, 17209603}, {65486, 17209859}, {65487, 17210115}, - {65488, 2}, {65490, 17210371}, {65491, 17210627}, {65492, 17210883}, - {65493, 17211139}, {65494, 17211395}, {65495, 17211651}, {65496, 2}, - {65498, 17211907}, {65499, 17212163}, {65500, 17212419}, {65501, 2}, - {65504, 17863427}, {65505, 17863683}, {65506, 17863939}, {65507, 33561859}, - {65508, 17864195}, {65509, 17864451}, {65510, 17864707}, {65511, 2}, - {65512, 17864963}, {65513, 17865219}, {65514, 17865475}, {65515, 17865731}, - {65516, 17865987}, {65517, 17866243}, {65518, 17866499}, {65519, 2}, - {65536, 1}, {65548, 2}, {65549, 1}, {65575, 2}, - {65576, 1}, {65595, 2}, {65596, 1}, {65598, 2}, - {65599, 1}, {65614, 2}, {65616, 1}, {65630, 2}, - {65664, 1}, {65787, 2}, {65792, 1}, {65795, 2}, - {65799, 1}, {65844, 2}, {65847, 1}, {65935, 2}, - {65936, 1}, {65949, 2}, {65952, 1}, {65953, 2}, - {66000, 1}, {66046, 2}, {66176, 1}, {66205, 2}, - {66208, 1}, {66257, 2}, {66272, 1}, {66300, 2}, - {66304, 1}, {66340, 2}, {66349, 1}, {66379, 2}, - {66384, 1}, {66427, 2}, {66432, 1}, {66462, 2}, - {66463, 1}, {66500, 2}, {66504, 1}, {66518, 2}, - {66560, 17866755}, {66561, 17867011}, {66562, 17867267}, {66563, 17867523}, - {66564, 17867779}, {66565, 17868035}, {66566, 17868291}, {66567, 17868547}, - {66568, 17868803}, {66569, 17869059}, {66570, 17869315}, {66571, 17869571}, - {66572, 17869827}, {66573, 17870083}, {66574, 17870339}, {66575, 17870595}, - {66576, 17870851}, {66577, 17871107}, {66578, 17871363}, {66579, 17871619}, - {66580, 17871875}, {66581, 17872131}, {66582, 17872387}, {66583, 17872643}, - {66584, 17872899}, {66585, 17873155}, {66586, 17873411}, {66587, 17873667}, - {66588, 17873923}, {66589, 17874179}, {66590, 17874435}, {66591, 17874691}, - {66592, 17874947}, {66593, 17875203}, {66594, 17875459}, {66595, 17875715}, - {66596, 17875971}, {66597, 17876227}, {66598, 17876483}, {66599, 17876739}, - {66600, 1}, {66718, 2}, {66720, 1}, {66730, 2}, - {66736, 17876995}, {66737, 17877251}, {66738, 17877507}, {66739, 17877763}, - {66740, 17878019}, {66741, 17878275}, {66742, 17878531}, {66743, 17878787}, - {66744, 17879043}, {66745, 17879299}, {66746, 17879555}, {66747, 17879811}, - {66748, 17880067}, {66749, 17880323}, {66750, 17880579}, {66751, 17880835}, - {66752, 17881091}, {66753, 17881347}, {66754, 17881603}, {66755, 17881859}, - {66756, 17882115}, {66757, 17882371}, {66758, 17882627}, {66759, 17882883}, - {66760, 17883139}, {66761, 17883395}, {66762, 17883651}, {66763, 17883907}, - {66764, 17884163}, {66765, 17884419}, {66766, 17884675}, {66767, 17884931}, - {66768, 17885187}, {66769, 17885443}, {66770, 17885699}, {66771, 17885955}, - {66772, 2}, {66776, 1}, {66812, 2}, {66816, 1}, - {66856, 2}, {66864, 1}, {66916, 2}, {66927, 1}, - {66928, 17886211}, {66929, 17886467}, {66930, 17886723}, {66931, 17886979}, - {66932, 17887235}, {66933, 17887491}, {66934, 17887747}, {66935, 17888003}, - {66936, 17888259}, {66937, 17888515}, {66938, 17888771}, {66939, 2}, - {66940, 17889027}, {66941, 17889283}, {66942, 17889539}, {66943, 17889795}, - {66944, 17890051}, {66945, 17890307}, {66946, 17890563}, {66947, 17890819}, - {66948, 17891075}, {66949, 17891331}, {66950, 17891587}, {66951, 17891843}, - {66952, 17892099}, {66953, 17892355}, {66954, 17892611}, {66955, 2}, - {66956, 17892867}, {66957, 17893123}, {66958, 17893379}, {66959, 17893635}, - {66960, 17893891}, {66961, 17894147}, {66962, 17894403}, {66963, 2}, - {66964, 17894659}, {66965, 17894915}, {66966, 2}, {66967, 1}, - {66978, 2}, {66979, 1}, {66994, 2}, {66995, 1}, - {67002, 2}, {67003, 1}, {67005, 2}, {67072, 1}, - {67383, 2}, {67392, 1}, {67414, 2}, {67424, 1}, - {67432, 2}, {67456, 1}, {67457, 17895171}, {67458, 17895427}, - {67459, 16791043}, {67460, 17895683}, {67461, 16814083}, {67462, 2}, - {67463, 17895939}, {67464, 17896195}, {67465, 17896451}, {67466, 17896707}, - {67467, 16815363}, {67468, 16815619}, {67469, 17896963}, {67470, 17897219}, - {67471, 17897475}, {67472, 17897731}, {67473, 17897987}, {67474, 17898243}, - {67475, 16817155}, {67476, 17898499}, {67477, 16802051}, {67478, 17898755}, - {67479, 17899011}, {67480, 17899267}, {67481, 17899523}, {67482, 17899779}, - {67483, 17512963}, {67484, 17900035}, {67485, 17900291}, {67486, 17900547}, - {67487, 17900803}, {67488, 17901059}, {67489, 17901315}, {67490, 16795395}, - {67491, 17901571}, {67492, 17901827}, {67493, 16781315}, {67494, 17902083}, - {67495, 17902339}, {67496, 17125379}, {67497, 17902595}, {67498, 16819971}, - {67499, 17902851}, {67500, 17903107}, {67501, 17903363}, {67502, 17903619}, - {67503, 16820995}, {67504, 17903875}, {67505, 2}, {67506, 17904131}, - {67507, 17904387}, {67508, 17904643}, {67509, 17904899}, {67510, 17905155}, - {67511, 17905411}, {67512, 17905667}, {67513, 17905923}, {67514, 17906179}, - {67515, 2}, {67584, 1}, {67590, 2}, {67592, 1}, - {67593, 2}, {67594, 1}, {67638, 2}, {67639, 1}, - {67641, 2}, {67644, 1}, {67645, 2}, {67647, 1}, - {67670, 2}, {67671, 1}, {67743, 2}, {67751, 1}, - {67760, 2}, {67808, 1}, {67827, 2}, {67828, 1}, - {67830, 2}, {67835, 1}, {67868, 2}, {67871, 1}, - {67898, 2}, {67903, 1}, {67904, 2}, {67968, 1}, - {68024, 2}, {68028, 1}, {68048, 2}, {68050, 1}, - {68100, 2}, {68101, 1}, {68103, 2}, {68108, 1}, - {68116, 2}, {68117, 1}, {68120, 2}, {68121, 1}, - {68150, 2}, {68152, 1}, {68155, 2}, {68159, 1}, - {68169, 2}, {68176, 1}, {68185, 2}, {68192, 1}, - {68256, 2}, {68288, 1}, {68327, 2}, {68331, 1}, - {68343, 2}, {68352, 1}, {68406, 2}, {68409, 1}, - {68438, 2}, {68440, 1}, {68467, 2}, {68472, 1}, - {68498, 2}, {68505, 1}, {68509, 2}, {68521, 1}, - {68528, 2}, {68608, 1}, {68681, 2}, {68736, 17906435}, - {68737, 17906691}, {68738, 17906947}, {68739, 17907203}, {68740, 17907459}, - {68741, 17907715}, {68742, 17907971}, {68743, 17908227}, {68744, 17908483}, - {68745, 17908739}, {68746, 17908995}, {68747, 17909251}, {68748, 17909507}, - {68749, 17909763}, {68750, 17910019}, {68751, 17910275}, {68752, 17910531}, - {68753, 17910787}, {68754, 17911043}, {68755, 17911299}, {68756, 17911555}, - {68757, 17911811}, {68758, 17912067}, {68759, 17912323}, {68760, 17912579}, - {68761, 17912835}, {68762, 17913091}, {68763, 17913347}, {68764, 17913603}, - {68765, 17913859}, {68766, 17914115}, {68767, 17914371}, {68768, 17914627}, - {68769, 17914883}, {68770, 17915139}, {68771, 17915395}, {68772, 17915651}, - {68773, 17915907}, {68774, 17916163}, {68775, 17916419}, {68776, 17916675}, - {68777, 17916931}, {68778, 17917187}, {68779, 17917443}, {68780, 17917699}, - {68781, 17917955}, {68782, 17918211}, {68783, 17918467}, {68784, 17918723}, - {68785, 17918979}, {68786, 17919235}, {68787, 2}, {68800, 1}, - {68851, 2}, {68858, 1}, {68904, 2}, {68912, 1}, - {68922, 2}, {69216, 1}, {69247, 2}, {69248, 1}, - {69290, 2}, {69291, 1}, {69294, 2}, {69296, 1}, - {69298, 2}, {69373, 1}, {69416, 2}, {69424, 1}, - {69466, 2}, {69488, 1}, {69514, 2}, {69552, 1}, - {69580, 2}, {69600, 1}, {69623, 2}, {69632, 1}, - {69710, 2}, {69714, 1}, {69750, 2}, {69759, 1}, - {69821, 2}, {69822, 1}, {69827, 2}, {69840, 1}, - {69865, 2}, {69872, 1}, {69882, 2}, {69888, 1}, - {69941, 2}, {69942, 1}, {69960, 2}, {69968, 1}, - {70007, 2}, {70016, 1}, {70112, 2}, {70113, 1}, - {70133, 2}, {70144, 1}, {70162, 2}, {70163, 1}, - {70210, 2}, {70272, 1}, {70279, 2}, {70280, 1}, - {70281, 2}, {70282, 1}, {70286, 2}, {70287, 1}, - {70302, 2}, {70303, 1}, {70314, 2}, {70320, 1}, - {70379, 2}, {70384, 1}, {70394, 2}, {70400, 1}, - {70404, 2}, {70405, 1}, {70413, 2}, {70415, 1}, - {70417, 2}, {70419, 1}, {70441, 2}, {70442, 1}, - {70449, 2}, {70450, 1}, {70452, 2}, {70453, 1}, - {70458, 2}, {70459, 1}, {70469, 2}, {70471, 1}, - {70473, 2}, {70475, 1}, {70478, 2}, {70480, 1}, - {70481, 2}, {70487, 1}, {70488, 2}, {70493, 1}, - {70500, 2}, {70502, 1}, {70509, 2}, {70512, 1}, - {70517, 2}, {70656, 1}, {70748, 2}, {70749, 1}, - {70754, 2}, {70784, 1}, {70856, 2}, {70864, 1}, - {70874, 2}, {71040, 1}, {71094, 2}, {71096, 1}, - {71134, 2}, {71168, 1}, {71237, 2}, {71248, 1}, - {71258, 2}, {71264, 1}, {71277, 2}, {71296, 1}, - {71354, 2}, {71360, 1}, {71370, 2}, {71424, 1}, - {71451, 2}, {71453, 1}, {71468, 2}, {71472, 1}, - {71495, 2}, {71680, 1}, {71740, 2}, {71840, 17919491}, - {71841, 17919747}, {71842, 17920003}, {71843, 17920259}, {71844, 17920515}, - {71845, 17920771}, {71846, 17921027}, {71847, 17921283}, {71848, 17921539}, - {71849, 17921795}, {71850, 17922051}, {71851, 17922307}, {71852, 17922563}, - {71853, 17922819}, {71854, 17923075}, {71855, 17923331}, {71856, 17923587}, - {71857, 17923843}, {71858, 17924099}, {71859, 17924355}, {71860, 17924611}, - {71861, 17924867}, {71862, 17925123}, {71863, 17925379}, {71864, 17925635}, - {71865, 17925891}, {71866, 17926147}, {71867, 17926403}, {71868, 17926659}, - {71869, 17926915}, {71870, 17927171}, {71871, 17927427}, {71872, 1}, - {71923, 2}, {71935, 1}, {71943, 2}, {71945, 1}, - {71946, 2}, {71948, 1}, {71956, 2}, {71957, 1}, - {71959, 2}, {71960, 1}, {71990, 2}, {71991, 1}, - {71993, 2}, {71995, 1}, {72007, 2}, {72016, 1}, - {72026, 2}, {72096, 1}, {72104, 2}, {72106, 1}, - {72152, 2}, {72154, 1}, {72165, 2}, {72192, 1}, - {72264, 2}, {72272, 1}, {72355, 2}, {72368, 1}, - {72441, 2}, {72448, 1}, {72458, 2}, {72704, 1}, - {72713, 2}, {72714, 1}, {72759, 2}, {72760, 1}, - {72774, 2}, {72784, 1}, {72813, 2}, {72816, 1}, - {72848, 2}, {72850, 1}, {72872, 2}, {72873, 1}, - {72887, 2}, {72960, 1}, {72967, 2}, {72968, 1}, - {72970, 2}, {72971, 1}, {73015, 2}, {73018, 1}, - {73019, 2}, {73020, 1}, {73022, 2}, {73023, 1}, - {73032, 2}, {73040, 1}, {73050, 2}, {73056, 1}, - {73062, 2}, {73063, 1}, {73065, 2}, {73066, 1}, - {73103, 2}, {73104, 1}, {73106, 2}, {73107, 1}, - {73113, 2}, {73120, 1}, {73130, 2}, {73440, 1}, - {73465, 2}, {73472, 1}, {73489, 2}, {73490, 1}, - {73531, 2}, {73534, 1}, {73562, 2}, {73648, 1}, - {73649, 2}, {73664, 1}, {73714, 2}, {73727, 1}, - {74650, 2}, {74752, 1}, {74863, 2}, {74864, 1}, - {74869, 2}, {74880, 1}, {75076, 2}, {77712, 1}, - {77811, 2}, {77824, 1}, {78896, 2}, {78912, 1}, - {78934, 2}, {82944, 1}, {83527, 2}, {92160, 1}, - {92729, 2}, {92736, 1}, {92767, 2}, {92768, 1}, - {92778, 2}, {92782, 1}, {92863, 2}, {92864, 1}, - {92874, 2}, {92880, 1}, {92910, 2}, {92912, 1}, - {92918, 2}, {92928, 1}, {92998, 2}, {93008, 1}, - {93018, 2}, {93019, 1}, {93026, 2}, {93027, 1}, - {93048, 2}, {93053, 1}, {93072, 2}, {93760, 17927683}, - {93761, 17927939}, {93762, 17928195}, {93763, 17928451}, {93764, 17928707}, - {93765, 17928963}, {93766, 17929219}, {93767, 17929475}, {93768, 17929731}, - {93769, 17929987}, {93770, 17930243}, {93771, 17930499}, {93772, 17930755}, - {93773, 17931011}, {93774, 17931267}, {93775, 17931523}, {93776, 17931779}, - {93777, 17932035}, {93778, 17932291}, {93779, 17932547}, {93780, 17932803}, - {93781, 17933059}, {93782, 17933315}, {93783, 17933571}, {93784, 17933827}, - {93785, 17934083}, {93786, 17934339}, {93787, 17934595}, {93788, 17934851}, - {93789, 17935107}, {93790, 17935363}, {93791, 17935619}, {93792, 1}, - {93851, 2}, {93952, 1}, {94027, 2}, {94031, 1}, - {94088, 2}, {94095, 1}, {94112, 2}, {94176, 1}, - {94181, 2}, {94192, 1}, {94194, 2}, {94208, 1}, - {100344, 2}, {100352, 1}, {101590, 2}, {101632, 1}, - {101641, 2}, {110576, 1}, {110580, 2}, {110581, 1}, - {110588, 2}, {110589, 1}, {110591, 2}, {110592, 1}, - {110883, 2}, {110898, 1}, {110899, 2}, {110928, 1}, - {110931, 2}, {110933, 1}, {110934, 2}, {110948, 1}, - {110952, 2}, {110960, 1}, {111356, 2}, {113664, 1}, - {113771, 2}, {113776, 1}, {113789, 2}, {113792, 1}, - {113801, 2}, {113808, 1}, {113818, 2}, {113820, 1}, - {113824, 0}, {113828, 2}, {118528, 1}, {118574, 2}, - {118576, 1}, {118599, 2}, {118608, 1}, {118724, 2}, - {118784, 1}, {119030, 2}, {119040, 1}, {119079, 2}, - {119081, 1}, {119134, 34713091}, {119135, 34713603}, {119136, 51491331}, - {119137, 51492099}, {119138, 51492867}, {119139, 51493635}, {119140, 51494403}, - {119141, 1}, {119155, 2}, {119163, 1}, {119227, 34717955}, - {119228, 34718467}, {119229, 51496195}, {119230, 51496963}, {119231, 51497731}, - {119232, 51498499}, {119233, 1}, {119275, 2}, {119296, 1}, - {119366, 2}, {119488, 1}, {119508, 2}, {119520, 1}, - {119540, 2}, {119552, 1}, {119639, 2}, {119648, 1}, - {119673, 2}, {119808, 16777219}, {119809, 16777475}, {119810, 16777731}, - {119811, 16777987}, {119812, 16778243}, {119813, 16778499}, {119814, 16778755}, - {119815, 16779011}, {119816, 16779267}, {119817, 16779523}, {119818, 16779779}, - {119819, 16780035}, {119820, 16780291}, {119821, 16780547}, {119822, 16780803}, - {119823, 16781059}, {119824, 16781315}, {119825, 16781571}, {119826, 16781827}, - {119827, 16782083}, {119828, 16782339}, {119829, 16782595}, {119830, 16782851}, - {119831, 16783107}, {119832, 16783363}, {119833, 16783619}, {119834, 16777219}, - {119835, 16777475}, {119836, 16777731}, {119837, 16777987}, {119838, 16778243}, - {119839, 16778499}, {119840, 16778755}, {119841, 16779011}, {119842, 16779267}, - {119843, 16779523}, {119844, 16779779}, {119845, 16780035}, {119846, 16780291}, - {119847, 16780547}, {119848, 16780803}, {119849, 16781059}, {119850, 16781315}, - {119851, 16781571}, {119852, 16781827}, {119853, 16782083}, {119854, 16782339}, - {119855, 16782595}, {119856, 16782851}, {119857, 16783107}, {119858, 16783363}, - {119859, 16783619}, {119860, 16777219}, {119861, 16777475}, {119862, 16777731}, - {119863, 16777987}, {119864, 16778243}, {119865, 16778499}, {119866, 16778755}, - {119867, 16779011}, {119868, 16779267}, {119869, 16779523}, {119870, 16779779}, - {119871, 16780035}, {119872, 16780291}, {119873, 16780547}, {119874, 16780803}, - {119875, 16781059}, {119876, 16781315}, {119877, 16781571}, {119878, 16781827}, - {119879, 16782083}, {119880, 16782339}, {119881, 16782595}, {119882, 16782851}, - {119883, 16783107}, {119884, 16783363}, {119885, 16783619}, {119886, 16777219}, - {119887, 16777475}, {119888, 16777731}, {119889, 16777987}, {119890, 16778243}, - {119891, 16778499}, {119892, 16778755}, {119893, 2}, {119894, 16779267}, - {119895, 16779523}, {119896, 16779779}, {119897, 16780035}, {119898, 16780291}, - {119899, 16780547}, {119900, 16780803}, {119901, 16781059}, {119902, 16781315}, - {119903, 16781571}, {119904, 16781827}, {119905, 16782083}, {119906, 16782339}, - {119907, 16782595}, {119908, 16782851}, {119909, 16783107}, {119910, 16783363}, - {119911, 16783619}, {119912, 16777219}, {119913, 16777475}, {119914, 16777731}, - {119915, 16777987}, {119916, 16778243}, {119917, 16778499}, {119918, 16778755}, - {119919, 16779011}, {119920, 16779267}, {119921, 16779523}, {119922, 16779779}, - {119923, 16780035}, {119924, 16780291}, {119925, 16780547}, {119926, 16780803}, - {119927, 16781059}, {119928, 16781315}, {119929, 16781571}, {119930, 16781827}, - {119931, 16782083}, {119932, 16782339}, {119933, 16782595}, {119934, 16782851}, - {119935, 16783107}, {119936, 16783363}, {119937, 16783619}, {119938, 16777219}, - {119939, 16777475}, {119940, 16777731}, {119941, 16777987}, {119942, 16778243}, - {119943, 16778499}, {119944, 16778755}, {119945, 16779011}, {119946, 16779267}, - {119947, 16779523}, {119948, 16779779}, {119949, 16780035}, {119950, 16780291}, - {119951, 16780547}, {119952, 16780803}, {119953, 16781059}, {119954, 16781315}, - {119955, 16781571}, {119956, 16781827}, {119957, 16782083}, {119958, 16782339}, - {119959, 16782595}, {119960, 16782851}, {119961, 16783107}, {119962, 16783363}, - {119963, 16783619}, {119964, 16777219}, {119965, 2}, {119966, 16777731}, - {119967, 16777987}, {119968, 2}, {119970, 16778755}, {119971, 2}, - {119973, 16779523}, {119974, 16779779}, {119975, 2}, {119977, 16780547}, - {119978, 16780803}, {119979, 16781059}, {119980, 16781315}, {119981, 2}, - {119982, 16781827}, {119983, 16782083}, {119984, 16782339}, {119985, 16782595}, - {119986, 16782851}, {119987, 16783107}, {119988, 16783363}, {119989, 16783619}, - {119990, 16777219}, {119991, 16777475}, {119992, 16777731}, {119993, 16777987}, - {119994, 2}, {119995, 16778499}, {119996, 2}, {119997, 16779011}, - {119998, 16779267}, {119999, 16779523}, {120000, 16779779}, {120001, 16780035}, - {120002, 16780291}, {120003, 16780547}, {120004, 2}, {120005, 16781059}, - {120006, 16781315}, {120007, 16781571}, {120008, 16781827}, {120009, 16782083}, - {120010, 16782339}, {120011, 16782595}, {120012, 16782851}, {120013, 16783107}, - {120014, 16783363}, {120015, 16783619}, {120016, 16777219}, {120017, 16777475}, - {120018, 16777731}, {120019, 16777987}, {120020, 16778243}, {120021, 16778499}, - {120022, 16778755}, {120023, 16779011}, {120024, 16779267}, {120025, 16779523}, - {120026, 16779779}, {120027, 16780035}, {120028, 16780291}, {120029, 16780547}, - {120030, 16780803}, {120031, 16781059}, {120032, 16781315}, {120033, 16781571}, - {120034, 16781827}, {120035, 16782083}, {120036, 16782339}, {120037, 16782595}, - {120038, 16782851}, {120039, 16783107}, {120040, 16783363}, {120041, 16783619}, - {120042, 16777219}, {120043, 16777475}, {120044, 16777731}, {120045, 16777987}, - {120046, 16778243}, {120047, 16778499}, {120048, 16778755}, {120049, 16779011}, - {120050, 16779267}, {120051, 16779523}, {120052, 16779779}, {120053, 16780035}, - {120054, 16780291}, {120055, 16780547}, {120056, 16780803}, {120057, 16781059}, - {120058, 16781315}, {120059, 16781571}, {120060, 16781827}, {120061, 16782083}, - {120062, 16782339}, {120063, 16782595}, {120064, 16782851}, {120065, 16783107}, - {120066, 16783363}, {120067, 16783619}, {120068, 16777219}, {120069, 16777475}, - {120070, 2}, {120071, 16777987}, {120072, 16778243}, {120073, 16778499}, - {120074, 16778755}, {120075, 2}, {120077, 16779523}, {120078, 16779779}, - {120079, 16780035}, {120080, 16780291}, {120081, 16780547}, {120082, 16780803}, - {120083, 16781059}, {120084, 16781315}, {120085, 2}, {120086, 16781827}, - {120087, 16782083}, {120088, 16782339}, {120089, 16782595}, {120090, 16782851}, - {120091, 16783107}, {120092, 16783363}, {120093, 2}, {120094, 16777219}, - {120095, 16777475}, {120096, 16777731}, {120097, 16777987}, {120098, 16778243}, - {120099, 16778499}, {120100, 16778755}, {120101, 16779011}, {120102, 16779267}, - {120103, 16779523}, {120104, 16779779}, {120105, 16780035}, {120106, 16780291}, - {120107, 16780547}, {120108, 16780803}, {120109, 16781059}, {120110, 16781315}, - {120111, 16781571}, {120112, 16781827}, {120113, 16782083}, {120114, 16782339}, - {120115, 16782595}, {120116, 16782851}, {120117, 16783107}, {120118, 16783363}, - {120119, 16783619}, {120120, 16777219}, {120121, 16777475}, {120122, 2}, - {120123, 16777987}, {120124, 16778243}, {120125, 16778499}, {120126, 16778755}, - {120127, 2}, {120128, 16779267}, {120129, 16779523}, {120130, 16779779}, - {120131, 16780035}, {120132, 16780291}, {120133, 2}, {120134, 16780803}, - {120135, 2}, {120138, 16781827}, {120139, 16782083}, {120140, 16782339}, - {120141, 16782595}, {120142, 16782851}, {120143, 16783107}, {120144, 16783363}, - {120145, 2}, {120146, 16777219}, {120147, 16777475}, {120148, 16777731}, - {120149, 16777987}, {120150, 16778243}, {120151, 16778499}, {120152, 16778755}, - {120153, 16779011}, {120154, 16779267}, {120155, 16779523}, {120156, 16779779}, - {120157, 16780035}, {120158, 16780291}, {120159, 16780547}, {120160, 16780803}, - {120161, 16781059}, {120162, 16781315}, {120163, 16781571}, {120164, 16781827}, - {120165, 16782083}, {120166, 16782339}, {120167, 16782595}, {120168, 16782851}, - {120169, 16783107}, {120170, 16783363}, {120171, 16783619}, {120172, 16777219}, - {120173, 16777475}, {120174, 16777731}, {120175, 16777987}, {120176, 16778243}, - {120177, 16778499}, {120178, 16778755}, {120179, 16779011}, {120180, 16779267}, - {120181, 16779523}, {120182, 16779779}, {120183, 16780035}, {120184, 16780291}, - {120185, 16780547}, {120186, 16780803}, {120187, 16781059}, {120188, 16781315}, - {120189, 16781571}, {120190, 16781827}, {120191, 16782083}, {120192, 16782339}, - {120193, 16782595}, {120194, 16782851}, {120195, 16783107}, {120196, 16783363}, - {120197, 16783619}, {120198, 16777219}, {120199, 16777475}, {120200, 16777731}, - {120201, 16777987}, {120202, 16778243}, {120203, 16778499}, {120204, 16778755}, - {120205, 16779011}, {120206, 16779267}, {120207, 16779523}, {120208, 16779779}, - {120209, 16780035}, {120210, 16780291}, {120211, 16780547}, {120212, 16780803}, - {120213, 16781059}, {120214, 16781315}, {120215, 16781571}, {120216, 16781827}, - {120217, 16782083}, {120218, 16782339}, {120219, 16782595}, {120220, 16782851}, - {120221, 16783107}, {120222, 16783363}, {120223, 16783619}, {120224, 16777219}, - {120225, 16777475}, {120226, 16777731}, {120227, 16777987}, {120228, 16778243}, - {120229, 16778499}, {120230, 16778755}, {120231, 16779011}, {120232, 16779267}, - {120233, 16779523}, {120234, 16779779}, {120235, 16780035}, {120236, 16780291}, - {120237, 16780547}, {120238, 16780803}, {120239, 16781059}, {120240, 16781315}, - {120241, 16781571}, {120242, 16781827}, {120243, 16782083}, {120244, 16782339}, - {120245, 16782595}, {120246, 16782851}, {120247, 16783107}, {120248, 16783363}, - {120249, 16783619}, {120250, 16777219}, {120251, 16777475}, {120252, 16777731}, - {120253, 16777987}, {120254, 16778243}, {120255, 16778499}, {120256, 16778755}, - {120257, 16779011}, {120258, 16779267}, {120259, 16779523}, {120260, 16779779}, - {120261, 16780035}, {120262, 16780291}, {120263, 16780547}, {120264, 16780803}, - {120265, 16781059}, {120266, 16781315}, {120267, 16781571}, {120268, 16781827}, - {120269, 16782083}, {120270, 16782339}, {120271, 16782595}, {120272, 16782851}, - {120273, 16783107}, {120274, 16783363}, {120275, 16783619}, {120276, 16777219}, - {120277, 16777475}, {120278, 16777731}, {120279, 16777987}, {120280, 16778243}, - {120281, 16778499}, {120282, 16778755}, {120283, 16779011}, {120284, 16779267}, - {120285, 16779523}, {120286, 16779779}, {120287, 16780035}, {120288, 16780291}, - {120289, 16780547}, {120290, 16780803}, {120291, 16781059}, {120292, 16781315}, - {120293, 16781571}, {120294, 16781827}, {120295, 16782083}, {120296, 16782339}, - {120297, 16782595}, {120298, 16782851}, {120299, 16783107}, {120300, 16783363}, - {120301, 16783619}, {120302, 16777219}, {120303, 16777475}, {120304, 16777731}, - {120305, 16777987}, {120306, 16778243}, {120307, 16778499}, {120308, 16778755}, - {120309, 16779011}, {120310, 16779267}, {120311, 16779523}, {120312, 16779779}, - {120313, 16780035}, {120314, 16780291}, {120315, 16780547}, {120316, 16780803}, - {120317, 16781059}, {120318, 16781315}, {120319, 16781571}, {120320, 16781827}, - {120321, 16782083}, {120322, 16782339}, {120323, 16782595}, {120324, 16782851}, - {120325, 16783107}, {120326, 16783363}, {120327, 16783619}, {120328, 16777219}, - {120329, 16777475}, {120330, 16777731}, {120331, 16777987}, {120332, 16778243}, - {120333, 16778499}, {120334, 16778755}, {120335, 16779011}, {120336, 16779267}, - {120337, 16779523}, {120338, 16779779}, {120339, 16780035}, {120340, 16780291}, - {120341, 16780547}, {120342, 16780803}, {120343, 16781059}, {120344, 16781315}, - {120345, 16781571}, {120346, 16781827}, {120347, 16782083}, {120348, 16782339}, - {120349, 16782595}, {120350, 16782851}, {120351, 16783107}, {120352, 16783363}, - {120353, 16783619}, {120354, 16777219}, {120355, 16777475}, {120356, 16777731}, - {120357, 16777987}, {120358, 16778243}, {120359, 16778499}, {120360, 16778755}, - {120361, 16779011}, {120362, 16779267}, {120363, 16779523}, {120364, 16779779}, - {120365, 16780035}, {120366, 16780291}, {120367, 16780547}, {120368, 16780803}, - {120369, 16781059}, {120370, 16781315}, {120371, 16781571}, {120372, 16781827}, - {120373, 16782083}, {120374, 16782339}, {120375, 16782595}, {120376, 16782851}, - {120377, 16783107}, {120378, 16783363}, {120379, 16783619}, {120380, 16777219}, - {120381, 16777475}, {120382, 16777731}, {120383, 16777987}, {120384, 16778243}, - {120385, 16778499}, {120386, 16778755}, {120387, 16779011}, {120388, 16779267}, - {120389, 16779523}, {120390, 16779779}, {120391, 16780035}, {120392, 16780291}, - {120393, 16780547}, {120394, 16780803}, {120395, 16781059}, {120396, 16781315}, - {120397, 16781571}, {120398, 16781827}, {120399, 16782083}, {120400, 16782339}, - {120401, 16782595}, {120402, 16782851}, {120403, 16783107}, {120404, 16783363}, - {120405, 16783619}, {120406, 16777219}, {120407, 16777475}, {120408, 16777731}, - {120409, 16777987}, {120410, 16778243}, {120411, 16778499}, {120412, 16778755}, - {120413, 16779011}, {120414, 16779267}, {120415, 16779523}, {120416, 16779779}, - {120417, 16780035}, {120418, 16780291}, {120419, 16780547}, {120420, 16780803}, - {120421, 16781059}, {120422, 16781315}, {120423, 16781571}, {120424, 16781827}, - {120425, 16782083}, {120426, 16782339}, {120427, 16782595}, {120428, 16782851}, - {120429, 16783107}, {120430, 16783363}, {120431, 16783619}, {120432, 16777219}, - {120433, 16777475}, {120434, 16777731}, {120435, 16777987}, {120436, 16778243}, - {120437, 16778499}, {120438, 16778755}, {120439, 16779011}, {120440, 16779267}, - {120441, 16779523}, {120442, 16779779}, {120443, 16780035}, {120444, 16780291}, - {120445, 16780547}, {120446, 16780803}, {120447, 16781059}, {120448, 16781315}, - {120449, 16781571}, {120450, 16781827}, {120451, 16782083}, {120452, 16782339}, - {120453, 16782595}, {120454, 16782851}, {120455, 16783107}, {120456, 16783363}, - {120457, 16783619}, {120458, 16777219}, {120459, 16777475}, {120460, 16777731}, - {120461, 16777987}, {120462, 16778243}, {120463, 16778499}, {120464, 16778755}, - {120465, 16779011}, {120466, 16779267}, {120467, 16779523}, {120468, 16779779}, - {120469, 16780035}, {120470, 16780291}, {120471, 16780547}, {120472, 16780803}, - {120473, 16781059}, {120474, 16781315}, {120475, 16781571}, {120476, 16781827}, - {120477, 16782083}, {120478, 16782339}, {120479, 16782595}, {120480, 16782851}, - {120481, 16783107}, {120482, 16783363}, {120483, 16783619}, {120484, 17944835}, - {120485, 17945091}, {120486, 2}, {120488, 16851715}, {120489, 16851971}, - {120490, 16852227}, {120491, 16852483}, {120492, 16852739}, {120493, 16852995}, - {120494, 16853251}, {120495, 16853507}, {120496, 16846851}, {120497, 16853763}, - {120498, 16854019}, {120499, 16786179}, {120500, 16854275}, {120501, 16854531}, - {120502, 16854787}, {120503, 16855043}, {120504, 16855299}, {120505, 16853507}, - {120506, 16855555}, {120507, 16855811}, {120508, 16856067}, {120509, 16856323}, - {120510, 16856579}, {120511, 16856835}, {120512, 16857091}, {120513, 17945347}, - {120514, 16851715}, {120515, 16851971}, {120516, 16852227}, {120517, 16852483}, - {120518, 16852739}, {120519, 16852995}, {120520, 16853251}, {120521, 16853507}, - {120522, 16846851}, {120523, 16853763}, {120524, 16854019}, {120525, 16786179}, - {120526, 16854275}, {120527, 16854531}, {120528, 16854787}, {120529, 16855043}, - {120530, 16855299}, {120531, 16855555}, {120533, 16855811}, {120534, 16856067}, - {120535, 16856323}, {120536, 16856579}, {120537, 16856835}, {120538, 16857091}, - {120539, 17945603}, {120540, 16852739}, {120541, 16853507}, {120542, 16853763}, - {120543, 16856323}, {120544, 16855299}, {120545, 16855043}, {120546, 16851715}, - {120547, 16851971}, {120548, 16852227}, {120549, 16852483}, {120550, 16852739}, - {120551, 16852995}, {120552, 16853251}, {120553, 16853507}, {120554, 16846851}, - {120555, 16853763}, {120556, 16854019}, {120557, 16786179}, {120558, 16854275}, - {120559, 16854531}, {120560, 16854787}, {120561, 16855043}, {120562, 16855299}, - {120563, 16853507}, {120564, 16855555}, {120565, 16855811}, {120566, 16856067}, - {120567, 16856323}, {120568, 16856579}, {120569, 16856835}, {120570, 16857091}, - {120571, 17945347}, {120572, 16851715}, {120573, 16851971}, {120574, 16852227}, - {120575, 16852483}, {120576, 16852739}, {120577, 16852995}, {120578, 16853251}, - {120579, 16853507}, {120580, 16846851}, {120581, 16853763}, {120582, 16854019}, - {120583, 16786179}, {120584, 16854275}, {120585, 16854531}, {120586, 16854787}, - {120587, 16855043}, {120588, 16855299}, {120589, 16855555}, {120591, 16855811}, - {120592, 16856067}, {120593, 16856323}, {120594, 16856579}, {120595, 16856835}, - {120596, 16857091}, {120597, 17945603}, {120598, 16852739}, {120599, 16853507}, - {120600, 16853763}, {120601, 16856323}, {120602, 16855299}, {120603, 16855043}, - {120604, 16851715}, {120605, 16851971}, {120606, 16852227}, {120607, 16852483}, - {120608, 16852739}, {120609, 16852995}, {120610, 16853251}, {120611, 16853507}, - {120612, 16846851}, {120613, 16853763}, {120614, 16854019}, {120615, 16786179}, - {120616, 16854275}, {120617, 16854531}, {120618, 16854787}, {120619, 16855043}, - {120620, 16855299}, {120621, 16853507}, {120622, 16855555}, {120623, 16855811}, - {120624, 16856067}, {120625, 16856323}, {120626, 16856579}, {120627, 16856835}, - {120628, 16857091}, {120629, 17945347}, {120630, 16851715}, {120631, 16851971}, - {120632, 16852227}, {120633, 16852483}, {120634, 16852739}, {120635, 16852995}, - {120636, 16853251}, {120637, 16853507}, {120638, 16846851}, {120639, 16853763}, - {120640, 16854019}, {120641, 16786179}, {120642, 16854275}, {120643, 16854531}, - {120644, 16854787}, {120645, 16855043}, {120646, 16855299}, {120647, 16855555}, - {120649, 16855811}, {120650, 16856067}, {120651, 16856323}, {120652, 16856579}, - {120653, 16856835}, {120654, 16857091}, {120655, 17945603}, {120656, 16852739}, - {120657, 16853507}, {120658, 16853763}, {120659, 16856323}, {120660, 16855299}, - {120661, 16855043}, {120662, 16851715}, {120663, 16851971}, {120664, 16852227}, - {120665, 16852483}, {120666, 16852739}, {120667, 16852995}, {120668, 16853251}, - {120669, 16853507}, {120670, 16846851}, {120671, 16853763}, {120672, 16854019}, - {120673, 16786179}, {120674, 16854275}, {120675, 16854531}, {120676, 16854787}, - {120677, 16855043}, {120678, 16855299}, {120679, 16853507}, {120680, 16855555}, - {120681, 16855811}, {120682, 16856067}, {120683, 16856323}, {120684, 16856579}, - {120685, 16856835}, {120686, 16857091}, {120687, 17945347}, {120688, 16851715}, - {120689, 16851971}, {120690, 16852227}, {120691, 16852483}, {120692, 16852739}, - {120693, 16852995}, {120694, 16853251}, {120695, 16853507}, {120696, 16846851}, - {120697, 16853763}, {120698, 16854019}, {120699, 16786179}, {120700, 16854275}, - {120701, 16854531}, {120702, 16854787}, {120703, 16855043}, {120704, 16855299}, - {120705, 16855555}, {120707, 16855811}, {120708, 16856067}, {120709, 16856323}, - {120710, 16856579}, {120711, 16856835}, {120712, 16857091}, {120713, 17945603}, - {120714, 16852739}, {120715, 16853507}, {120716, 16853763}, {120717, 16856323}, - {120718, 16855299}, {120719, 16855043}, {120720, 16851715}, {120721, 16851971}, - {120722, 16852227}, {120723, 16852483}, {120724, 16852739}, {120725, 16852995}, - {120726, 16853251}, {120727, 16853507}, {120728, 16846851}, {120729, 16853763}, - {120730, 16854019}, {120731, 16786179}, {120732, 16854275}, {120733, 16854531}, - {120734, 16854787}, {120735, 16855043}, {120736, 16855299}, {120737, 16853507}, - {120738, 16855555}, {120739, 16855811}, {120740, 16856067}, {120741, 16856323}, - {120742, 16856579}, {120743, 16856835}, {120744, 16857091}, {120745, 17945347}, - {120746, 16851715}, {120747, 16851971}, {120748, 16852227}, {120749, 16852483}, - {120750, 16852739}, {120751, 16852995}, {120752, 16853251}, {120753, 16853507}, - {120754, 16846851}, {120755, 16853763}, {120756, 16854019}, {120757, 16786179}, - {120758, 16854275}, {120759, 16854531}, {120760, 16854787}, {120761, 16855043}, - {120762, 16855299}, {120763, 16855555}, {120765, 16855811}, {120766, 16856067}, - {120767, 16856323}, {120768, 16856579}, {120769, 16856835}, {120770, 16857091}, - {120771, 17945603}, {120772, 16852739}, {120773, 16853507}, {120774, 16853763}, - {120775, 16856323}, {120776, 16855299}, {120777, 16855043}, {120778, 16858627}, - {120780, 2}, {120782, 17035523}, {120783, 16786947}, {120784, 16785155}, - {120785, 16785411}, {120786, 16787715}, {120787, 17035779}, {120788, 17036035}, - {120789, 17036291}, {120790, 17036547}, {120791, 17036803}, {120792, 17035523}, - {120793, 16786947}, {120794, 16785155}, {120795, 16785411}, {120796, 16787715}, - {120797, 17035779}, {120798, 17036035}, {120799, 17036291}, {120800, 17036547}, - {120801, 17036803}, {120802, 17035523}, {120803, 16786947}, {120804, 16785155}, - {120805, 16785411}, {120806, 16787715}, {120807, 17035779}, {120808, 17036035}, - {120809, 17036291}, {120810, 17036547}, {120811, 17036803}, {120812, 17035523}, - {120813, 16786947}, {120814, 16785155}, {120815, 16785411}, {120816, 16787715}, - {120817, 17035779}, {120818, 17036035}, {120819, 17036291}, {120820, 17036547}, - {120821, 17036803}, {120822, 17035523}, {120823, 16786947}, {120824, 16785155}, - {120825, 16785411}, {120826, 16787715}, {120827, 17035779}, {120828, 17036035}, - {120829, 17036291}, {120830, 17036547}, {120831, 17036803}, {120832, 1}, - {121484, 2}, {121499, 1}, {121504, 2}, {121505, 1}, - {121520, 2}, {122624, 1}, {122655, 2}, {122661, 1}, - {122667, 2}, {122880, 1}, {122887, 2}, {122888, 1}, - {122905, 2}, {122907, 1}, {122914, 2}, {122915, 1}, - {122917, 2}, {122918, 1}, {122923, 2}, {122928, 16866563}, - {122929, 16866819}, {122930, 16867075}, {122931, 16867331}, {122932, 16867587}, - {122933, 16867843}, {122934, 16868099}, {122935, 16868355}, {122936, 16868611}, - {122937, 16869123}, {122938, 16869379}, {122939, 16869635}, {122940, 16870147}, - {122941, 16870403}, {122942, 16870659}, {122943, 16870915}, {122944, 16871171}, - {122945, 16871427}, {122946, 16871683}, {122947, 16871939}, {122948, 16872195}, - {122949, 16872451}, {122950, 16872707}, {122951, 16873475}, {122952, 16873987}, - {122953, 16874243}, {122954, 17495299}, {122955, 16888835}, {122956, 16864003}, - {122957, 16864515}, {122958, 16890883}, {122959, 16883715}, {122960, 17945859}, - {122961, 16866563}, {122962, 16866819}, {122963, 16867075}, {122964, 16867331}, - {122965, 16867587}, {122966, 16867843}, {122967, 16868099}, {122968, 16868355}, - {122969, 16868611}, {122970, 16869123}, {122971, 16869379}, {122972, 16870147}, - {122973, 16870403}, {122974, 16870915}, {122975, 16871427}, {122976, 16871683}, - {122977, 16871939}, {122978, 16872195}, {122979, 16872451}, {122980, 16872707}, - {122981, 16873219}, {122982, 16873475}, {122983, 16879875}, {122984, 16864003}, - {122985, 16863747}, {122986, 16866307}, {122987, 16883203}, {122988, 17490435}, - {122989, 16883971}, {122990, 2}, {123023, 1}, {123024, 2}, - {123136, 1}, {123181, 2}, {123184, 1}, {123198, 2}, - {123200, 1}, {123210, 2}, {123214, 1}, {123216, 2}, - {123536, 1}, {123567, 2}, {123584, 1}, {123642, 2}, - {123647, 1}, {123648, 2}, {124112, 1}, {124154, 2}, - {124896, 1}, {124903, 2}, {124904, 1}, {124908, 2}, - {124909, 1}, {124911, 2}, {124912, 1}, {124927, 2}, - {124928, 1}, {125125, 2}, {125127, 1}, {125143, 2}, - {125184, 17946115}, {125185, 17946371}, {125186, 17946627}, {125187, 17946883}, - {125188, 17947139}, {125189, 17947395}, {125190, 17947651}, {125191, 17947907}, - {125192, 17948163}, {125193, 17948419}, {125194, 17948675}, {125195, 17948931}, - {125196, 17949187}, {125197, 17949443}, {125198, 17949699}, {125199, 17949955}, - {125200, 17950211}, {125201, 17950467}, {125202, 17950723}, {125203, 17950979}, - {125204, 17951235}, {125205, 17951491}, {125206, 17951747}, {125207, 17952003}, - {125208, 17952259}, {125209, 17952515}, {125210, 17952771}, {125211, 17953027}, - {125212, 17953283}, {125213, 17953539}, {125214, 17953795}, {125215, 17954051}, - {125216, 17954307}, {125217, 17954563}, {125218, 1}, {125260, 2}, - {125264, 1}, {125274, 2}, {125278, 1}, {125280, 2}, - {126065, 1}, {126133, 2}, {126209, 1}, {126270, 2}, - {126464, 16910339}, {126465, 17683715}, {126466, 17681923}, {126467, 17834499}, - {126468, 2}, {126469, 16910851}, {126470, 17731587}, {126471, 17682435}, - {126472, 17700099}, {126473, 16911875}, {126474, 17708803}, {126475, 17711107}, - {126476, 17682947}, {126477, 17718019}, {126478, 17694979}, {126479, 17701635}, - {126480, 17703683}, {126481, 17697027}, {126482, 17706755}, {126483, 17725187}, - {126484, 17745155}, {126485, 17686787}, {126486, 17689859}, {126487, 17684995}, - {126488, 17724675}, {126489, 17698051}, {126490, 17701123}, {126491, 17702659}, - {126492, 17954819}, {126493, 17673475}, {126494, 17955075}, {126495, 17955331}, - {126496, 2}, {126497, 17683715}, {126498, 17681923}, {126499, 2}, - {126500, 17721091}, {126501, 2}, {126503, 17682435}, {126504, 2}, - {126505, 16911875}, {126506, 17708803}, {126507, 17711107}, {126508, 17682947}, - {126509, 17718019}, {126510, 17694979}, {126511, 17701635}, {126512, 17703683}, - {126513, 17697027}, {126514, 17706755}, {126515, 2}, {126516, 17745155}, - {126517, 17686787}, {126518, 17689859}, {126519, 17684995}, {126520, 2}, - {126521, 17698051}, {126522, 2}, {126523, 17702659}, {126524, 2}, - {126530, 17681923}, {126531, 2}, {126535, 17682435}, {126536, 2}, - {126537, 16911875}, {126538, 2}, {126539, 17711107}, {126540, 2}, - {126541, 17718019}, {126542, 17694979}, {126543, 17701635}, {126544, 2}, - {126545, 17697027}, {126546, 17706755}, {126547, 2}, {126548, 17745155}, - {126549, 2}, {126551, 17684995}, {126552, 2}, {126553, 17698051}, - {126554, 2}, {126555, 17702659}, {126556, 2}, {126557, 17673475}, - {126558, 2}, {126559, 17955331}, {126560, 2}, {126561, 17683715}, - {126562, 17681923}, {126563, 2}, {126564, 17721091}, {126565, 2}, - {126567, 17682435}, {126568, 17700099}, {126569, 16911875}, {126570, 17708803}, - {126571, 2}, {126572, 17682947}, {126573, 17718019}, {126574, 17694979}, - {126575, 17701635}, {126576, 17703683}, {126577, 17697027}, {126578, 17706755}, - {126579, 2}, {126580, 17745155}, {126581, 17686787}, {126582, 17689859}, - {126583, 17684995}, {126584, 2}, {126585, 17698051}, {126586, 17701123}, - {126587, 17702659}, {126588, 17954819}, {126589, 2}, {126590, 17955075}, - {126591, 2}, {126592, 16910339}, {126593, 17683715}, {126594, 17681923}, - {126595, 17834499}, {126596, 17721091}, {126597, 16910851}, {126598, 17731587}, - {126599, 17682435}, {126600, 17700099}, {126601, 16911875}, {126602, 2}, - {126603, 17711107}, {126604, 17682947}, {126605, 17718019}, {126606, 17694979}, - {126607, 17701635}, {126608, 17703683}, {126609, 17697027}, {126610, 17706755}, - {126611, 17725187}, {126612, 17745155}, {126613, 17686787}, {126614, 17689859}, - {126615, 17684995}, {126616, 17724675}, {126617, 17698051}, {126618, 17701123}, - {126619, 17702659}, {126620, 2}, {126625, 17683715}, {126626, 17681923}, - {126627, 17834499}, {126628, 2}, {126629, 16910851}, {126630, 17731587}, - {126631, 17682435}, {126632, 17700099}, {126633, 16911875}, {126634, 2}, - {126635, 17711107}, {126636, 17682947}, {126637, 17718019}, {126638, 17694979}, - {126639, 17701635}, {126640, 17703683}, {126641, 17697027}, {126642, 17706755}, - {126643, 17725187}, {126644, 17745155}, {126645, 17686787}, {126646, 17689859}, - {126647, 17684995}, {126648, 17724675}, {126649, 17698051}, {126650, 17701123}, - {126651, 17702659}, {126652, 2}, {126704, 1}, {126706, 2}, - {126976, 1}, {127020, 2}, {127024, 1}, {127124, 2}, - {127136, 1}, {127151, 2}, {127153, 1}, {127168, 2}, - {127169, 1}, {127184, 2}, {127185, 1}, {127222, 2}, - {127233, 34732803}, {127234, 34733315}, {127235, 34733827}, {127236, 34734339}, - {127237, 34734851}, {127238, 34735363}, {127239, 34735875}, {127240, 34736387}, - {127241, 34736899}, {127242, 34737411}, {127243, 1}, {127248, 50644995}, - {127249, 50645763}, {127250, 50646531}, {127251, 50647299}, {127252, 50648067}, - {127253, 50648835}, {127254, 50649603}, {127255, 50650371}, {127256, 50651139}, - {127257, 50651907}, {127258, 50652675}, {127259, 50653443}, {127260, 50654211}, - {127261, 50654979}, {127262, 50655747}, {127263, 50656515}, {127264, 50657283}, - {127265, 50658051}, {127266, 50658819}, {127267, 50659587}, {127268, 50660355}, - {127269, 50661123}, {127270, 50661891}, {127271, 50662659}, {127272, 50663427}, - {127273, 50664195}, {127274, 51515139}, {127275, 16777731}, {127276, 16781571}, - {127277, 33554947}, {127278, 34738691}, {127279, 1}, {127280, 16777219}, - {127281, 16777475}, {127282, 16777731}, {127283, 16777987}, {127284, 16778243}, - {127285, 16778499}, {127286, 16778755}, {127287, 16779011}, {127288, 16779267}, - {127289, 16779523}, {127290, 16779779}, {127291, 16780035}, {127292, 16780291}, - {127293, 16780547}, {127294, 16780803}, {127295, 16781059}, {127296, 16781315}, - {127297, 16781571}, {127298, 16781827}, {127299, 16782083}, {127300, 16782339}, - {127301, 16782595}, {127302, 16782851}, {127303, 16783107}, {127304, 16783363}, - {127305, 16783619}, {127306, 34739203}, {127307, 34226691}, {127308, 34739715}, - {127309, 33752579}, {127310, 51517443}, {127311, 34740995}, {127312, 1}, - {127338, 34209539}, {127339, 34189571}, {127340, 34741507}, {127341, 1}, - {127376, 34742019}, {127377, 1}, {127406, 2}, {127462, 1}, - {127488, 34742531}, {127489, 34743043}, {127490, 17307907}, {127491, 2}, - {127504, 17157891}, {127505, 17966339}, {127506, 17966595}, {127507, 17351683}, - {127508, 17143299}, {127509, 17966851}, {127510, 17967107}, {127511, 17225475}, - {127512, 17967363}, {127513, 17967619}, {127514, 17967875}, {127515, 17584643}, - {127516, 17968131}, {127517, 17968387}, {127518, 17968643}, {127519, 17968899}, - {127520, 17969155}, {127521, 17969411}, {127522, 17167107}, {127523, 17969667}, - {127524, 17969923}, {127525, 17970179}, {127526, 17970435}, {127527, 17970691}, - {127528, 17970947}, {127529, 17141763}, {127530, 17223427}, {127531, 17971203}, - {127532, 17288707}, {127533, 17224195}, {127534, 17288963}, {127535, 17971459}, - {127536, 17181443}, {127537, 17971715}, {127538, 17971971}, {127539, 17972227}, - {127540, 17972483}, {127541, 17972739}, {127542, 17264387}, {127543, 17160451}, - {127544, 17972995}, {127545, 17973251}, {127546, 17973507}, {127547, 17973763}, - {127548, 2}, {127552, 51528451}, {127553, 51529219}, {127554, 51529987}, - {127555, 51530755}, {127556, 51531523}, {127557, 51532291}, {127558, 51533059}, - {127559, 51533827}, {127560, 51534595}, {127561, 2}, {127568, 17980931}, - {127569, 17981187}, {127570, 2}, {127584, 1}, {127590, 2}, - {127744, 1}, {128728, 2}, {128732, 1}, {128749, 2}, - {128752, 1}, {128765, 2}, {128768, 1}, {128887, 2}, - {128891, 1}, {128986, 2}, {128992, 1}, {129004, 2}, - {129008, 1}, {129009, 2}, {129024, 1}, {129036, 2}, - {129040, 1}, {129096, 2}, {129104, 1}, {129114, 2}, - {129120, 1}, {129160, 2}, {129168, 1}, {129198, 2}, - {129200, 1}, {129202, 2}, {129280, 1}, {129620, 2}, - {129632, 1}, {129646, 2}, {129648, 1}, {129661, 2}, - {129664, 1}, {129673, 2}, {129680, 1}, {129726, 2}, - {129727, 1}, {129734, 2}, {129742, 1}, {129756, 2}, - {129760, 1}, {129769, 2}, {129776, 1}, {129785, 2}, - {129792, 1}, {129939, 2}, {129940, 1}, {129995, 2}, - {130032, 17035523}, {130033, 16786947}, {130034, 16785155}, {130035, 16785411}, - {130036, 16787715}, {130037, 17035779}, {130038, 17036035}, {130039, 17036291}, - {130040, 17036547}, {130041, 17036803}, {130042, 2}, {131072, 1}, + {7304, 16946435}, {7305, 16946691}, {7306, 1}, {7307, 2}, + {7312, 16946947}, {7313, 16947203}, {7314, 16947459}, {7315, 16947715}, + {7316, 16947971}, {7317, 16948227}, {7318, 16948483}, {7319, 16948739}, + {7320, 16948995}, {7321, 16949251}, {7322, 16949507}, {7323, 16949763}, + {7324, 16944643}, {7325, 16950019}, {7326, 16950275}, {7327, 16950531}, + {7328, 16950787}, {7329, 16951043}, {7330, 16951299}, {7331, 16951555}, + {7332, 16951811}, {7333, 16952067}, {7334, 16952323}, {7335, 16952579}, + {7336, 16952835}, {7337, 16953091}, {7338, 16953347}, {7339, 16953603}, + {7340, 16953859}, {7341, 16954115}, {7342, 16954371}, {7343, 16954627}, + {7344, 16954883}, {7345, 16955139}, {7346, 16955395}, {7347, 16955651}, + {7348, 16955907}, {7349, 16956163}, {7350, 16956419}, {7351, 16956675}, + {7352, 16956931}, {7353, 16957187}, {7354, 16957443}, {7355, 2}, + {7357, 16957699}, {7358, 16957955}, {7359, 16958211}, {7360, 1}, + {7368, 2}, {7376, 1}, {7419, 2}, {7424, 1}, + {7468, 16777219}, {7469, 16791043}, {7470, 16777475}, {7471, 1}, + {7472, 16777987}, {7473, 16778243}, {7474, 16816131}, {7475, 16778755}, + {7476, 16779011}, {7477, 16779267}, {7478, 16779523}, {7479, 16779779}, + {7480, 16780035}, {7481, 16780291}, {7482, 16780547}, {7483, 1}, + {7484, 16780803}, {7485, 16835843}, {7486, 16781059}, {7487, 16781571}, + {7488, 16782083}, {7489, 16782339}, {7490, 16782851}, {7491, 16777219}, + {7492, 16958467}, {7493, 16958723}, {7494, 16958979}, {7495, 16777475}, + {7496, 16777987}, {7497, 16778243}, {7498, 16816387}, {7499, 16816643}, + {7500, 16959235}, {7501, 16778755}, {7502, 1}, {7503, 16779779}, + {7504, 16780291}, {7505, 16807171}, {7506, 16780803}, {7507, 16814851}, + {7508, 16959491}, {7509, 16959747}, {7510, 16781059}, {7511, 16782083}, + {7512, 16782339}, {7513, 16960003}, {7514, 16818435}, {7515, 16782595}, + {7516, 16960259}, {7517, 16851971}, {7518, 16852227}, {7519, 16852483}, + {7520, 16856323}, {7521, 16856579}, {7522, 16779267}, {7523, 16781571}, + {7524, 16782339}, {7525, 16782595}, {7526, 16851971}, {7527, 16852227}, + {7528, 16855299}, {7529, 16856323}, {7530, 16856579}, {7531, 1}, + {7544, 16869891}, {7545, 1}, {7579, 16960515}, {7580, 16777731}, + {7581, 16960771}, {7582, 16793603}, {7583, 16959235}, {7584, 16778499}, + {7585, 16961027}, {7586, 16961283}, {7587, 16961539}, {7588, 16817923}, + {7589, 16817667}, {7590, 16961795}, {7591, 16962051}, {7592, 16962307}, + {7593, 16962563}, {7594, 16962819}, {7595, 16963075}, {7596, 16963331}, + {7597, 16963587}, {7598, 16818691}, {7599, 16963843}, {7600, 16964099}, + {7601, 16818947}, {7602, 16964355}, {7603, 16964611}, {7604, 16820483}, + {7605, 16964867}, {7606, 16839683}, {7607, 16821507}, {7608, 16965123}, + {7609, 16821763}, {7610, 16839939}, {7611, 16783619}, {7612, 16965379}, + {7613, 16965635}, {7614, 16822531}, {7615, 16853507}, {7616, 1}, + {7680, 16965891}, {7681, 1}, {7682, 16966147}, {7683, 1}, + {7684, 16966403}, {7685, 1}, {7686, 16966659}, {7687, 1}, + {7688, 16966915}, {7689, 1}, {7690, 16967171}, {7691, 1}, + {7692, 16967427}, {7693, 1}, {7694, 16967683}, {7695, 1}, + {7696, 16967939}, {7697, 1}, {7698, 16968195}, {7699, 1}, + {7700, 16968451}, {7701, 1}, {7702, 16968707}, {7703, 1}, + {7704, 16968963}, {7705, 1}, {7706, 16969219}, {7707, 1}, + {7708, 16969475}, {7709, 1}, {7710, 16969731}, {7711, 1}, + {7712, 16969987}, {7713, 1}, {7714, 16970243}, {7715, 1}, + {7716, 16970499}, {7717, 1}, {7718, 16970755}, {7719, 1}, + {7720, 16971011}, {7721, 1}, {7722, 16971267}, {7723, 1}, + {7724, 16971523}, {7725, 1}, {7726, 16971779}, {7727, 1}, + {7728, 16972035}, {7729, 1}, {7730, 16972291}, {7731, 1}, + {7732, 16972547}, {7733, 1}, {7734, 16972803}, {7735, 1}, + {7736, 16973059}, {7737, 1}, {7738, 16973315}, {7739, 1}, + {7740, 16973571}, {7741, 1}, {7742, 16973827}, {7743, 1}, + {7744, 16974083}, {7745, 1}, {7746, 16974339}, {7747, 1}, + {7748, 16974595}, {7749, 1}, {7750, 16974851}, {7751, 1}, + {7752, 16975107}, {7753, 1}, {7754, 16975363}, {7755, 1}, + {7756, 16975619}, {7757, 1}, {7758, 16975875}, {7759, 1}, + {7760, 16976131}, {7761, 1}, {7762, 16976387}, {7763, 1}, + {7764, 16976643}, {7765, 1}, {7766, 16976899}, {7767, 1}, + {7768, 16977155}, {7769, 1}, {7770, 16977411}, {7771, 1}, + {7772, 16977667}, {7773, 1}, {7774, 16977923}, {7775, 1}, + {7776, 16978179}, {7777, 1}, {7778, 16978435}, {7779, 1}, + {7780, 16978691}, {7781, 1}, {7782, 16978947}, {7783, 1}, + {7784, 16979203}, {7785, 1}, {7786, 16979459}, {7787, 1}, + {7788, 16979715}, {7789, 1}, {7790, 16979971}, {7791, 1}, + {7792, 16980227}, {7793, 1}, {7794, 16980483}, {7795, 1}, + {7796, 16980739}, {7797, 1}, {7798, 16980995}, {7799, 1}, + {7800, 16981251}, {7801, 1}, {7802, 16981507}, {7803, 1}, + {7804, 16981763}, {7805, 1}, {7806, 16982019}, {7807, 1}, + {7808, 16982275}, {7809, 1}, {7810, 16982531}, {7811, 1}, + {7812, 16982787}, {7813, 1}, {7814, 16983043}, {7815, 1}, + {7816, 16983299}, {7817, 1}, {7818, 16983555}, {7819, 1}, + {7820, 16983811}, {7821, 1}, {7822, 16984067}, {7823, 1}, + {7824, 16984323}, {7825, 1}, {7826, 16984579}, {7827, 1}, + {7828, 16984835}, {7829, 1}, {7834, 33762307}, {7835, 16978179}, + {7836, 1}, {7838, 16985603}, {7839, 1}, {7840, 16985859}, + {7841, 1}, {7842, 16986115}, {7843, 1}, {7844, 16986371}, + {7845, 1}, {7846, 16986627}, {7847, 1}, {7848, 16986883}, + {7849, 1}, {7850, 16987139}, {7851, 1}, {7852, 16987395}, + {7853, 1}, {7854, 16987651}, {7855, 1}, {7856, 16987907}, + {7857, 1}, {7858, 16988163}, {7859, 1}, {7860, 16988419}, + {7861, 1}, {7862, 16988675}, {7863, 1}, {7864, 16988931}, + {7865, 1}, {7866, 16989187}, {7867, 1}, {7868, 16989443}, + {7869, 1}, {7870, 16989699}, {7871, 1}, {7872, 16989955}, + {7873, 1}, {7874, 16990211}, {7875, 1}, {7876, 16990467}, + {7877, 1}, {7878, 16990723}, {7879, 1}, {7880, 16990979}, + {7881, 1}, {7882, 16991235}, {7883, 1}, {7884, 16991491}, + {7885, 1}, {7886, 16991747}, {7887, 1}, {7888, 16992003}, + {7889, 1}, {7890, 16992259}, {7891, 1}, {7892, 16992515}, + {7893, 1}, {7894, 16992771}, {7895, 1}, {7896, 16993027}, + {7897, 1}, {7898, 16993283}, {7899, 1}, {7900, 16993539}, + {7901, 1}, {7902, 16993795}, {7903, 1}, {7904, 16994051}, + {7905, 1}, {7906, 16994307}, {7907, 1}, {7908, 16994563}, + {7909, 1}, {7910, 16994819}, {7911, 1}, {7912, 16995075}, + {7913, 1}, {7914, 16995331}, {7915, 1}, {7916, 16995587}, + {7917, 1}, {7918, 16995843}, {7919, 1}, {7920, 16996099}, + {7921, 1}, {7922, 16996355}, {7923, 1}, {7924, 16996611}, + {7925, 1}, {7926, 16996867}, {7927, 1}, {7928, 16997123}, + {7929, 1}, {7930, 16997379}, {7931, 1}, {7932, 16997635}, + {7933, 1}, {7934, 16997891}, {7935, 1}, {7944, 16998147}, + {7945, 16998403}, {7946, 16998659}, {7947, 16998915}, {7948, 16999171}, + {7949, 16999427}, {7950, 16999683}, {7951, 16999939}, {7952, 1}, + {7958, 2}, {7960, 17000195}, {7961, 17000451}, {7962, 17000707}, + {7963, 17000963}, {7964, 17001219}, {7965, 17001475}, {7966, 2}, + {7968, 1}, {7976, 17001731}, {7977, 17001987}, {7978, 17002243}, + {7979, 17002499}, {7980, 17002755}, {7981, 17003011}, {7982, 17003267}, + {7983, 17003523}, {7984, 1}, {7992, 17003779}, {7993, 17004035}, + {7994, 17004291}, {7995, 17004547}, {7996, 17004803}, {7997, 17005059}, + {7998, 17005315}, {7999, 17005571}, {8000, 1}, {8006, 2}, + {8008, 17005827}, {8009, 17006083}, {8010, 17006339}, {8011, 17006595}, + {8012, 17006851}, {8013, 17007107}, {8014, 2}, {8016, 1}, + {8024, 2}, {8025, 17007363}, {8026, 2}, {8027, 17007619}, + {8028, 2}, {8029, 17007875}, {8030, 2}, {8031, 17008131}, + {8032, 1}, {8040, 17008387}, {8041, 17008643}, {8042, 17008899}, + {8043, 17009155}, {8044, 17009411}, {8045, 17009667}, {8046, 17009923}, + {8047, 17010179}, {8048, 1}, {8049, 16849923}, {8050, 1}, + {8051, 16850179}, {8052, 1}, {8053, 16850435}, {8054, 1}, + {8055, 16850691}, {8056, 1}, {8057, 16850947}, {8058, 1}, + {8059, 16851203}, {8060, 1}, {8061, 16851459}, {8062, 2}, + {8064, 33787651}, {8065, 33788163}, {8066, 33788675}, {8067, 33789187}, + {8068, 33789699}, {8069, 33790211}, {8070, 33790723}, {8071, 33791235}, + {8072, 33787651}, {8073, 33788163}, {8074, 33788675}, {8075, 33789187}, + {8076, 33789699}, {8077, 33790211}, {8078, 33790723}, {8079, 33791235}, + {8080, 33791747}, {8081, 33792259}, {8082, 33792771}, {8083, 33793283}, + {8084, 33793795}, {8085, 33794307}, {8086, 33794819}, {8087, 33795331}, + {8088, 33791747}, {8089, 33792259}, {8090, 33792771}, {8091, 33793283}, + {8092, 33793795}, {8093, 33794307}, {8094, 33794819}, {8095, 33795331}, + {8096, 33795843}, {8097, 33796355}, {8098, 33796867}, {8099, 33797379}, + {8100, 33797891}, {8101, 33798403}, {8102, 33798915}, {8103, 33799427}, + {8104, 33795843}, {8105, 33796355}, {8106, 33796867}, {8107, 33797379}, + {8108, 33797891}, {8109, 33798403}, {8110, 33798915}, {8111, 33799427}, + {8112, 1}, {8114, 33799939}, {8115, 33800451}, {8116, 33800963}, + {8117, 2}, {8118, 1}, {8119, 33801475}, {8120, 17024771}, + {8121, 17025027}, {8122, 17022723}, {8123, 16849923}, {8124, 33800451}, + {8125, 33802499}, {8126, 16846851}, {8127, 33802499}, {8128, 33803011}, + {8129, 50580739}, {8130, 33804291}, {8131, 33804803}, {8132, 33805315}, + {8133, 2}, {8134, 1}, {8135, 33805827}, {8136, 17029123}, + {8137, 16850179}, {8138, 17027075}, {8139, 16850435}, {8140, 33804803}, + {8141, 50583811}, {8142, 50584579}, {8143, 50585347}, {8144, 1}, + {8147, 17031683}, {8148, 2}, {8150, 1}, {8152, 17031939}, + {8153, 17032195}, {8154, 17032451}, {8155, 16850691}, {8156, 2}, + {8157, 50587139}, {8158, 50587907}, {8159, 50588675}, {8160, 1}, + {8163, 17035011}, {8164, 1}, {8168, 17035267}, {8169, 17035523}, + {8170, 17035779}, {8171, 16851203}, {8172, 17036035}, {8173, 50590723}, + {8174, 50403587}, {8175, 17037059}, {8176, 2}, {8178, 33814531}, + {8179, 33815043}, {8180, 33815555}, {8181, 2}, {8182, 1}, + {8183, 33816067}, {8184, 17039363}, {8185, 16850947}, {8186, 17037315}, + {8187, 16851459}, {8188, 33815043}, {8189, 33562883}, {8190, 33809923}, + {8191, 2}, {8192, 16783875}, {8203, 0}, {8204, 1}, + {8206, 2}, {8208, 1}, {8209, 17039619}, {8210, 1}, + {8215, 33817091}, {8216, 1}, {8228, 2}, {8231, 1}, + {8232, 2}, {8239, 16783875}, {8240, 1}, {8243, 33817603}, + {8244, 50595331}, {8245, 1}, {8246, 33818883}, {8247, 50596611}, + {8248, 1}, {8252, 33820163}, {8253, 1}, {8254, 33820675}, + {8255, 1}, {8263, 33821187}, {8264, 33821699}, {8265, 33822211}, + {8266, 1}, {8279, 67372035}, {8280, 1}, {8287, 16783875}, + {8288, 0}, {8293, 2}, {8298, 0}, {8304, 17045507}, + {8305, 16779267}, {8306, 2}, {8308, 16787715}, {8309, 17045763}, + {8310, 17046019}, {8311, 17046275}, {8312, 17046531}, {8313, 17046787}, + {8314, 17047043}, {8315, 17047299}, {8316, 17047555}, {8317, 17047811}, + {8318, 17048067}, {8319, 16780547}, {8320, 17045507}, {8321, 16786947}, + {8322, 16785155}, {8323, 16785411}, {8324, 16787715}, {8325, 17045763}, + {8326, 17046019}, {8327, 17046275}, {8328, 17046531}, {8329, 17046787}, + {8330, 17047043}, {8331, 17047299}, {8332, 17047555}, {8333, 17047811}, + {8334, 17048067}, {8335, 2}, {8336, 16777219}, {8337, 16778243}, + {8338, 16780803}, {8339, 16783107}, {8340, 16816387}, {8341, 16779011}, + {8342, 16779779}, {8343, 16780035}, {8344, 16780291}, {8345, 16780547}, + {8346, 16781059}, {8347, 16781827}, {8348, 16782083}, {8349, 2}, + {8352, 1}, {8360, 33558787}, {8361, 1}, {8385, 2}, + {8400, 1}, {8433, 2}, {8448, 50602755}, {8449, 50603523}, + {8450, 16777731}, {8451, 33827075}, {8452, 1}, {8453, 50604803}, + {8454, 50605571}, {8455, 16816643}, {8456, 1}, {8457, 33829123}, + {8458, 16778755}, {8459, 16779011}, {8463, 16802051}, {8464, 16779267}, + {8466, 16780035}, {8468, 1}, {8469, 16780547}, {8470, 33557763}, + {8471, 1}, {8473, 16781059}, {8474, 16781315}, {8475, 16781571}, + {8478, 1}, {8480, 33829635}, {8481, 50607363}, {8482, 33830915}, + {8483, 1}, {8484, 16783619}, {8485, 1}, {8486, 16857091}, + {8487, 1}, {8488, 16783619}, {8489, 1}, {8490, 16779779}, + {8491, 16790787}, {8492, 16777475}, {8493, 16777731}, {8494, 1}, + {8495, 16778243}, {8497, 16778499}, {8498, 17054211}, {8499, 16780291}, + {8500, 16780803}, {8501, 17054467}, {8502, 17054723}, {8503, 17054979}, + {8504, 17055235}, {8505, 16779267}, {8506, 1}, {8507, 50609923}, + {8508, 16855043}, {8509, 16852227}, {8511, 16855043}, {8512, 17056259}, + {8513, 1}, {8517, 16777987}, {8519, 16778243}, {8520, 16779267}, + {8521, 16779523}, {8522, 1}, {8528, 50610947}, {8529, 50611715}, + {8530, 67389699}, {8531, 50613507}, {8532, 50614275}, {8533, 50615043}, + {8534, 50615811}, {8535, 50616579}, {8536, 50617347}, {8537, 50618115}, + {8538, 50618883}, {8539, 50619651}, {8540, 50620419}, {8541, 50621187}, + {8542, 50621955}, {8543, 33564419}, {8544, 16779267}, {8545, 33845507}, + {8546, 50623235}, {8547, 33846787}, {8548, 16782595}, {8549, 33847299}, + {8550, 50625027}, {8551, 67403011}, {8552, 33849603}, {8553, 16783107}, + {8554, 33850115}, {8555, 50627843}, {8556, 16780035}, {8557, 16777731}, + {8558, 16777987}, {8559, 16780291}, {8560, 16779267}, {8561, 33845507}, + {8562, 50622723}, {8563, 33846787}, {8564, 16782595}, {8565, 33847299}, + {8566, 50625027}, {8567, 67403011}, {8568, 33849603}, {8569, 16783107}, + {8570, 33850115}, {8571, 50627843}, {8572, 16780035}, {8573, 16777731}, + {8574, 16777987}, {8575, 16780291}, {8576, 1}, {8579, 17074179}, + {8580, 1}, {8585, 50628867}, {8586, 1}, {8588, 2}, + {8592, 1}, {8748, 33852419}, {8749, 50630147}, {8750, 1}, + {8751, 33853699}, {8752, 50631427}, {8753, 1}, {9001, 17077763}, + {9002, 17078019}, {9003, 1}, {9258, 2}, {9280, 1}, + {9291, 2}, {9312, 16786947}, {9313, 16785155}, {9314, 16785411}, + {9315, 16787715}, {9316, 17045763}, {9317, 17046019}, {9318, 17046275}, + {9319, 17046531}, {9320, 17046787}, {9321, 33835779}, {9322, 33564163}, + {9323, 33855491}, {9324, 33856003}, {9325, 33856515}, {9326, 33857027}, + {9327, 33857539}, {9328, 33858051}, {9329, 33858563}, {9330, 33859075}, + {9331, 33859587}, {9332, 50637315}, {9333, 50638083}, {9334, 50638851}, + {9335, 50639619}, {9336, 50640387}, {9337, 50641155}, {9338, 50641923}, + {9339, 50642691}, {9340, 50643459}, {9341, 67421443}, {9342, 67422467}, + {9343, 67423491}, {9344, 67424515}, {9345, 67425539}, {9346, 67426563}, + {9347, 67427587}, {9348, 67428611}, {9349, 67429635}, {9350, 67430659}, + {9351, 67431683}, {9352, 2}, {9372, 50655491}, {9373, 50656259}, + {9374, 50657027}, {9375, 50657795}, {9376, 50658563}, {9377, 50659331}, + {9378, 50660099}, {9379, 50660867}, {9380, 50661635}, {9381, 50662403}, + {9382, 50663171}, {9383, 50663939}, {9384, 50664707}, {9385, 50665475}, + {9386, 50666243}, {9387, 50667011}, {9388, 50667779}, {9389, 50668547}, + {9390, 50669315}, {9391, 50670083}, {9392, 50670851}, {9393, 50671619}, + {9394, 50672387}, {9395, 50673155}, {9396, 50673923}, {9397, 50674691}, + {9398, 16777219}, {9399, 16777475}, {9400, 16777731}, {9401, 16777987}, + {9402, 16778243}, {9403, 16778499}, {9404, 16778755}, {9405, 16779011}, + {9406, 16779267}, {9407, 16779523}, {9408, 16779779}, {9409, 16780035}, + {9410, 16780291}, {9411, 16780547}, {9412, 16780803}, {9413, 16781059}, + {9414, 16781315}, {9415, 16781571}, {9416, 16781827}, {9417, 16782083}, + {9418, 16782339}, {9419, 16782595}, {9420, 16782851}, {9421, 16783107}, + {9422, 16783363}, {9423, 16783619}, {9424, 16777219}, {9425, 16777475}, + {9426, 16777731}, {9427, 16777987}, {9428, 16778243}, {9429, 16778499}, + {9430, 16778755}, {9431, 16779011}, {9432, 16779267}, {9433, 16779523}, + {9434, 16779779}, {9435, 16780035}, {9436, 16780291}, {9437, 16780547}, + {9438, 16780803}, {9439, 16781059}, {9440, 16781315}, {9441, 16781571}, + {9442, 16781827}, {9443, 16782083}, {9444, 16782339}, {9445, 16782595}, + {9446, 16782851}, {9447, 16783107}, {9448, 16783363}, {9449, 16783619}, + {9450, 17045507}, {9451, 1}, {10764, 67406851}, {10765, 1}, + {10868, 50675459}, {10869, 33899011}, {10870, 50675971}, {10871, 1}, + {10972, 33899523}, {10973, 1}, {11124, 2}, {11126, 1}, + {11158, 2}, {11159, 1}, {11264, 17122819}, {11265, 17123075}, + {11266, 17123331}, {11267, 17123587}, {11268, 17123843}, {11269, 17124099}, + {11270, 17124355}, {11271, 17124611}, {11272, 17124867}, {11273, 17125123}, + {11274, 17125379}, {11275, 17125635}, {11276, 17125891}, {11277, 17126147}, + {11278, 17126403}, {11279, 17126659}, {11280, 17126915}, {11281, 17127171}, + {11282, 17127427}, {11283, 17127683}, {11284, 17127939}, {11285, 17128195}, + {11286, 17128451}, {11287, 17128707}, {11288, 17128963}, {11289, 17129219}, + {11290, 17129475}, {11291, 17129731}, {11292, 17129987}, {11293, 17130243}, + {11294, 17130499}, {11295, 17130755}, {11296, 17131011}, {11297, 17131267}, + {11298, 17131523}, {11299, 17131779}, {11300, 17132035}, {11301, 17132291}, + {11302, 17132547}, {11303, 17132803}, {11304, 17133059}, {11305, 17133315}, + {11306, 17133571}, {11307, 17133827}, {11308, 17134083}, {11309, 17134339}, + {11310, 17134595}, {11311, 17134851}, {11312, 1}, {11360, 17135107}, + {11361, 1}, {11362, 17135363}, {11363, 17135619}, {11364, 17135875}, + {11365, 1}, {11367, 17136131}, {11368, 1}, {11369, 17136387}, + {11370, 1}, {11371, 17136643}, {11372, 1}, {11373, 16958723}, + {11374, 16963331}, {11375, 16958467}, {11376, 16960515}, {11377, 1}, + {11378, 17136899}, {11379, 1}, {11381, 17137155}, {11382, 1}, + {11388, 16779523}, {11389, 16782595}, {11390, 17137411}, {11391, 17137667}, + {11392, 17137923}, {11393, 1}, {11394, 17138179}, {11395, 1}, + {11396, 17138435}, {11397, 1}, {11398, 17138691}, {11399, 1}, + {11400, 17138947}, {11401, 1}, {11402, 17139203}, {11403, 1}, + {11404, 17139459}, {11405, 1}, {11406, 17139715}, {11407, 1}, + {11408, 17139971}, {11409, 1}, {11410, 17140227}, {11411, 1}, + {11412, 17140483}, {11413, 1}, {11414, 17140739}, {11415, 1}, + {11416, 17140995}, {11417, 1}, {11418, 17141251}, {11419, 1}, + {11420, 17141507}, {11421, 1}, {11422, 17141763}, {11423, 1}, + {11424, 17142019}, {11425, 1}, {11426, 17142275}, {11427, 1}, + {11428, 17142531}, {11429, 1}, {11430, 17142787}, {11431, 1}, + {11432, 17143043}, {11433, 1}, {11434, 17143299}, {11435, 1}, + {11436, 17143555}, {11437, 1}, {11438, 17143811}, {11439, 1}, + {11440, 17144067}, {11441, 1}, {11442, 17144323}, {11443, 1}, + {11444, 17144579}, {11445, 1}, {11446, 17144835}, {11447, 1}, + {11448, 17145091}, {11449, 1}, {11450, 17145347}, {11451, 1}, + {11452, 17145603}, {11453, 1}, {11454, 17145859}, {11455, 1}, + {11456, 17146115}, {11457, 1}, {11458, 17146371}, {11459, 1}, + {11460, 17146627}, {11461, 1}, {11462, 17146883}, {11463, 1}, + {11464, 17147139}, {11465, 1}, {11466, 17147395}, {11467, 1}, + {11468, 17147651}, {11469, 1}, {11470, 17147907}, {11471, 1}, + {11472, 17148163}, {11473, 1}, {11474, 17148419}, {11475, 1}, + {11476, 17148675}, {11477, 1}, {11478, 17148931}, {11479, 1}, + {11480, 17149187}, {11481, 1}, {11482, 17149443}, {11483, 1}, + {11484, 17149699}, {11485, 1}, {11486, 17149955}, {11487, 1}, + {11488, 17150211}, {11489, 1}, {11490, 17150467}, {11491, 1}, + {11499, 17150723}, {11500, 1}, {11501, 17150979}, {11502, 1}, + {11506, 17151235}, {11507, 1}, {11508, 2}, {11513, 1}, + {11558, 2}, {11559, 1}, {11560, 2}, {11565, 1}, + {11566, 2}, {11568, 1}, {11624, 2}, {11631, 17151491}, + {11632, 1}, {11633, 2}, {11647, 1}, {11671, 2}, + {11680, 1}, {11687, 2}, {11688, 1}, {11695, 2}, + {11696, 1}, {11703, 2}, {11704, 1}, {11711, 2}, + {11712, 1}, {11719, 2}, {11720, 1}, {11727, 2}, + {11728, 1}, {11735, 2}, {11736, 1}, {11743, 2}, + {11744, 1}, {11870, 2}, {11904, 1}, {11930, 2}, + {11931, 1}, {11935, 17151747}, {11936, 1}, {12019, 17152003}, + {12020, 2}, {12032, 17152259}, {12033, 17152515}, {12034, 17152771}, + {12035, 17153027}, {12036, 17153283}, {12037, 17153539}, {12038, 17153795}, + {12039, 17154051}, {12040, 17154307}, {12041, 17154563}, {12042, 17154819}, + {12043, 17155075}, {12044, 17155331}, {12045, 17155587}, {12046, 17155843}, + {12047, 17156099}, {12048, 17156355}, {12049, 17156611}, {12050, 17156867}, + {12051, 17157123}, {12052, 17157379}, {12053, 17157635}, {12054, 17157891}, + {12055, 17158147}, {12056, 17158403}, {12057, 17158659}, {12058, 17158915}, + {12059, 17159171}, {12060, 17159427}, {12061, 17159683}, {12062, 17159939}, + {12063, 17160195}, {12064, 17160451}, {12065, 17160707}, {12066, 17160963}, + {12067, 17161219}, {12068, 17161475}, {12069, 17161731}, {12070, 17161987}, + {12071, 17162243}, {12072, 17162499}, {12073, 17162755}, {12074, 17163011}, + {12075, 17163267}, {12076, 17163523}, {12077, 17163779}, {12078, 17164035}, + {12079, 17164291}, {12080, 17164547}, {12081, 17164803}, {12082, 17165059}, + {12083, 17165315}, {12084, 17165571}, {12085, 17165827}, {12086, 17166083}, + {12087, 17166339}, {12088, 17166595}, {12089, 17166851}, {12090, 17167107}, + {12091, 17167363}, {12092, 17167619}, {12093, 17167875}, {12094, 17168131}, + {12095, 17168387}, {12096, 17168643}, {12097, 17168899}, {12098, 17169155}, + {12099, 17169411}, {12100, 17169667}, {12101, 17169923}, {12102, 17170179}, + {12103, 17170435}, {12104, 17170691}, {12105, 17170947}, {12106, 17171203}, + {12107, 17171459}, {12108, 17171715}, {12109, 17171971}, {12110, 17172227}, + {12111, 17172483}, {12112, 17172739}, {12113, 17172995}, {12114, 17173251}, + {12115, 17173507}, {12116, 17173763}, {12117, 17174019}, {12118, 17174275}, + {12119, 17174531}, {12120, 17174787}, {12121, 17175043}, {12122, 17175299}, + {12123, 17175555}, {12124, 17175811}, {12125, 17176067}, {12126, 17176323}, + {12127, 17176579}, {12128, 17176835}, {12129, 17177091}, {12130, 17177347}, + {12131, 17177603}, {12132, 17177859}, {12133, 17178115}, {12134, 17178371}, + {12135, 17178627}, {12136, 17178883}, {12137, 17179139}, {12138, 17179395}, + {12139, 17179651}, {12140, 17179907}, {12141, 17180163}, {12142, 17180419}, + {12143, 17180675}, {12144, 17180931}, {12145, 17181187}, {12146, 17181443}, + {12147, 17181699}, {12148, 17181955}, {12149, 17182211}, {12150, 17182467}, + {12151, 17182723}, {12152, 17182979}, {12153, 17183235}, {12154, 17183491}, + {12155, 17183747}, {12156, 17184003}, {12157, 17184259}, {12158, 17184515}, + {12159, 17184771}, {12160, 17185027}, {12161, 17185283}, {12162, 17185539}, + {12163, 17185795}, {12164, 17186051}, {12165, 17186307}, {12166, 17186563}, + {12167, 17186819}, {12168, 17187075}, {12169, 17187331}, {12170, 17187587}, + {12171, 17187843}, {12172, 17188099}, {12173, 17188355}, {12174, 17188611}, + {12175, 17188867}, {12176, 17189123}, {12177, 17189379}, {12178, 17189635}, + {12179, 17189891}, {12180, 17190147}, {12181, 17190403}, {12182, 17190659}, + {12183, 17190915}, {12184, 17191171}, {12185, 17191427}, {12186, 17191683}, + {12187, 17191939}, {12188, 17192195}, {12189, 17192451}, {12190, 17192707}, + {12191, 17192963}, {12192, 17193219}, {12193, 17193475}, {12194, 17193731}, + {12195, 17193987}, {12196, 17194243}, {12197, 17194499}, {12198, 17194755}, + {12199, 17195011}, {12200, 17195267}, {12201, 17195523}, {12202, 17195779}, + {12203, 17196035}, {12204, 17196291}, {12205, 17196547}, {12206, 17196803}, + {12207, 17197059}, {12208, 17197315}, {12209, 17197571}, {12210, 17197827}, + {12211, 17198083}, {12212, 17198339}, {12213, 17198595}, {12214, 17198851}, + {12215, 17199107}, {12216, 17199363}, {12217, 17199619}, {12218, 17199875}, + {12219, 17200131}, {12220, 17200387}, {12221, 17200643}, {12222, 17200899}, + {12223, 17201155}, {12224, 17201411}, {12225, 17201667}, {12226, 17201923}, + {12227, 17202179}, {12228, 17202435}, {12229, 17202691}, {12230, 17202947}, + {12231, 17203203}, {12232, 17203459}, {12233, 17203715}, {12234, 17203971}, + {12235, 17204227}, {12236, 17204483}, {12237, 17204739}, {12238, 17204995}, + {12239, 17205251}, {12240, 17205507}, {12241, 17205763}, {12242, 17206019}, + {12243, 17206275}, {12244, 17206531}, {12245, 17206787}, {12246, 2}, + {12288, 16783875}, {12289, 1}, {12290, 17207043}, {12291, 1}, + {12342, 17207299}, {12343, 1}, {12344, 17158147}, {12345, 17207555}, + {12346, 17207811}, {12347, 1}, {12352, 2}, {12353, 1}, + {12439, 2}, {12441, 1}, {12443, 33985283}, {12444, 33985795}, + {12445, 1}, {12447, 33986307}, {12448, 1}, {12543, 33986819}, + {12544, 2}, {12549, 1}, {12592, 2}, {12593, 17210115}, + {12594, 17210371}, {12595, 17210627}, {12596, 17210883}, {12597, 17211139}, + {12598, 17211395}, {12599, 17211651}, {12600, 17211907}, {12601, 17212163}, + {12602, 17212419}, {12603, 17212675}, {12604, 17212931}, {12605, 17213187}, + {12606, 17213443}, {12607, 17213699}, {12608, 17213955}, {12609, 17214211}, + {12610, 17214467}, {12611, 17214723}, {12612, 17214979}, {12613, 17215235}, + {12614, 17215491}, {12615, 17215747}, {12616, 17216003}, {12617, 17216259}, + {12618, 17216515}, {12619, 17216771}, {12620, 17217027}, {12621, 17217283}, + {12622, 17217539}, {12623, 17217795}, {12624, 17218051}, {12625, 17218307}, + {12626, 17218563}, {12627, 17218819}, {12628, 17219075}, {12629, 17219331}, + {12630, 17219587}, {12631, 17219843}, {12632, 17220099}, {12633, 17220355}, + {12634, 17220611}, {12635, 17220867}, {12636, 17221123}, {12637, 17221379}, + {12638, 17221635}, {12639, 17221891}, {12640, 17222147}, {12641, 17222403}, + {12642, 17222659}, {12643, 17222915}, {12644, 0}, {12645, 17223171}, + {12646, 17223427}, {12647, 17223683}, {12648, 17223939}, {12649, 17224195}, + {12650, 17224451}, {12651, 17224707}, {12652, 17224963}, {12653, 17225219}, + {12654, 17225475}, {12655, 17225731}, {12656, 17225987}, {12657, 17226243}, + {12658, 17226499}, {12659, 17226755}, {12660, 17227011}, {12661, 17227267}, + {12662, 17227523}, {12663, 17227779}, {12664, 17228035}, {12665, 17228291}, + {12666, 17228547}, {12667, 17228803}, {12668, 17229059}, {12669, 17229315}, + {12670, 17229571}, {12671, 17229827}, {12672, 17230083}, {12673, 17230339}, + {12674, 17230595}, {12675, 17230851}, {12676, 17231107}, {12677, 17231363}, + {12678, 17231619}, {12679, 17231875}, {12680, 17232131}, {12681, 17232387}, + {12682, 17232643}, {12683, 17232899}, {12684, 17233155}, {12685, 17233411}, + {12686, 17233667}, {12687, 2}, {12688, 1}, {12690, 17152259}, + {12691, 17153795}, {12692, 17233923}, {12693, 17234179}, {12694, 17234435}, + {12695, 17234691}, {12696, 17234947}, {12697, 17235203}, {12698, 17153283}, + {12699, 17235459}, {12700, 17235715}, {12701, 17235971}, {12702, 17236227}, + {12703, 17154307}, {12704, 1}, {12774, 2}, {12784, 1}, + {12800, 50790915}, {12801, 50791683}, {12802, 50792451}, {12803, 50793219}, + {12804, 50793987}, {12805, 50794755}, {12806, 50795523}, {12807, 50796291}, + {12808, 50797059}, {12809, 50797827}, {12810, 50798595}, {12811, 50799363}, + {12812, 50800131}, {12813, 50800899}, {12814, 50801667}, {12815, 50802435}, + {12816, 50803203}, {12817, 50803971}, {12818, 50804739}, {12819, 50805507}, + {12820, 50806275}, {12821, 50807043}, {12822, 50807811}, {12823, 50808579}, + {12824, 50809347}, {12825, 50810115}, {12826, 50810883}, {12827, 50811651}, + {12828, 50812419}, {12829, 67590403}, {12830, 67591427}, {12831, 2}, + {12832, 50815235}, {12833, 50816003}, {12834, 50816771}, {12835, 50817539}, + {12836, 50818307}, {12837, 50819075}, {12838, 50819843}, {12839, 50820611}, + {12840, 50821379}, {12841, 50822147}, {12842, 50822915}, {12843, 50823683}, + {12844, 50824451}, {12845, 50825219}, {12846, 50825987}, {12847, 50826755}, + {12848, 50827523}, {12849, 50828291}, {12850, 50829059}, {12851, 50829827}, + {12852, 50830595}, {12853, 50831363}, {12854, 50832131}, {12855, 50832899}, + {12856, 50833667}, {12857, 50834435}, {12858, 50835203}, {12859, 50835971}, + {12860, 50836739}, {12861, 50837507}, {12862, 50838275}, {12863, 50839043}, + {12864, 50839811}, {12865, 50840579}, {12866, 50841347}, {12867, 50842115}, + {12868, 17288451}, {12869, 17288707}, {12870, 17169155}, {12871, 17288963}, + {12872, 1}, {12880, 50843651}, {12881, 33855747}, {12882, 34067203}, + {12883, 33562371}, {12884, 34067715}, {12885, 34068227}, {12886, 34068739}, + {12887, 34069251}, {12888, 34069763}, {12889, 34070275}, {12890, 34070787}, + {12891, 33837571}, {12892, 33836803}, {12893, 34071299}, {12894, 34071811}, + {12895, 34072323}, {12896, 17210115}, {12897, 17210883}, {12898, 17211651}, + {12899, 17212163}, {12900, 17214211}, {12901, 17214467}, {12902, 17215235}, + {12903, 17215747}, {12904, 17216003}, {12905, 17216515}, {12906, 17216771}, + {12907, 17217027}, {12908, 17217283}, {12909, 17217539}, {12910, 17247491}, + {12911, 17248259}, {12912, 17249027}, {12913, 17249795}, {12914, 17250563}, + {12915, 17251331}, {12916, 17252099}, {12917, 17252867}, {12918, 17253635}, + {12919, 17254403}, {12920, 17255171}, {12921, 17255939}, {12922, 17256707}, + {12923, 17257475}, {12924, 34072835}, {12925, 34073347}, {12926, 17296643}, + {12927, 1}, {12928, 17152259}, {12929, 17153795}, {12930, 17233923}, + {12931, 17234179}, {12932, 17264131}, {12933, 17264899}, {12934, 17265667}, + {12935, 17155075}, {12936, 17267203}, {12937, 17158147}, {12938, 17170947}, + {12939, 17174019}, {12940, 17173763}, {12941, 17171203}, {12942, 17194755}, + {12943, 17160195}, {12944, 17170435}, {12945, 17274115}, {12946, 17274883}, + {12947, 17275651}, {12948, 17276419}, {12949, 17277187}, {12950, 17277955}, + {12951, 17278723}, {12952, 17279491}, {12953, 17296899}, {12954, 17297155}, + {12955, 17161731}, {12956, 17297411}, {12957, 17297667}, {12958, 17297923}, + {12959, 17298179}, {12960, 17298435}, {12961, 17286403}, {12962, 17298691}, + {12963, 17298947}, {12964, 17234435}, {12965, 17234691}, {12966, 17234947}, + {12967, 17299203}, {12968, 17299459}, {12969, 17299715}, {12970, 17299971}, + {12971, 17281795}, {12972, 17282563}, {12973, 17283331}, {12974, 17284099}, + {12975, 17284867}, {12976, 17300227}, {12977, 34077699}, {12978, 34078211}, + {12979, 34078723}, {12980, 34079235}, {12981, 34079747}, {12982, 33564931}, + {12983, 34067971}, {12984, 34072067}, {12985, 34080259}, {12986, 34080771}, + {12987, 34081283}, {12988, 34081795}, {12989, 34082307}, {12990, 34082819}, + {12991, 34083331}, {12992, 34083843}, {12993, 34084355}, {12994, 34084867}, + {12995, 34085379}, {12996, 34085891}, {12997, 34086403}, {12998, 34086915}, + {12999, 34087427}, {13000, 34087939}, {13001, 50865667}, {13002, 50866435}, + {13003, 50867203}, {13004, 34090755}, {13005, 50868483}, {13006, 34092035}, + {13007, 50869763}, {13008, 17316099}, {13009, 17316355}, {13010, 17316611}, + {13011, 17316867}, {13012, 17317123}, {13013, 17317379}, {13014, 17317635}, + {13015, 17317891}, {13016, 17318147}, {13017, 17209603}, {13018, 17318403}, + {13019, 17318659}, {13020, 17318915}, {13021, 17319171}, {13022, 17319427}, + {13023, 17319683}, {13024, 17319939}, {13025, 17320195}, {13026, 17320451}, + {13027, 17209859}, {13028, 17320707}, {13029, 17320963}, {13030, 17321219}, + {13031, 17321475}, {13032, 17321731}, {13033, 17321987}, {13034, 17322243}, + {13035, 17322499}, {13036, 17322755}, {13037, 17323011}, {13038, 17323267}, + {13039, 17323523}, {13040, 17323779}, {13041, 17324035}, {13042, 17324291}, + {13043, 17324547}, {13044, 17324803}, {13045, 17325059}, {13046, 17325315}, + {13047, 17325571}, {13048, 17325827}, {13049, 17326083}, {13050, 17326339}, + {13051, 17326595}, {13052, 17326851}, {13053, 17327107}, {13054, 17327363}, + {13055, 34104835}, {13056, 67659779}, {13057, 67660803}, {13058, 67661827}, + {13059, 50885635}, {13060, 67663619}, {13061, 50887427}, {13062, 50888195}, + {13063, 84443395}, {13064, 67667459}, {13065, 50891267}, {13066, 50892035}, + {13067, 50892803}, {13068, 67670787}, {13069, 67671811}, {13070, 50895619}, + {13071, 50896387}, {13072, 34119939}, {13073, 50897667}, {13074, 67675651}, + {13075, 67676675}, {13076, 34123267}, {13077, 84455427}, {13078, 101233923}, + {13079, 84458243}, {13080, 50901507}, {13081, 84459523}, {13082, 84460803}, + {13083, 67684867}, {13084, 50908675}, {13085, 50909443}, {13086, 50910211}, + {13087, 67688195}, {13088, 84466435}, {13089, 67690499}, {13090, 50914307}, + {13091, 50915075}, {13092, 50915843}, {13093, 34139395}, {13094, 34139907}, + {13095, 34128643}, {13096, 34140419}, {13097, 50918147}, {13098, 50918915}, + {13099, 84474115}, {13100, 50920963}, {13101, 67698947}, {13102, 84477187}, + {13103, 50924035}, {13104, 34147587}, {13105, 34148099}, {13106, 84480259}, + {13107, 67704323}, {13108, 84482563}, {13109, 50929411}, {13110, 84484611}, + {13111, 34154243}, {13112, 50931971}, {13113, 50932739}, {13114, 50933507}, + {13115, 50934275}, {13116, 50935043}, {13117, 67713027}, {13118, 50936835}, + {13119, 34160387}, {13120, 50938115}, {13121, 50938883}, {13122, 50939651}, + {13123, 67717635}, {13124, 50941443}, {13125, 50942211}, {13126, 50942979}, + {13127, 84498179}, {13128, 67722243}, {13129, 34168835}, {13130, 84500995}, + {13131, 34170627}, {13132, 67725571}, {13133, 67680003}, {13134, 50949379}, + {13135, 50950147}, {13136, 50950915}, {13137, 67728899}, {13138, 34175491}, + {13139, 50953219}, {13140, 67731203}, {13141, 34177795}, {13142, 84509955}, + {13143, 50904323}, {13144, 34179587}, {13145, 34180099}, {13146, 34180611}, + {13147, 34181123}, {13148, 34181635}, {13149, 34182147}, {13150, 34182659}, + {13151, 34183171}, {13152, 34183683}, {13153, 34184195}, {13154, 50961923}, + {13155, 50962691}, {13156, 50963459}, {13157, 50964227}, {13158, 50964995}, + {13159, 50965763}, {13160, 50966531}, {13161, 50967299}, {13162, 50968067}, + {13163, 50968835}, {13164, 50969603}, {13165, 50970371}, {13166, 50971139}, + {13167, 50971907}, {13168, 50972675}, {13169, 50973443}, {13170, 34196995}, + {13171, 34197507}, {13172, 50975235}, {13173, 34198787}, {13174, 34199299}, + {13175, 34199811}, {13176, 50977539}, {13177, 50978307}, {13178, 34201859}, + {13179, 34202371}, {13180, 34202883}, {13181, 34203395}, {13182, 34203907}, + {13183, 67758851}, {13184, 34196483}, {13185, 34205443}, {13186, 34205955}, + {13187, 34206467}, {13188, 34206979}, {13189, 34207491}, {13190, 34208003}, + {13191, 34208515}, {13192, 50986243}, {13193, 67764227}, {13194, 34210819}, + {13195, 34211331}, {13196, 34211843}, {13197, 34212355}, {13198, 34212867}, + {13199, 34213379}, {13200, 34213891}, {13201, 50991619}, {13202, 50992387}, + {13203, 50990851}, {13204, 50993155}, {13205, 34216707}, {13206, 34217219}, + {13207, 34217731}, {13208, 33556995}, {13209, 34218243}, {13210, 34218755}, + {13211, 34219267}, {13212, 34219779}, {13213, 34220291}, {13214, 34220803}, + {13215, 50998531}, {13216, 50999299}, {13217, 34200579}, {13218, 51000067}, + {13219, 51000835}, {13220, 51001603}, {13221, 34201347}, {13222, 51002371}, + {13223, 51003139}, {13224, 67781123}, {13225, 34196483}, {13226, 51004931}, + {13227, 51005699}, {13228, 51006467}, {13229, 51007235}, {13230, 84562435}, + {13231, 101340931}, {13232, 34233603}, {13233, 34234115}, {13234, 34234627}, + {13235, 34235139}, {13236, 34235651}, {13237, 34236163}, {13238, 34236675}, + {13239, 34237187}, {13240, 34237699}, {13241, 34237187}, {13242, 34238211}, + {13243, 34238723}, {13244, 34239235}, {13245, 34239747}, {13246, 34240259}, + {13247, 34239747}, {13248, 34240771}, {13249, 34241283}, {13250, 2}, + {13251, 34241795}, {13252, 33827331}, {13253, 33554947}, {13254, 67796739}, + {13255, 2}, {13256, 34243331}, {13257, 34243843}, {13258, 34244355}, + {13259, 34196227}, {13260, 34244867}, {13261, 34245379}, {13262, 34220803}, + {13263, 34245891}, {13264, 33557251}, {13265, 34246403}, {13266, 51024131}, + {13267, 34247683}, {13268, 34208003}, {13269, 51025411}, {13270, 51026179}, + {13271, 34249731}, {13272, 2}, {13273, 51027459}, {13274, 34251011}, + {13275, 34231811}, {13276, 34251523}, {13277, 34252035}, {13278, 51029763}, + {13279, 51030531}, {13280, 34254083}, {13281, 34254595}, {13282, 34255107}, + {13283, 34255619}, {13284, 34256131}, {13285, 34256643}, {13286, 34257155}, + {13287, 34257667}, {13288, 34258179}, {13289, 51035907}, {13290, 51036675}, + {13291, 51037443}, {13292, 51038211}, {13293, 51038979}, {13294, 51039747}, + {13295, 51040515}, {13296, 51041283}, {13297, 51042051}, {13298, 51042819}, + {13299, 51043587}, {13300, 51044355}, {13301, 51045123}, {13302, 51045891}, + {13303, 51046659}, {13304, 51047427}, {13305, 51048195}, {13306, 51048963}, + {13307, 51049731}, {13308, 51050499}, {13309, 51051267}, {13310, 51052035}, + {13311, 51052803}, {13312, 1}, {42125, 2}, {42128, 1}, + {42183, 2}, {42192, 1}, {42540, 2}, {42560, 17499139}, + {42561, 1}, {42562, 17499395}, {42563, 1}, {42564, 17499651}, + {42565, 1}, {42566, 17499907}, {42567, 1}, {42568, 17500163}, + {42569, 1}, {42570, 16946435}, {42571, 1}, {42572, 17500419}, + {42573, 1}, {42574, 17500675}, {42575, 1}, {42576, 17500931}, + {42577, 1}, {42578, 17501187}, {42579, 1}, {42580, 17501443}, + {42581, 1}, {42582, 17501699}, {42583, 1}, {42584, 17501955}, + {42585, 1}, {42586, 17502211}, {42587, 1}, {42588, 17502467}, + {42589, 1}, {42590, 17502723}, {42591, 1}, {42592, 17502979}, + {42593, 1}, {42594, 17503235}, {42595, 1}, {42596, 17503491}, + {42597, 1}, {42598, 17503747}, {42599, 1}, {42600, 17504003}, + {42601, 1}, {42602, 17504259}, {42603, 1}, {42604, 17504515}, + {42605, 1}, {42624, 17504771}, {42625, 1}, {42626, 17505027}, + {42627, 1}, {42628, 17505283}, {42629, 1}, {42630, 17505539}, + {42631, 1}, {42632, 17505795}, {42633, 1}, {42634, 17506051}, + {42635, 1}, {42636, 17506307}, {42637, 1}, {42638, 17506563}, + {42639, 1}, {42640, 17506819}, {42641, 1}, {42642, 17507075}, + {42643, 1}, {42644, 17507331}, {42645, 1}, {42646, 17507587}, + {42647, 1}, {42648, 17507843}, {42649, 1}, {42650, 17508099}, + {42651, 1}, {42652, 16873219}, {42653, 16873731}, {42654, 1}, + {42744, 2}, {42752, 1}, {42786, 17508355}, {42787, 1}, + {42788, 17508611}, {42789, 1}, {42790, 17508867}, {42791, 1}, + {42792, 17509123}, {42793, 1}, {42794, 17509379}, {42795, 1}, + {42796, 17509635}, {42797, 1}, {42798, 17509891}, {42799, 1}, + {42802, 17510147}, {42803, 1}, {42804, 17510403}, {42805, 1}, + {42806, 17510659}, {42807, 1}, {42808, 17510915}, {42809, 1}, + {42810, 17511171}, {42811, 1}, {42812, 17511427}, {42813, 1}, + {42814, 17511683}, {42815, 1}, {42816, 17511939}, {42817, 1}, + {42818, 17512195}, {42819, 1}, {42820, 17512451}, {42821, 1}, + {42822, 17512707}, {42823, 1}, {42824, 17512963}, {42825, 1}, + {42826, 17513219}, {42827, 1}, {42828, 17513475}, {42829, 1}, + {42830, 17513731}, {42831, 1}, {42832, 17513987}, {42833, 1}, + {42834, 17514243}, {42835, 1}, {42836, 17514499}, {42837, 1}, + {42838, 17514755}, {42839, 1}, {42840, 17515011}, {42841, 1}, + {42842, 17515267}, {42843, 1}, {42844, 17515523}, {42845, 1}, + {42846, 17515779}, {42847, 1}, {42848, 17516035}, {42849, 1}, + {42850, 17516291}, {42851, 1}, {42852, 17516547}, {42853, 1}, + {42854, 17516803}, {42855, 1}, {42856, 17517059}, {42857, 1}, + {42858, 17517315}, {42859, 1}, {42860, 17517571}, {42861, 1}, + {42862, 17517827}, {42863, 1}, {42864, 17517827}, {42865, 1}, + {42873, 17518083}, {42874, 1}, {42875, 17518339}, {42876, 1}, + {42877, 17518595}, {42878, 17518851}, {42879, 1}, {42880, 17519107}, + {42881, 1}, {42882, 17519363}, {42883, 1}, {42884, 17519619}, + {42885, 1}, {42886, 17519875}, {42887, 1}, {42891, 17520131}, + {42892, 1}, {42893, 16961539}, {42894, 1}, {42896, 17520387}, + {42897, 1}, {42898, 17520643}, {42899, 1}, {42902, 17520899}, + {42903, 1}, {42904, 17521155}, {42905, 1}, {42906, 17521411}, + {42907, 1}, {42908, 17521667}, {42909, 1}, {42910, 17521923}, + {42911, 1}, {42912, 17522179}, {42913, 1}, {42914, 17522435}, + {42915, 1}, {42916, 17522691}, {42917, 1}, {42918, 17522947}, + {42919, 1}, {42920, 17523203}, {42921, 1}, {42922, 16841475}, + {42923, 16959235}, {42924, 16961283}, {42925, 17523459}, {42926, 16961795}, + {42927, 1}, {42928, 17523715}, {42929, 17523971}, {42930, 16962307}, + {42931, 17524227}, {42932, 17524483}, {42933, 1}, {42934, 17524739}, + {42935, 1}, {42936, 17524995}, {42937, 1}, {42938, 17525251}, + {42939, 1}, {42940, 17525507}, {42941, 1}, {42942, 17525763}, + {42943, 1}, {42944, 17526019}, {42945, 1}, {42946, 17526275}, + {42947, 1}, {42948, 17526531}, {42949, 16964611}, {42950, 17526787}, + {42951, 17527043}, {42952, 1}, {42953, 17527299}, {42954, 1}, + {42955, 17527555}, {42956, 17527811}, {42957, 1}, {42958, 2}, + {42960, 17528067}, {42961, 1}, {42962, 2}, {42963, 1}, + {42964, 2}, {42965, 1}, {42966, 17528323}, {42967, 1}, + {42968, 17528579}, {42969, 1}, {42970, 17528835}, {42971, 1}, + {42972, 17529091}, {42973, 2}, {42994, 16777731}, {42995, 16778499}, + {42996, 16781315}, {42997, 17529347}, {42998, 1}, {43000, 16802051}, + {43001, 16808195}, {43002, 1}, {43053, 2}, {43056, 1}, + {43066, 2}, {43072, 1}, {43128, 2}, {43136, 1}, + {43206, 2}, {43214, 1}, {43226, 2}, {43232, 1}, + {43348, 2}, {43359, 1}, {43389, 2}, {43392, 1}, + {43470, 2}, {43471, 1}, {43482, 2}, {43486, 1}, + {43519, 2}, {43520, 1}, {43575, 2}, {43584, 1}, + {43598, 2}, {43600, 1}, {43610, 2}, {43612, 1}, + {43715, 2}, {43739, 1}, {43767, 2}, {43777, 1}, + {43783, 2}, {43785, 1}, {43791, 2}, {43793, 1}, + {43799, 2}, {43808, 1}, {43815, 2}, {43816, 1}, + {43823, 2}, {43824, 1}, {43868, 17508867}, {43869, 17529603}, + {43870, 17135363}, {43871, 17529859}, {43872, 1}, {43881, 17530115}, + {43882, 1}, {43884, 2}, {43888, 17530371}, {43889, 17530627}, + {43890, 17530883}, {43891, 17531139}, {43892, 17531395}, {43893, 17531651}, + {43894, 17531907}, {43895, 17532163}, {43896, 17532419}, {43897, 17532675}, + {43898, 17532931}, {43899, 17533187}, {43900, 17533443}, {43901, 17533699}, + {43902, 17533955}, {43903, 17534211}, {43904, 17534467}, {43905, 17534723}, + {43906, 17534979}, {43907, 17535235}, {43908, 17535491}, {43909, 17535747}, + {43910, 17536003}, {43911, 17536259}, {43912, 17536515}, {43913, 17536771}, + {43914, 17537027}, {43915, 17537283}, {43916, 17537539}, {43917, 17537795}, + {43918, 17538051}, {43919, 17538307}, {43920, 17538563}, {43921, 17538819}, + {43922, 17539075}, {43923, 17539331}, {43924, 17539587}, {43925, 17539843}, + {43926, 17540099}, {43927, 17540355}, {43928, 17540611}, {43929, 17540867}, + {43930, 17541123}, {43931, 17541379}, {43932, 17541635}, {43933, 17541891}, + {43934, 17542147}, {43935, 17542403}, {43936, 17542659}, {43937, 17542915}, + {43938, 17543171}, {43939, 17543427}, {43940, 17543683}, {43941, 17543939}, + {43942, 17544195}, {43943, 17544451}, {43944, 17544707}, {43945, 17544963}, + {43946, 17545219}, {43947, 17545475}, {43948, 17545731}, {43949, 17545987}, + {43950, 17546243}, {43951, 17546499}, {43952, 17546755}, {43953, 17547011}, + {43954, 17547267}, {43955, 17547523}, {43956, 17547779}, {43957, 17548035}, + {43958, 17548291}, {43959, 17548547}, {43960, 17548803}, {43961, 17549059}, + {43962, 17549315}, {43963, 17549571}, {43964, 17549827}, {43965, 17550083}, + {43966, 17550339}, {43967, 17550595}, {43968, 1}, {44014, 2}, + {44016, 1}, {44026, 2}, {44032, 1}, {55204, 2}, + {55216, 1}, {55239, 2}, {55243, 1}, {55292, 2}, + {63744, 17550851}, {63745, 17551107}, {63746, 17192707}, {63747, 17551363}, + {63748, 17551619}, {63749, 17551875}, {63750, 17552131}, {63751, 17206531}, + {63753, 17552387}, {63754, 17194755}, {63755, 17552643}, {63756, 17552899}, + {63757, 17553155}, {63758, 17553411}, {63759, 17553667}, {63760, 17553923}, + {63761, 17554179}, {63762, 17554435}, {63763, 17554691}, {63764, 17554947}, + {63765, 17555203}, {63766, 17555459}, {63767, 17555715}, {63768, 17555971}, + {63769, 17556227}, {63770, 17556483}, {63771, 17556739}, {63772, 17556995}, + {63773, 17557251}, {63774, 17557507}, {63775, 17557763}, {63776, 17558019}, + {63777, 17558275}, {63778, 17558531}, {63779, 17558787}, {63780, 17559043}, + {63781, 17559299}, {63782, 17559555}, {63783, 17559811}, {63784, 17560067}, + {63785, 17560323}, {63786, 17560579}, {63787, 17560835}, {63788, 17561091}, + {63789, 17561347}, {63790, 17561603}, {63791, 17561859}, {63792, 17562115}, + {63793, 17562371}, {63794, 17562627}, {63795, 17562883}, {63796, 17184003}, + {63797, 17563139}, {63798, 17563395}, {63799, 17563651}, {63800, 17563907}, + {63801, 17564163}, {63802, 17564419}, {63803, 17564675}, {63804, 17564931}, + {63805, 17565187}, {63806, 17565443}, {63807, 17565699}, {63808, 17202691}, + {63809, 17565955}, {63810, 17566211}, {63811, 17566467}, {63812, 17566723}, + {63813, 17566979}, {63814, 17567235}, {63815, 17567491}, {63816, 17567747}, + {63817, 17568003}, {63818, 17568259}, {63819, 17568515}, {63820, 17568771}, + {63821, 17569027}, {63822, 17569283}, {63823, 17569539}, {63824, 17569795}, + {63825, 17570051}, {63826, 17570307}, {63827, 17570563}, {63828, 17570819}, + {63829, 17571075}, {63830, 17571331}, {63831, 17571587}, {63832, 17571843}, + {63833, 17572099}, {63834, 17572355}, {63835, 17572611}, {63836, 17554947}, + {63837, 17572867}, {63838, 17573123}, {63839, 17573379}, {63840, 17573635}, + {63841, 17573891}, {63842, 17574147}, {63843, 17574403}, {63844, 17574659}, + {63845, 17574915}, {63846, 17575171}, {63847, 17575427}, {63848, 17575683}, + {63849, 17575939}, {63850, 17576195}, {63851, 17576451}, {63852, 17576707}, + {63853, 17576963}, {63854, 17577219}, {63855, 17577475}, {63856, 17577731}, + {63857, 17193219}, {63858, 17577987}, {63859, 17578243}, {63860, 17578499}, + {63861, 17578755}, {63862, 17579011}, {63863, 17579267}, {63864, 17579523}, + {63865, 17579779}, {63866, 17580035}, {63867, 17580291}, {63868, 17580547}, + {63869, 17580803}, {63870, 17581059}, {63871, 17581315}, {63872, 17581571}, + {63873, 17161731}, {63874, 17581827}, {63875, 17582083}, {63876, 17582339}, + {63877, 17582595}, {63878, 17582851}, {63879, 17583107}, {63880, 17583363}, + {63881, 17583619}, {63882, 17156867}, {63883, 17583875}, {63884, 17584131}, + {63885, 17584387}, {63886, 17584643}, {63887, 17584899}, {63888, 17585155}, + {63889, 17585411}, {63890, 17585667}, {63891, 17585923}, {63892, 17586179}, + {63893, 17586435}, {63894, 17586691}, {63895, 17586947}, {63896, 17587203}, + {63897, 17587459}, {63898, 17587715}, {63899, 17587971}, {63900, 17588227}, + {63901, 17588483}, {63902, 17588739}, {63903, 17588995}, {63904, 17589251}, + {63905, 17577475}, {63906, 17589507}, {63907, 17589763}, {63908, 17590019}, + {63909, 17590275}, {63910, 17590531}, {63911, 17590787}, {63912, 17327619}, + {63913, 17591043}, {63914, 17573379}, {63915, 17591299}, {63916, 17591555}, + {63917, 17591811}, {63918, 17592067}, {63919, 17592323}, {63920, 17592579}, + {63921, 17592835}, {63922, 17593091}, {63923, 17593347}, {63924, 17593603}, + {63925, 17593859}, {63926, 17594115}, {63927, 17594371}, {63928, 17594627}, + {63929, 17594883}, {63930, 17595139}, {63931, 17595395}, {63932, 17595651}, + {63933, 17595907}, {63934, 17596163}, {63935, 17554947}, {63936, 17596419}, + {63937, 17596675}, {63938, 17596931}, {63939, 17597187}, {63940, 17206275}, + {63941, 17597443}, {63942, 17597699}, {63943, 17597955}, {63944, 17598211}, + {63945, 17598467}, {63946, 17598723}, {63947, 17598979}, {63948, 17599235}, + {63949, 17599491}, {63950, 17599747}, {63951, 17600003}, {63952, 17600259}, + {63953, 17264899}, {63954, 17600515}, {63955, 17600771}, {63956, 17601027}, + {63957, 17601283}, {63958, 17601539}, {63959, 17601795}, {63960, 17602051}, + {63961, 17602307}, {63962, 17602563}, {63963, 17573891}, {63964, 17602819}, + {63965, 17603075}, {63966, 17603331}, {63967, 17603587}, {63968, 17603843}, + {63969, 17604099}, {63970, 17604355}, {63971, 17604611}, {63972, 17604867}, + {63973, 17605123}, {63974, 17605379}, {63975, 17605635}, {63976, 17605891}, + {63977, 17194499}, {63978, 17606147}, {63979, 17606403}, {63980, 17606659}, + {63981, 17606915}, {63982, 17607171}, {63983, 17607427}, {63984, 17607683}, + {63985, 17607939}, {63986, 17608195}, {63987, 17608451}, {63988, 17608707}, + {63989, 17608963}, {63990, 17609219}, {63991, 17181955}, {63992, 17609475}, + {63993, 17609731}, {63994, 17609987}, {63995, 17610243}, {63996, 17610499}, + {63997, 17610755}, {63998, 17611011}, {63999, 17611267}, {64000, 17611523}, + {64001, 17611779}, {64002, 17612035}, {64003, 17612291}, {64004, 17612547}, + {64005, 17612803}, {64006, 17613059}, {64007, 17613315}, {64008, 17188867}, + {64009, 17613571}, {64010, 17189635}, {64011, 17613827}, {64012, 17614083}, + {64013, 17614339}, {64014, 1}, {64016, 17614595}, {64017, 1}, + {64018, 17614851}, {64019, 1}, {64021, 17615107}, {64022, 17615363}, + {64023, 17615619}, {64024, 17615875}, {64025, 17616131}, {64026, 17616387}, + {64027, 17616643}, {64028, 17616899}, {64029, 17617155}, {64030, 17183747}, + {64031, 1}, {64032, 17617411}, {64033, 1}, {64034, 17617667}, + {64035, 1}, {64037, 17617923}, {64038, 17618179}, {64039, 1}, + {64042, 17618435}, {64043, 17618691}, {64044, 17618947}, {64045, 17619203}, + {64046, 17619459}, {64047, 17619715}, {64048, 17619971}, {64049, 17620227}, + {64050, 17620483}, {64051, 17620739}, {64052, 17620995}, {64053, 17621251}, + {64054, 17621507}, {64055, 17621763}, {64056, 17622019}, {64057, 17622275}, + {64058, 17622531}, {64059, 17622787}, {64060, 17163523}, {64061, 17623043}, + {64062, 17623299}, {64063, 17623555}, {64064, 17623811}, {64065, 17624067}, + {64066, 17624323}, {64067, 17624579}, {64068, 17624835}, {64069, 17625091}, + {64070, 17625347}, {64071, 17625603}, {64072, 17625859}, {64073, 17626115}, + {64074, 17626371}, {64075, 17626627}, {64076, 17275651}, {64077, 17626883}, + {64078, 17627139}, {64079, 17627395}, {64080, 17627651}, {64081, 17278723}, + {64082, 17627907}, {64083, 17628163}, {64084, 17628419}, {64085, 17628675}, + {64086, 17628931}, {64087, 17586691}, {64088, 17629187}, {64089, 17629443}, + {64090, 17629699}, {64091, 17629955}, {64092, 17630211}, {64093, 17630467}, + {64095, 17630723}, {64096, 17630979}, {64097, 17631235}, {64098, 17631491}, + {64099, 17631747}, {64100, 17632003}, {64101, 17632259}, {64102, 17632515}, + {64103, 17617923}, {64104, 17632771}, {64105, 17633027}, {64106, 17633283}, + {64107, 17633539}, {64108, 17633795}, {64109, 17634051}, {64110, 2}, + {64112, 17634307}, {64113, 17634563}, {64114, 17634819}, {64115, 17635075}, + {64116, 17635331}, {64117, 17635587}, {64118, 17635843}, {64119, 17636099}, + {64120, 17621507}, {64121, 17636355}, {64122, 17636611}, {64123, 17636867}, + {64124, 17614595}, {64125, 17637123}, {64126, 17637379}, {64127, 17637635}, + {64128, 17637891}, {64129, 17638147}, {64130, 17638403}, {64131, 17638659}, + {64132, 17638915}, {64133, 17639171}, {64134, 17639427}, {64135, 17639683}, + {64136, 17639939}, {64137, 17623555}, {64138, 17640195}, {64139, 17623811}, + {64140, 17640451}, {64141, 17640707}, {64142, 17640963}, {64143, 17641219}, + {64144, 17641475}, {64145, 17614851}, {64146, 17560323}, {64147, 17641731}, + {64148, 17641987}, {64149, 17171971}, {64150, 17577731}, {64151, 17598723}, + {64152, 17642243}, {64153, 17642499}, {64154, 17625603}, {64155, 17642755}, + {64156, 17625859}, {64157, 17643011}, {64158, 17643267}, {64159, 17643523}, + {64160, 17615363}, {64161, 17643779}, {64162, 17644035}, {64163, 17644291}, + {64164, 17644547}, {64165, 17644803}, {64166, 17615619}, {64167, 17645059}, + {64168, 17645315}, {64169, 17645571}, {64170, 17645827}, {64171, 17646083}, + {64172, 17646339}, {64173, 17628931}, {64174, 17646595}, {64175, 17646851}, + {64176, 17586691}, {64177, 17647107}, {64178, 17629955}, {64179, 17647363}, + {64180, 17647619}, {64181, 17647875}, {64182, 17648131}, {64183, 17648387}, + {64184, 17631235}, {64185, 17648643}, {64186, 17617667}, {64187, 17648899}, + {64188, 17631491}, {64189, 17572867}, {64190, 17649155}, {64191, 17631747}, + {64192, 17649411}, {64193, 17632259}, {64194, 17649667}, {64195, 17649923}, + {64196, 17650179}, {64197, 17650435}, {64198, 17650691}, {64199, 17632771}, + {64200, 17616899}, {64201, 17650947}, {64202, 17633027}, {64203, 17651203}, + {64204, 17633283}, {64205, 17651459}, {64206, 17206531}, {64207, 17651715}, + {64208, 17651971}, {64209, 17652227}, {64210, 17652483}, {64211, 17652739}, + {64212, 17652995}, {64213, 17653251}, {64214, 17653507}, {64215, 17653763}, + {64216, 17654019}, {64217, 17654275}, {64218, 2}, {64256, 34431747}, + {64257, 34432259}, {64258, 34432771}, {64259, 51209219}, {64260, 51210499}, + {64261, 33559043}, {64263, 2}, {64275, 34434051}, {64276, 34434563}, + {64277, 34435075}, {64278, 34435587}, {64279, 34436099}, {64280, 2}, + {64285, 34436611}, {64286, 1}, {64287, 34437123}, {64288, 17660419}, + {64289, 17054467}, {64290, 17055235}, {64291, 17660675}, {64292, 17660931}, + {64293, 17661187}, {64294, 17661443}, {64295, 17661699}, {64296, 17661955}, + {64297, 17047043}, {64298, 34439427}, {64299, 34439939}, {64300, 51217667}, + {64301, 51218435}, {64302, 34441987}, {64303, 34442499}, {64304, 34443011}, + {64305, 34443523}, {64306, 34444035}, {64307, 34444547}, {64308, 34445059}, + {64309, 34445571}, {64310, 34446083}, {64311, 2}, {64312, 34446595}, + {64313, 34447107}, {64314, 34447619}, {64315, 34448131}, {64316, 34448643}, + {64317, 2}, {64318, 34449155}, {64319, 2}, {64320, 34449667}, + {64321, 34450179}, {64322, 2}, {64323, 34450691}, {64324, 34451203}, + {64325, 2}, {64326, 34451715}, {64327, 34452227}, {64328, 34452739}, + {64329, 34440451}, {64330, 34453251}, {64331, 34453763}, {64332, 34454275}, + {64333, 34454787}, {64334, 34455299}, {64335, 34455811}, {64336, 17679107}, + {64338, 17679363}, {64342, 17679619}, {64346, 17679875}, {64350, 17680131}, + {64354, 17680387}, {64358, 17680643}, {64362, 17680899}, {64366, 17681155}, + {64370, 17681411}, {64374, 17681667}, {64378, 17681923}, {64382, 17682179}, + {64386, 17682435}, {64388, 17682691}, {64390, 17682947}, {64392, 17683203}, + {64394, 17683459}, {64396, 17683715}, {64398, 17683971}, {64402, 17684227}, + {64406, 17684483}, {64410, 17684739}, {64414, 17684995}, {64416, 17685251}, + {64420, 17685507}, {64422, 17685763}, {64426, 17686019}, {64430, 17686275}, + {64432, 17686531}, {64434, 1}, {64451, 2}, {64467, 17686787}, + {64471, 16911619}, {64473, 17687043}, {64475, 17687299}, {64477, 33688835}, + {64478, 17687555}, {64480, 17687811}, {64482, 17688067}, {64484, 17688323}, + {64488, 17688579}, {64490, 34466051}, {64492, 34466563}, {64494, 34467075}, + {64496, 34467587}, {64498, 34468099}, {64500, 34468611}, {64502, 34469123}, + {64505, 34469635}, {64508, 17692931}, {64512, 34470403}, {64513, 34470915}, + {64514, 34471427}, {64515, 34469635}, {64516, 34471939}, {64517, 34472451}, + {64518, 34472963}, {64519, 34473475}, {64520, 34473987}, {64521, 34474499}, + {64522, 34475011}, {64523, 34475523}, {64524, 34476035}, {64525, 34476547}, + {64526, 34477059}, {64527, 34477571}, {64528, 34478083}, {64529, 34478595}, + {64530, 34479107}, {64531, 34479619}, {64532, 34480131}, {64533, 34480643}, + {64534, 34481155}, {64535, 34480899}, {64536, 34481667}, {64537, 34482179}, + {64538, 34482691}, {64539, 34483203}, {64540, 34483715}, {64541, 34484227}, + {64542, 34484739}, {64543, 34485251}, {64544, 34485763}, {64545, 34486275}, + {64546, 34486787}, {64547, 34487299}, {64548, 34487811}, {64549, 34488323}, + {64550, 34488835}, {64551, 34489347}, {64552, 34489859}, {64553, 34490371}, + {64554, 34490883}, {64555, 34491395}, {64556, 34491907}, {64557, 34492419}, + {64558, 34492931}, {64559, 34493443}, {64560, 34493955}, {64561, 34494467}, + {64562, 34494979}, {64563, 34495491}, {64564, 34496003}, {64565, 34496515}, + {64566, 34497027}, {64567, 34497539}, {64568, 34498051}, {64569, 34498563}, + {64570, 34499075}, {64571, 34499587}, {64572, 34500099}, {64573, 34500611}, + {64574, 34501123}, {64575, 34501635}, {64576, 34502147}, {64577, 34502659}, + {64578, 34503171}, {64579, 34503683}, {64580, 34504195}, {64581, 34504707}, + {64582, 34481411}, {64583, 34481923}, {64584, 34505219}, {64585, 34505731}, + {64586, 34506243}, {64587, 34506755}, {64588, 34507267}, {64589, 34507779}, + {64590, 34508291}, {64591, 34508803}, {64592, 34509315}, {64593, 34509827}, + {64594, 34510339}, {64595, 34510851}, {64596, 34511363}, {64597, 34480387}, + {64598, 34511875}, {64599, 34512387}, {64600, 34504451}, {64601, 34512899}, + {64602, 34511619}, {64603, 34513411}, {64604, 34513923}, {64605, 34514435}, + {64606, 51292163}, {64607, 51292931}, {64608, 51293699}, {64609, 51294467}, + {64610, 51295235}, {64611, 51296003}, {64612, 34519555}, {64613, 34520067}, + {64614, 34471427}, {64615, 34520579}, {64616, 34469635}, {64617, 34471939}, + {64618, 34521091}, {64619, 34521603}, {64620, 34473987}, {64621, 34522115}, + {64622, 34474499}, {64623, 34475011}, {64624, 34522627}, {64625, 34523139}, + {64626, 34477059}, {64627, 34523651}, {64628, 34477571}, {64629, 34478083}, + {64630, 34524163}, {64631, 34524675}, {64632, 34479107}, {64633, 34525187}, + {64634, 34479619}, {64635, 34480131}, {64636, 34494467}, {64637, 34494979}, + {64638, 34496515}, {64639, 34497027}, {64640, 34497539}, {64641, 34499587}, + {64642, 34500099}, {64643, 34500611}, {64644, 34501123}, {64645, 34503171}, + {64646, 34503683}, {64647, 34504195}, {64648, 34525699}, {64649, 34505219}, + {64650, 34526211}, {64651, 34526723}, {64652, 34508291}, {64653, 34527235}, + {64654, 34508803}, {64655, 34509315}, {64656, 34514435}, {64657, 34527747}, + {64658, 34528259}, {64659, 34504451}, {64660, 34506499}, {64661, 34512899}, + {64662, 34511619}, {64663, 34470403}, {64664, 34470915}, {64665, 34528771}, + {64666, 34471427}, {64667, 34529283}, {64668, 34472451}, {64669, 34472963}, + {64670, 34473475}, {64671, 34473987}, {64672, 34529795}, {64673, 34475523}, + {64674, 34476035}, {64675, 34476547}, {64676, 34477059}, {64677, 34530307}, + {64678, 34479107}, {64679, 34480643}, {64680, 34481155}, {64681, 34480899}, + {64682, 34481667}, {64683, 34482179}, {64684, 34483203}, {64685, 34483715}, + {64686, 34484227}, {64687, 34484739}, {64688, 34485251}, {64689, 34485763}, + {64690, 34530819}, {64691, 34486275}, {64692, 34486787}, {64693, 34487299}, + {64694, 34487811}, {64695, 34488323}, {64696, 34488835}, {64697, 34489859}, + {64698, 34490371}, {64699, 34490883}, {64700, 34491395}, {64701, 34491907}, + {64702, 34492419}, {64703, 34492931}, {64704, 34493443}, {64705, 34493955}, + {64706, 34495491}, {64707, 34496003}, {64708, 34498051}, {64709, 34498563}, + {64710, 34499075}, {64711, 34499587}, {64712, 34500099}, {64713, 34501635}, + {64714, 34502147}, {64715, 34502659}, {64716, 34503171}, {64717, 34531331}, + {64718, 34504707}, {64719, 34481411}, {64720, 34481923}, {64721, 34505219}, + {64722, 34506755}, {64723, 34507267}, {64724, 34507779}, {64725, 34508291}, + {64726, 34531843}, {64727, 34509827}, {64728, 34510339}, {64729, 34532355}, + {64730, 34480387}, {64731, 34511875}, {64732, 34512387}, {64733, 34504451}, + {64734, 34509571}, {64735, 34471427}, {64736, 34529283}, {64737, 34473987}, + {64738, 34529795}, {64739, 34477059}, {64740, 34530307}, {64741, 34479107}, + {64742, 34532867}, {64743, 34485251}, {64744, 34533379}, {64745, 34533891}, + {64746, 34534403}, {64747, 34499587}, {64748, 34500099}, {64749, 34503171}, + {64750, 34508291}, {64751, 34531843}, {64752, 34504451}, {64753, 34509571}, + {64754, 51312131}, {64755, 51312899}, {64756, 51313667}, {64757, 34537219}, + {64758, 34537731}, {64759, 34538243}, {64760, 34538755}, {64761, 34539267}, + {64762, 34539779}, {64763, 34540291}, {64764, 34540803}, {64765, 34541315}, + {64766, 34541827}, {64767, 34542339}, {64768, 34512131}, {64769, 34542851}, + {64770, 34543363}, {64771, 34543875}, {64772, 34512643}, {64773, 34544387}, + {64774, 34544899}, {64775, 34545411}, {64776, 34545923}, {64777, 34546435}, + {64778, 34546947}, {64779, 34547459}, {64780, 34533891}, {64781, 34547971}, + {64782, 34548483}, {64783, 34548995}, {64784, 34549507}, {64785, 34537219}, + {64786, 34537731}, {64787, 34538243}, {64788, 34538755}, {64789, 34539267}, + {64790, 34539779}, {64791, 34540291}, {64792, 34540803}, {64793, 34541315}, + {64794, 34541827}, {64795, 34542339}, {64796, 34512131}, {64797, 34542851}, + {64798, 34543363}, {64799, 34543875}, {64800, 34512643}, {64801, 34544387}, + {64802, 34544899}, {64803, 34545411}, {64804, 34545923}, {64805, 34546435}, + {64806, 34546947}, {64807, 34547459}, {64808, 34533891}, {64809, 34547971}, + {64810, 34548483}, {64811, 34548995}, {64812, 34549507}, {64813, 34546435}, + {64814, 34546947}, {64815, 34547459}, {64816, 34533891}, {64817, 34533379}, + {64818, 34534403}, {64819, 34489347}, {64820, 34483715}, {64821, 34484227}, + {64822, 34484739}, {64823, 34546435}, {64824, 34546947}, {64825, 34547459}, + {64826, 34489347}, {64827, 34489859}, {64828, 34550019}, {64830, 1}, + {64848, 51327747}, {64849, 51328515}, {64851, 51329283}, {64852, 51330051}, + {64853, 51330819}, {64854, 51331587}, {64855, 51332355}, {64856, 51258371}, + {64858, 51333123}, {64859, 51333891}, {64860, 51334659}, {64861, 51335427}, + {64862, 51336195}, {64863, 51336963}, {64865, 51337731}, {64866, 51338499}, + {64868, 51339267}, {64870, 51340035}, {64871, 51340803}, {64873, 51341571}, + {64874, 51342339}, {64876, 51343107}, {64878, 51343875}, {64879, 51344643}, + {64881, 51345411}, {64883, 51346179}, {64884, 51346947}, {64885, 51347715}, + {64886, 51348483}, {64888, 51349251}, {64889, 51350019}, {64890, 51350787}, + {64891, 51351555}, {64892, 51352323}, {64894, 51353091}, {64895, 51353859}, + {64896, 51354627}, {64897, 51355395}, {64898, 51356163}, {64899, 51356931}, + {64901, 51357699}, {64903, 51358467}, {64905, 51359235}, {64906, 51258627}, + {64907, 51360003}, {64908, 51360771}, {64909, 51281923}, {64910, 51259139}, + {64911, 51361539}, {64912, 2}, {64914, 51362307}, {64915, 51363075}, + {64916, 51363843}, {64917, 51364611}, {64918, 51365379}, {64919, 51366147}, + {64921, 51366915}, {64922, 51367683}, {64923, 51368451}, {64924, 51369219}, + {64926, 51369987}, {64927, 51370755}, {64928, 51371523}, {64929, 51372291}, + {64930, 51373059}, {64931, 51373827}, {64932, 51374595}, {64933, 51375363}, + {64934, 51376131}, {64935, 51376899}, {64936, 51377667}, {64937, 51378435}, + {64938, 51379203}, {64939, 51379971}, {64940, 51380739}, {64941, 51381507}, + {64942, 51289091}, {64943, 51382275}, {64944, 51383043}, {64945, 51383811}, + {64946, 51384579}, {64947, 51385347}, {64948, 51353091}, {64949, 51354627}, + {64950, 51386115}, {64951, 51386883}, {64952, 51387651}, {64953, 51388419}, + {64954, 51389187}, {64955, 51389955}, {64956, 51389187}, {64957, 51387651}, + {64958, 51390723}, {64959, 51391491}, {64960, 51392259}, {64961, 51393027}, + {64962, 51393795}, {64963, 51389955}, {64964, 51347715}, {64965, 51340035}, + {64966, 51394563}, {64967, 51395331}, {64968, 2}, {64975, 1}, + {64976, 2}, {65008, 51396099}, {65009, 51396867}, {65010, 68174851}, + {65011, 68175875}, {65012, 68176899}, {65013, 68177923}, {65014, 68178947}, + {65015, 68179971}, {65016, 68180995}, {65017, 51404803}, {65018, 303063811}, + {65019, 135296259}, {65020, 68189443}, {65021, 1}, {65024, 0}, + {65040, 17858819}, {65041, 17859075}, {65042, 2}, {65043, 17121027}, + {65044, 16848643}, {65045, 17042947}, {65046, 17043971}, {65047, 17859331}, + {65048, 17859587}, {65049, 2}, {65056, 1}, {65072, 2}, + {65073, 17859843}, {65074, 17860099}, {65075, 17860355}, {65077, 17047811}, + {65078, 17048067}, {65079, 17860611}, {65080, 17860867}, {65081, 17861123}, + {65082, 17861379}, {65083, 17861635}, {65084, 17861891}, {65085, 17862147}, + {65086, 17862403}, {65087, 17077763}, {65088, 17078019}, {65089, 17862659}, + {65090, 17862915}, {65091, 17863171}, {65092, 17863427}, {65093, 1}, + {65095, 17863683}, {65096, 17863939}, {65097, 33820675}, {65101, 17860355}, + {65104, 17858819}, {65105, 17859075}, {65106, 2}, {65108, 16848643}, + {65109, 17121027}, {65110, 17043971}, {65111, 17042947}, {65112, 17859843}, + {65113, 17047811}, {65114, 17048067}, {65115, 17860611}, {65116, 17860867}, + {65117, 17861123}, {65118, 17861379}, {65119, 17864195}, {65120, 17864451}, + {65121, 17864707}, {65122, 17047043}, {65123, 17864963}, {65124, 17865219}, + {65125, 17865475}, {65126, 17047555}, {65127, 2}, {65128, 17865731}, + {65129, 17865987}, {65130, 17866243}, {65131, 17866499}, {65132, 2}, + {65136, 34643971}, {65137, 34644483}, {65138, 34514947}, {65139, 1}, + {65140, 34515715}, {65141, 2}, {65142, 34516483}, {65143, 34534915}, + {65144, 34517251}, {65145, 34535683}, {65146, 34518019}, {65147, 34536451}, + {65148, 34518787}, {65149, 34644995}, {65150, 34645507}, {65151, 34646019}, + {65152, 17869315}, {65153, 17869571}, {65155, 17869827}, {65157, 17870083}, + {65159, 17870339}, {65161, 17688835}, {65165, 16910595}, {65167, 17695235}, + {65171, 17870595}, {65173, 17698307}, {65177, 17701379}, {65181, 17693443}, + {65185, 17693955}, {65189, 17696515}, {65193, 17846019}, {65195, 17736195}, + {65197, 17736707}, {65199, 17743107}, {65201, 17706499}, {65205, 17756675}, + {65209, 17708547}, {65213, 17709571}, {65217, 17711619}, {65221, 17712643}, + {65225, 17713155}, {65229, 17714179}, {65233, 17715203}, {65237, 17718275}, + {65241, 17720323}, {65245, 17722627}, {65249, 17694467}, {65253, 17729539}, + {65257, 17732611}, {65261, 16911107}, {65263, 17688579}, {65265, 16912131}, + {65269, 34648067}, {65271, 34648579}, {65273, 34649091}, {65275, 34633987}, + {65277, 2}, {65279, 0}, {65280, 2}, {65281, 17042947}, + {65282, 17872387}, {65283, 17864195}, {65284, 17865987}, {65285, 17866243}, + {65286, 17864451}, {65287, 17872643}, {65288, 17047811}, {65289, 17048067}, + {65290, 17864707}, {65291, 17047043}, {65292, 17858819}, {65293, 17864963}, + {65294, 17207043}, {65295, 17048579}, {65296, 17045507}, {65297, 16786947}, + {65298, 16785155}, {65299, 16785411}, {65300, 16787715}, {65301, 17045763}, + {65302, 17046019}, {65303, 17046275}, {65304, 17046531}, {65305, 17046787}, + {65306, 17121027}, {65307, 16848643}, {65308, 17865219}, {65309, 17047555}, + {65310, 17865475}, {65311, 17043971}, {65312, 17866499}, {65313, 16777219}, + {65314, 16777475}, {65315, 16777731}, {65316, 16777987}, {65317, 16778243}, + {65318, 16778499}, {65319, 16778755}, {65320, 16779011}, {65321, 16779267}, + {65322, 16779523}, {65323, 16779779}, {65324, 16780035}, {65325, 16780291}, + {65326, 16780547}, {65327, 16780803}, {65328, 16781059}, {65329, 16781315}, + {65330, 16781571}, {65331, 16781827}, {65332, 16782083}, {65333, 16782339}, + {65334, 16782595}, {65335, 16782851}, {65336, 16783107}, {65337, 16783363}, + {65338, 16783619}, {65339, 17863683}, {65340, 17865731}, {65341, 17863939}, + {65342, 17872899}, {65343, 17860355}, {65344, 17037059}, {65345, 16777219}, + {65346, 16777475}, {65347, 16777731}, {65348, 16777987}, {65349, 16778243}, + {65350, 16778499}, {65351, 16778755}, {65352, 16779011}, {65353, 16779267}, + {65354, 16779523}, {65355, 16779779}, {65356, 16780035}, {65357, 16780291}, + {65358, 16780547}, {65359, 16780803}, {65360, 16781059}, {65361, 16781315}, + {65362, 16781571}, {65363, 16781827}, {65364, 16782083}, {65365, 16782339}, + {65366, 16782595}, {65367, 16782851}, {65368, 16783107}, {65369, 16783363}, + {65370, 16783619}, {65371, 17860611}, {65372, 17873155}, {65373, 17860867}, + {65374, 17873411}, {65375, 17873667}, {65376, 17873923}, {65377, 17207043}, + {65378, 17862659}, {65379, 17862915}, {65380, 17859075}, {65381, 17874179}, + {65382, 17327363}, {65383, 17329923}, {65384, 17372931}, {65385, 17874435}, + {65386, 17374467}, {65387, 17334019}, {65388, 17874691}, {65389, 17344259}, + {65390, 17390083}, {65391, 17339651}, {65392, 17328643}, {65393, 17316099}, + {65394, 17316355}, {65395, 17316611}, {65396, 17316867}, {65397, 17317123}, + {65398, 17317379}, {65399, 17317635}, {65400, 17317891}, {65401, 17318147}, + {65402, 17209603}, {65403, 17318403}, {65404, 17318659}, {65405, 17318915}, + {65406, 17319171}, {65407, 17319427}, {65408, 17319683}, {65409, 17319939}, + {65410, 17320195}, {65411, 17320451}, {65412, 17209859}, {65413, 17320707}, + {65414, 17320963}, {65415, 17321219}, {65416, 17321475}, {65417, 17321731}, + {65418, 17321987}, {65419, 17322243}, {65420, 17322499}, {65421, 17322755}, + {65422, 17323011}, {65423, 17323267}, {65424, 17323523}, {65425, 17323779}, + {65426, 17324035}, {65427, 17324291}, {65428, 17324547}, {65429, 17324803}, + {65430, 17325059}, {65431, 17325315}, {65432, 17325571}, {65433, 17325827}, + {65434, 17326083}, {65435, 17326339}, {65436, 17326595}, {65437, 17330435}, + {65438, 17208323}, {65439, 17208835}, {65440, 0}, {65441, 17210115}, + {65442, 17210371}, {65443, 17210627}, {65444, 17210883}, {65445, 17211139}, + {65446, 17211395}, {65447, 17211651}, {65448, 17211907}, {65449, 17212163}, + {65450, 17212419}, {65451, 17212675}, {65452, 17212931}, {65453, 17213187}, + {65454, 17213443}, {65455, 17213699}, {65456, 17213955}, {65457, 17214211}, + {65458, 17214467}, {65459, 17214723}, {65460, 17214979}, {65461, 17215235}, + {65462, 17215491}, {65463, 17215747}, {65464, 17216003}, {65465, 17216259}, + {65466, 17216515}, {65467, 17216771}, {65468, 17217027}, {65469, 17217283}, + {65470, 17217539}, {65471, 2}, {65474, 17217795}, {65475, 17218051}, + {65476, 17218307}, {65477, 17218563}, {65478, 17218819}, {65479, 17219075}, + {65480, 2}, {65482, 17219331}, {65483, 17219587}, {65484, 17219843}, + {65485, 17220099}, {65486, 17220355}, {65487, 17220611}, {65488, 2}, + {65490, 17220867}, {65491, 17221123}, {65492, 17221379}, {65493, 17221635}, + {65494, 17221891}, {65495, 17222147}, {65496, 2}, {65498, 17222403}, + {65499, 17222659}, {65500, 17222915}, {65501, 2}, {65504, 17874947}, + {65505, 17875203}, {65506, 17875459}, {65507, 33561859}, {65508, 17875715}, + {65509, 17875971}, {65510, 17876227}, {65511, 2}, {65512, 17876483}, + {65513, 17876739}, {65514, 17876995}, {65515, 17877251}, {65516, 17877507}, + {65517, 17877763}, {65518, 17878019}, {65519, 2}, {65536, 1}, + {65548, 2}, {65549, 1}, {65575, 2}, {65576, 1}, + {65595, 2}, {65596, 1}, {65598, 2}, {65599, 1}, + {65614, 2}, {65616, 1}, {65630, 2}, {65664, 1}, + {65787, 2}, {65792, 1}, {65795, 2}, {65799, 1}, + {65844, 2}, {65847, 1}, {65935, 2}, {65936, 1}, + {65949, 2}, {65952, 1}, {65953, 2}, {66000, 1}, + {66046, 2}, {66176, 1}, {66205, 2}, {66208, 1}, + {66257, 2}, {66272, 1}, {66300, 2}, {66304, 1}, + {66340, 2}, {66349, 1}, {66379, 2}, {66384, 1}, + {66427, 2}, {66432, 1}, {66462, 2}, {66463, 1}, + {66500, 2}, {66504, 1}, {66518, 2}, {66560, 17878275}, + {66561, 17878531}, {66562, 17878787}, {66563, 17879043}, {66564, 17879299}, + {66565, 17879555}, {66566, 17879811}, {66567, 17880067}, {66568, 17880323}, + {66569, 17880579}, {66570, 17880835}, {66571, 17881091}, {66572, 17881347}, + {66573, 17881603}, {66574, 17881859}, {66575, 17882115}, {66576, 17882371}, + {66577, 17882627}, {66578, 17882883}, {66579, 17883139}, {66580, 17883395}, + {66581, 17883651}, {66582, 17883907}, {66583, 17884163}, {66584, 17884419}, + {66585, 17884675}, {66586, 17884931}, {66587, 17885187}, {66588, 17885443}, + {66589, 17885699}, {66590, 17885955}, {66591, 17886211}, {66592, 17886467}, + {66593, 17886723}, {66594, 17886979}, {66595, 17887235}, {66596, 17887491}, + {66597, 17887747}, {66598, 17888003}, {66599, 17888259}, {66600, 1}, + {66718, 2}, {66720, 1}, {66730, 2}, {66736, 17888515}, + {66737, 17888771}, {66738, 17889027}, {66739, 17889283}, {66740, 17889539}, + {66741, 17889795}, {66742, 17890051}, {66743, 17890307}, {66744, 17890563}, + {66745, 17890819}, {66746, 17891075}, {66747, 17891331}, {66748, 17891587}, + {66749, 17891843}, {66750, 17892099}, {66751, 17892355}, {66752, 17892611}, + {66753, 17892867}, {66754, 17893123}, {66755, 17893379}, {66756, 17893635}, + {66757, 17893891}, {66758, 17894147}, {66759, 17894403}, {66760, 17894659}, + {66761, 17894915}, {66762, 17895171}, {66763, 17895427}, {66764, 17895683}, + {66765, 17895939}, {66766, 17896195}, {66767, 17896451}, {66768, 17896707}, + {66769, 17896963}, {66770, 17897219}, {66771, 17897475}, {66772, 2}, + {66776, 1}, {66812, 2}, {66816, 1}, {66856, 2}, + {66864, 1}, {66916, 2}, {66927, 1}, {66928, 17897731}, + {66929, 17897987}, {66930, 17898243}, {66931, 17898499}, {66932, 17898755}, + {66933, 17899011}, {66934, 17899267}, {66935, 17899523}, {66936, 17899779}, + {66937, 17900035}, {66938, 17900291}, {66939, 2}, {66940, 17900547}, + {66941, 17900803}, {66942, 17901059}, {66943, 17901315}, {66944, 17901571}, + {66945, 17901827}, {66946, 17902083}, {66947, 17902339}, {66948, 17902595}, + {66949, 17902851}, {66950, 17903107}, {66951, 17903363}, {66952, 17903619}, + {66953, 17903875}, {66954, 17904131}, {66955, 2}, {66956, 17904387}, + {66957, 17904643}, {66958, 17904899}, {66959, 17905155}, {66960, 17905411}, + {66961, 17905667}, {66962, 17905923}, {66963, 2}, {66964, 17906179}, + {66965, 17906435}, {66966, 2}, {66967, 1}, {66978, 2}, + {66979, 1}, {66994, 2}, {66995, 1}, {67002, 2}, + {67003, 1}, {67005, 2}, {67008, 1}, {67060, 2}, + {67072, 1}, {67383, 2}, {67392, 1}, {67414, 2}, + {67424, 1}, {67432, 2}, {67456, 1}, {67457, 17906691}, + {67458, 17906947}, {67459, 16791043}, {67460, 17907203}, {67461, 16814083}, + {67462, 2}, {67463, 17907459}, {67464, 17907715}, {67465, 17907971}, + {67466, 17908227}, {67467, 16815363}, {67468, 16815619}, {67469, 17908483}, + {67470, 17908739}, {67471, 17908995}, {67472, 17909251}, {67473, 17527555}, + {67474, 17909507}, {67475, 16817155}, {67476, 17909763}, {67477, 16802051}, + {67478, 17910019}, {67479, 17910275}, {67480, 17910531}, {67481, 17910787}, + {67482, 17911043}, {67483, 17523459}, {67484, 17911299}, {67485, 17911555}, + {67486, 17911811}, {67487, 17912067}, {67488, 17912323}, {67489, 17912579}, + {67490, 16795395}, {67491, 17912835}, {67492, 17913091}, {67493, 16781315}, + {67494, 17913347}, {67495, 17913603}, {67496, 17135875}, {67497, 17913859}, + {67498, 16819971}, {67499, 17914115}, {67500, 17914371}, {67501, 17914627}, + {67502, 17914883}, {67503, 16820995}, {67504, 17915139}, {67505, 2}, + {67506, 17915395}, {67507, 17915651}, {67508, 17915907}, {67509, 17916163}, + {67510, 17916419}, {67511, 17916675}, {67512, 17916931}, {67513, 17917187}, + {67514, 17917443}, {67515, 2}, {67584, 1}, {67590, 2}, + {67592, 1}, {67593, 2}, {67594, 1}, {67638, 2}, + {67639, 1}, {67641, 2}, {67644, 1}, {67645, 2}, + {67647, 1}, {67670, 2}, {67671, 1}, {67743, 2}, + {67751, 1}, {67760, 2}, {67808, 1}, {67827, 2}, + {67828, 1}, {67830, 2}, {67835, 1}, {67868, 2}, + {67871, 1}, {67898, 2}, {67903, 1}, {67904, 2}, + {67968, 1}, {68024, 2}, {68028, 1}, {68048, 2}, + {68050, 1}, {68100, 2}, {68101, 1}, {68103, 2}, + {68108, 1}, {68116, 2}, {68117, 1}, {68120, 2}, + {68121, 1}, {68150, 2}, {68152, 1}, {68155, 2}, + {68159, 1}, {68169, 2}, {68176, 1}, {68185, 2}, + {68192, 1}, {68256, 2}, {68288, 1}, {68327, 2}, + {68331, 1}, {68343, 2}, {68352, 1}, {68406, 2}, + {68409, 1}, {68438, 2}, {68440, 1}, {68467, 2}, + {68472, 1}, {68498, 2}, {68505, 1}, {68509, 2}, + {68521, 1}, {68528, 2}, {68608, 1}, {68681, 2}, + {68736, 17917699}, {68737, 17917955}, {68738, 17918211}, {68739, 17918467}, + {68740, 17918723}, {68741, 17918979}, {68742, 17919235}, {68743, 17919491}, + {68744, 17919747}, {68745, 17920003}, {68746, 17920259}, {68747, 17920515}, + {68748, 17920771}, {68749, 17921027}, {68750, 17921283}, {68751, 17921539}, + {68752, 17921795}, {68753, 17922051}, {68754, 17922307}, {68755, 17922563}, + {68756, 17922819}, {68757, 17923075}, {68758, 17923331}, {68759, 17923587}, + {68760, 17923843}, {68761, 17924099}, {68762, 17924355}, {68763, 17924611}, + {68764, 17924867}, {68765, 17925123}, {68766, 17925379}, {68767, 17925635}, + {68768, 17925891}, {68769, 17926147}, {68770, 17926403}, {68771, 17926659}, + {68772, 17926915}, {68773, 17927171}, {68774, 17927427}, {68775, 17927683}, + {68776, 17927939}, {68777, 17928195}, {68778, 17928451}, {68779, 17928707}, + {68780, 17928963}, {68781, 17929219}, {68782, 17929475}, {68783, 17929731}, + {68784, 17929987}, {68785, 17930243}, {68786, 17930499}, {68787, 2}, + {68800, 1}, {68851, 2}, {68858, 1}, {68904, 2}, + {68912, 1}, {68922, 2}, {68928, 1}, {68944, 17930755}, + {68945, 17931011}, {68946, 17931267}, {68947, 17931523}, {68948, 17931779}, + {68949, 17932035}, {68950, 17932291}, {68951, 17932547}, {68952, 17932803}, + {68953, 17933059}, {68954, 17933315}, {68955, 17933571}, {68956, 17933827}, + {68957, 17934083}, {68958, 17934339}, {68959, 17934595}, {68960, 17934851}, + {68961, 17935107}, {68962, 17935363}, {68963, 17935619}, {68964, 17935875}, + {68965, 17936131}, {68966, 2}, {68969, 1}, {68998, 2}, + {69006, 1}, {69008, 2}, {69216, 1}, {69247, 2}, + {69248, 1}, {69290, 2}, {69291, 1}, {69294, 2}, + {69296, 1}, {69298, 2}, {69314, 1}, {69317, 2}, + {69372, 1}, {69416, 2}, {69424, 1}, {69466, 2}, + {69488, 1}, {69514, 2}, {69552, 1}, {69580, 2}, + {69600, 1}, {69623, 2}, {69632, 1}, {69710, 2}, + {69714, 1}, {69750, 2}, {69759, 1}, {69821, 2}, + {69822, 1}, {69827, 2}, {69840, 1}, {69865, 2}, + {69872, 1}, {69882, 2}, {69888, 1}, {69941, 2}, + {69942, 1}, {69960, 2}, {69968, 1}, {70007, 2}, + {70016, 1}, {70112, 2}, {70113, 1}, {70133, 2}, + {70144, 1}, {70162, 2}, {70163, 1}, {70210, 2}, + {70272, 1}, {70279, 2}, {70280, 1}, {70281, 2}, + {70282, 1}, {70286, 2}, {70287, 1}, {70302, 2}, + {70303, 1}, {70314, 2}, {70320, 1}, {70379, 2}, + {70384, 1}, {70394, 2}, {70400, 1}, {70404, 2}, + {70405, 1}, {70413, 2}, {70415, 1}, {70417, 2}, + {70419, 1}, {70441, 2}, {70442, 1}, {70449, 2}, + {70450, 1}, {70452, 2}, {70453, 1}, {70458, 2}, + {70459, 1}, {70469, 2}, {70471, 1}, {70473, 2}, + {70475, 1}, {70478, 2}, {70480, 1}, {70481, 2}, + {70487, 1}, {70488, 2}, {70493, 1}, {70500, 2}, + {70502, 1}, {70509, 2}, {70512, 1}, {70517, 2}, + {70528, 1}, {70538, 2}, {70539, 1}, {70540, 2}, + {70542, 1}, {70543, 2}, {70544, 1}, {70582, 2}, + {70583, 1}, {70593, 2}, {70594, 1}, {70595, 2}, + {70597, 1}, {70598, 2}, {70599, 1}, {70603, 2}, + {70604, 1}, {70614, 2}, {70615, 1}, {70617, 2}, + {70625, 1}, {70627, 2}, {70656, 1}, {70748, 2}, + {70749, 1}, {70754, 2}, {70784, 1}, {70856, 2}, + {70864, 1}, {70874, 2}, {71040, 1}, {71094, 2}, + {71096, 1}, {71134, 2}, {71168, 1}, {71237, 2}, + {71248, 1}, {71258, 2}, {71264, 1}, {71277, 2}, + {71296, 1}, {71354, 2}, {71360, 1}, {71370, 2}, + {71376, 1}, {71396, 2}, {71424, 1}, {71451, 2}, + {71453, 1}, {71468, 2}, {71472, 1}, {71495, 2}, + {71680, 1}, {71740, 2}, {71840, 17936387}, {71841, 17936643}, + {71842, 17936899}, {71843, 17937155}, {71844, 17937411}, {71845, 17937667}, + {71846, 17937923}, {71847, 17938179}, {71848, 17938435}, {71849, 17938691}, + {71850, 17938947}, {71851, 17939203}, {71852, 17939459}, {71853, 17939715}, + {71854, 17939971}, {71855, 17940227}, {71856, 17940483}, {71857, 17940739}, + {71858, 17940995}, {71859, 17941251}, {71860, 17941507}, {71861, 17941763}, + {71862, 17942019}, {71863, 17942275}, {71864, 17942531}, {71865, 17942787}, + {71866, 17943043}, {71867, 17943299}, {71868, 17943555}, {71869, 17943811}, + {71870, 17944067}, {71871, 17944323}, {71872, 1}, {71923, 2}, + {71935, 1}, {71943, 2}, {71945, 1}, {71946, 2}, + {71948, 1}, {71956, 2}, {71957, 1}, {71959, 2}, + {71960, 1}, {71990, 2}, {71991, 1}, {71993, 2}, + {71995, 1}, {72007, 2}, {72016, 1}, {72026, 2}, + {72096, 1}, {72104, 2}, {72106, 1}, {72152, 2}, + {72154, 1}, {72165, 2}, {72192, 1}, {72264, 2}, + {72272, 1}, {72355, 2}, {72368, 1}, {72441, 2}, + {72448, 1}, {72458, 2}, {72640, 1}, {72674, 2}, + {72688, 1}, {72698, 2}, {72704, 1}, {72713, 2}, + {72714, 1}, {72759, 2}, {72760, 1}, {72774, 2}, + {72784, 1}, {72813, 2}, {72816, 1}, {72848, 2}, + {72850, 1}, {72872, 2}, {72873, 1}, {72887, 2}, + {72960, 1}, {72967, 2}, {72968, 1}, {72970, 2}, + {72971, 1}, {73015, 2}, {73018, 1}, {73019, 2}, + {73020, 1}, {73022, 2}, {73023, 1}, {73032, 2}, + {73040, 1}, {73050, 2}, {73056, 1}, {73062, 2}, + {73063, 1}, {73065, 2}, {73066, 1}, {73103, 2}, + {73104, 1}, {73106, 2}, {73107, 1}, {73113, 2}, + {73120, 1}, {73130, 2}, {73440, 1}, {73465, 2}, + {73472, 1}, {73489, 2}, {73490, 1}, {73531, 2}, + {73534, 1}, {73563, 2}, {73648, 1}, {73649, 2}, + {73664, 1}, {73714, 2}, {73727, 1}, {74650, 2}, + {74752, 1}, {74863, 2}, {74864, 1}, {74869, 2}, + {74880, 1}, {75076, 2}, {77712, 1}, {77811, 2}, + {77824, 1}, {78896, 2}, {78912, 1}, {78934, 2}, + {78944, 1}, {82939, 2}, {82944, 1}, {83527, 2}, + {90368, 1}, {90426, 2}, {92160, 1}, {92729, 2}, + {92736, 1}, {92767, 2}, {92768, 1}, {92778, 2}, + {92782, 1}, {92863, 2}, {92864, 1}, {92874, 2}, + {92880, 1}, {92910, 2}, {92912, 1}, {92918, 2}, + {92928, 1}, {92998, 2}, {93008, 1}, {93018, 2}, + {93019, 1}, {93026, 2}, {93027, 1}, {93048, 2}, + {93053, 1}, {93072, 2}, {93504, 1}, {93562, 2}, + {93760, 17944579}, {93761, 17944835}, {93762, 17945091}, {93763, 17945347}, + {93764, 17945603}, {93765, 17945859}, {93766, 17946115}, {93767, 17946371}, + {93768, 17946627}, {93769, 17946883}, {93770, 17947139}, {93771, 17947395}, + {93772, 17947651}, {93773, 17947907}, {93774, 17948163}, {93775, 17948419}, + {93776, 17948675}, {93777, 17948931}, {93778, 17949187}, {93779, 17949443}, + {93780, 17949699}, {93781, 17949955}, {93782, 17950211}, {93783, 17950467}, + {93784, 17950723}, {93785, 17950979}, {93786, 17951235}, {93787, 17951491}, + {93788, 17951747}, {93789, 17952003}, {93790, 17952259}, {93791, 17952515}, + {93792, 1}, {93851, 2}, {93952, 1}, {94027, 2}, + {94031, 1}, {94088, 2}, {94095, 1}, {94112, 2}, + {94176, 1}, {94181, 2}, {94192, 1}, {94194, 2}, + {94208, 1}, {100344, 2}, {100352, 1}, {101590, 2}, + {101631, 1}, {101641, 2}, {110576, 1}, {110580, 2}, + {110581, 1}, {110588, 2}, {110589, 1}, {110591, 2}, + {110592, 1}, {110883, 2}, {110898, 1}, {110899, 2}, + {110928, 1}, {110931, 2}, {110933, 1}, {110934, 2}, + {110948, 1}, {110952, 2}, {110960, 1}, {111356, 2}, + {113664, 1}, {113771, 2}, {113776, 1}, {113789, 2}, + {113792, 1}, {113801, 2}, {113808, 1}, {113818, 2}, + {113820, 1}, {113824, 0}, {113828, 2}, {117760, 1}, + {117974, 16777219}, {117975, 16777475}, {117976, 16777731}, {117977, 16777987}, + {117978, 16778243}, {117979, 16778499}, {117980, 16778755}, {117981, 16779011}, + {117982, 16779267}, {117983, 16779523}, {117984, 16779779}, {117985, 16780035}, + {117986, 16780291}, {117987, 16780547}, {117988, 16780803}, {117989, 16781059}, + {117990, 16781315}, {117991, 16781571}, {117992, 16781827}, {117993, 16782083}, + {117994, 16782339}, {117995, 16782595}, {117996, 16782851}, {117997, 16783107}, + {117998, 16783363}, {117999, 16783619}, {118000, 17045507}, {118001, 16786947}, + {118002, 16785155}, {118003, 16785411}, {118004, 16787715}, {118005, 17045763}, + {118006, 17046019}, {118007, 17046275}, {118008, 17046531}, {118009, 17046787}, + {118010, 2}, {118016, 1}, {118452, 2}, {118528, 1}, + {118574, 2}, {118576, 1}, {118599, 2}, {118608, 1}, + {118724, 2}, {118784, 1}, {119030, 2}, {119040, 1}, + {119079, 2}, {119081, 1}, {119134, 34729987}, {119135, 34730499}, + {119136, 51508227}, {119137, 51508995}, {119138, 51509763}, {119139, 51510531}, + {119140, 51511299}, {119141, 1}, {119155, 0}, {119163, 1}, + {119227, 34734851}, {119228, 34735363}, {119229, 51513091}, {119230, 51513859}, + {119231, 51514627}, {119232, 51515395}, {119233, 1}, {119275, 2}, + {119296, 1}, {119366, 2}, {119488, 1}, {119508, 2}, + {119520, 1}, {119540, 2}, {119552, 1}, {119639, 2}, + {119648, 1}, {119673, 2}, {119808, 16777219}, {119809, 16777475}, + {119810, 16777731}, {119811, 16777987}, {119812, 16778243}, {119813, 16778499}, + {119814, 16778755}, {119815, 16779011}, {119816, 16779267}, {119817, 16779523}, + {119818, 16779779}, {119819, 16780035}, {119820, 16780291}, {119821, 16780547}, + {119822, 16780803}, {119823, 16781059}, {119824, 16781315}, {119825, 16781571}, + {119826, 16781827}, {119827, 16782083}, {119828, 16782339}, {119829, 16782595}, + {119830, 16782851}, {119831, 16783107}, {119832, 16783363}, {119833, 16783619}, + {119834, 16777219}, {119835, 16777475}, {119836, 16777731}, {119837, 16777987}, + {119838, 16778243}, {119839, 16778499}, {119840, 16778755}, {119841, 16779011}, + {119842, 16779267}, {119843, 16779523}, {119844, 16779779}, {119845, 16780035}, + {119846, 16780291}, {119847, 16780547}, {119848, 16780803}, {119849, 16781059}, + {119850, 16781315}, {119851, 16781571}, {119852, 16781827}, {119853, 16782083}, + {119854, 16782339}, {119855, 16782595}, {119856, 16782851}, {119857, 16783107}, + {119858, 16783363}, {119859, 16783619}, {119860, 16777219}, {119861, 16777475}, + {119862, 16777731}, {119863, 16777987}, {119864, 16778243}, {119865, 16778499}, + {119866, 16778755}, {119867, 16779011}, {119868, 16779267}, {119869, 16779523}, + {119870, 16779779}, {119871, 16780035}, {119872, 16780291}, {119873, 16780547}, + {119874, 16780803}, {119875, 16781059}, {119876, 16781315}, {119877, 16781571}, + {119878, 16781827}, {119879, 16782083}, {119880, 16782339}, {119881, 16782595}, + {119882, 16782851}, {119883, 16783107}, {119884, 16783363}, {119885, 16783619}, + {119886, 16777219}, {119887, 16777475}, {119888, 16777731}, {119889, 16777987}, + {119890, 16778243}, {119891, 16778499}, {119892, 16778755}, {119893, 2}, + {119894, 16779267}, {119895, 16779523}, {119896, 16779779}, {119897, 16780035}, + {119898, 16780291}, {119899, 16780547}, {119900, 16780803}, {119901, 16781059}, + {119902, 16781315}, {119903, 16781571}, {119904, 16781827}, {119905, 16782083}, + {119906, 16782339}, {119907, 16782595}, {119908, 16782851}, {119909, 16783107}, + {119910, 16783363}, {119911, 16783619}, {119912, 16777219}, {119913, 16777475}, + {119914, 16777731}, {119915, 16777987}, {119916, 16778243}, {119917, 16778499}, + {119918, 16778755}, {119919, 16779011}, {119920, 16779267}, {119921, 16779523}, + {119922, 16779779}, {119923, 16780035}, {119924, 16780291}, {119925, 16780547}, + {119926, 16780803}, {119927, 16781059}, {119928, 16781315}, {119929, 16781571}, + {119930, 16781827}, {119931, 16782083}, {119932, 16782339}, {119933, 16782595}, + {119934, 16782851}, {119935, 16783107}, {119936, 16783363}, {119937, 16783619}, + {119938, 16777219}, {119939, 16777475}, {119940, 16777731}, {119941, 16777987}, + {119942, 16778243}, {119943, 16778499}, {119944, 16778755}, {119945, 16779011}, + {119946, 16779267}, {119947, 16779523}, {119948, 16779779}, {119949, 16780035}, + {119950, 16780291}, {119951, 16780547}, {119952, 16780803}, {119953, 16781059}, + {119954, 16781315}, {119955, 16781571}, {119956, 16781827}, {119957, 16782083}, + {119958, 16782339}, {119959, 16782595}, {119960, 16782851}, {119961, 16783107}, + {119962, 16783363}, {119963, 16783619}, {119964, 16777219}, {119965, 2}, + {119966, 16777731}, {119967, 16777987}, {119968, 2}, {119970, 16778755}, + {119971, 2}, {119973, 16779523}, {119974, 16779779}, {119975, 2}, + {119977, 16780547}, {119978, 16780803}, {119979, 16781059}, {119980, 16781315}, + {119981, 2}, {119982, 16781827}, {119983, 16782083}, {119984, 16782339}, + {119985, 16782595}, {119986, 16782851}, {119987, 16783107}, {119988, 16783363}, + {119989, 16783619}, {119990, 16777219}, {119991, 16777475}, {119992, 16777731}, + {119993, 16777987}, {119994, 2}, {119995, 16778499}, {119996, 2}, + {119997, 16779011}, {119998, 16779267}, {119999, 16779523}, {120000, 16779779}, + {120001, 16780035}, {120002, 16780291}, {120003, 16780547}, {120004, 2}, + {120005, 16781059}, {120006, 16781315}, {120007, 16781571}, {120008, 16781827}, + {120009, 16782083}, {120010, 16782339}, {120011, 16782595}, {120012, 16782851}, + {120013, 16783107}, {120014, 16783363}, {120015, 16783619}, {120016, 16777219}, + {120017, 16777475}, {120018, 16777731}, {120019, 16777987}, {120020, 16778243}, + {120021, 16778499}, {120022, 16778755}, {120023, 16779011}, {120024, 16779267}, + {120025, 16779523}, {120026, 16779779}, {120027, 16780035}, {120028, 16780291}, + {120029, 16780547}, {120030, 16780803}, {120031, 16781059}, {120032, 16781315}, + {120033, 16781571}, {120034, 16781827}, {120035, 16782083}, {120036, 16782339}, + {120037, 16782595}, {120038, 16782851}, {120039, 16783107}, {120040, 16783363}, + {120041, 16783619}, {120042, 16777219}, {120043, 16777475}, {120044, 16777731}, + {120045, 16777987}, {120046, 16778243}, {120047, 16778499}, {120048, 16778755}, + {120049, 16779011}, {120050, 16779267}, {120051, 16779523}, {120052, 16779779}, + {120053, 16780035}, {120054, 16780291}, {120055, 16780547}, {120056, 16780803}, + {120057, 16781059}, {120058, 16781315}, {120059, 16781571}, {120060, 16781827}, + {120061, 16782083}, {120062, 16782339}, {120063, 16782595}, {120064, 16782851}, + {120065, 16783107}, {120066, 16783363}, {120067, 16783619}, {120068, 16777219}, + {120069, 16777475}, {120070, 2}, {120071, 16777987}, {120072, 16778243}, + {120073, 16778499}, {120074, 16778755}, {120075, 2}, {120077, 16779523}, + {120078, 16779779}, {120079, 16780035}, {120080, 16780291}, {120081, 16780547}, + {120082, 16780803}, {120083, 16781059}, {120084, 16781315}, {120085, 2}, + {120086, 16781827}, {120087, 16782083}, {120088, 16782339}, {120089, 16782595}, + {120090, 16782851}, {120091, 16783107}, {120092, 16783363}, {120093, 2}, + {120094, 16777219}, {120095, 16777475}, {120096, 16777731}, {120097, 16777987}, + {120098, 16778243}, {120099, 16778499}, {120100, 16778755}, {120101, 16779011}, + {120102, 16779267}, {120103, 16779523}, {120104, 16779779}, {120105, 16780035}, + {120106, 16780291}, {120107, 16780547}, {120108, 16780803}, {120109, 16781059}, + {120110, 16781315}, {120111, 16781571}, {120112, 16781827}, {120113, 16782083}, + {120114, 16782339}, {120115, 16782595}, {120116, 16782851}, {120117, 16783107}, + {120118, 16783363}, {120119, 16783619}, {120120, 16777219}, {120121, 16777475}, + {120122, 2}, {120123, 16777987}, {120124, 16778243}, {120125, 16778499}, + {120126, 16778755}, {120127, 2}, {120128, 16779267}, {120129, 16779523}, + {120130, 16779779}, {120131, 16780035}, {120132, 16780291}, {120133, 2}, + {120134, 16780803}, {120135, 2}, {120138, 16781827}, {120139, 16782083}, + {120140, 16782339}, {120141, 16782595}, {120142, 16782851}, {120143, 16783107}, + {120144, 16783363}, {120145, 2}, {120146, 16777219}, {120147, 16777475}, + {120148, 16777731}, {120149, 16777987}, {120150, 16778243}, {120151, 16778499}, + {120152, 16778755}, {120153, 16779011}, {120154, 16779267}, {120155, 16779523}, + {120156, 16779779}, {120157, 16780035}, {120158, 16780291}, {120159, 16780547}, + {120160, 16780803}, {120161, 16781059}, {120162, 16781315}, {120163, 16781571}, + {120164, 16781827}, {120165, 16782083}, {120166, 16782339}, {120167, 16782595}, + {120168, 16782851}, {120169, 16783107}, {120170, 16783363}, {120171, 16783619}, + {120172, 16777219}, {120173, 16777475}, {120174, 16777731}, {120175, 16777987}, + {120176, 16778243}, {120177, 16778499}, {120178, 16778755}, {120179, 16779011}, + {120180, 16779267}, {120181, 16779523}, {120182, 16779779}, {120183, 16780035}, + {120184, 16780291}, {120185, 16780547}, {120186, 16780803}, {120187, 16781059}, + {120188, 16781315}, {120189, 16781571}, {120190, 16781827}, {120191, 16782083}, + {120192, 16782339}, {120193, 16782595}, {120194, 16782851}, {120195, 16783107}, + {120196, 16783363}, {120197, 16783619}, {120198, 16777219}, {120199, 16777475}, + {120200, 16777731}, {120201, 16777987}, {120202, 16778243}, {120203, 16778499}, + {120204, 16778755}, {120205, 16779011}, {120206, 16779267}, {120207, 16779523}, + {120208, 16779779}, {120209, 16780035}, {120210, 16780291}, {120211, 16780547}, + {120212, 16780803}, {120213, 16781059}, {120214, 16781315}, {120215, 16781571}, + {120216, 16781827}, {120217, 16782083}, {120218, 16782339}, {120219, 16782595}, + {120220, 16782851}, {120221, 16783107}, {120222, 16783363}, {120223, 16783619}, + {120224, 16777219}, {120225, 16777475}, {120226, 16777731}, {120227, 16777987}, + {120228, 16778243}, {120229, 16778499}, {120230, 16778755}, {120231, 16779011}, + {120232, 16779267}, {120233, 16779523}, {120234, 16779779}, {120235, 16780035}, + {120236, 16780291}, {120237, 16780547}, {120238, 16780803}, {120239, 16781059}, + {120240, 16781315}, {120241, 16781571}, {120242, 16781827}, {120243, 16782083}, + {120244, 16782339}, {120245, 16782595}, {120246, 16782851}, {120247, 16783107}, + {120248, 16783363}, {120249, 16783619}, {120250, 16777219}, {120251, 16777475}, + {120252, 16777731}, {120253, 16777987}, {120254, 16778243}, {120255, 16778499}, + {120256, 16778755}, {120257, 16779011}, {120258, 16779267}, {120259, 16779523}, + {120260, 16779779}, {120261, 16780035}, {120262, 16780291}, {120263, 16780547}, + {120264, 16780803}, {120265, 16781059}, {120266, 16781315}, {120267, 16781571}, + {120268, 16781827}, {120269, 16782083}, {120270, 16782339}, {120271, 16782595}, + {120272, 16782851}, {120273, 16783107}, {120274, 16783363}, {120275, 16783619}, + {120276, 16777219}, {120277, 16777475}, {120278, 16777731}, {120279, 16777987}, + {120280, 16778243}, {120281, 16778499}, {120282, 16778755}, {120283, 16779011}, + {120284, 16779267}, {120285, 16779523}, {120286, 16779779}, {120287, 16780035}, + {120288, 16780291}, {120289, 16780547}, {120290, 16780803}, {120291, 16781059}, + {120292, 16781315}, {120293, 16781571}, {120294, 16781827}, {120295, 16782083}, + {120296, 16782339}, {120297, 16782595}, {120298, 16782851}, {120299, 16783107}, + {120300, 16783363}, {120301, 16783619}, {120302, 16777219}, {120303, 16777475}, + {120304, 16777731}, {120305, 16777987}, {120306, 16778243}, {120307, 16778499}, + {120308, 16778755}, {120309, 16779011}, {120310, 16779267}, {120311, 16779523}, + {120312, 16779779}, {120313, 16780035}, {120314, 16780291}, {120315, 16780547}, + {120316, 16780803}, {120317, 16781059}, {120318, 16781315}, {120319, 16781571}, + {120320, 16781827}, {120321, 16782083}, {120322, 16782339}, {120323, 16782595}, + {120324, 16782851}, {120325, 16783107}, {120326, 16783363}, {120327, 16783619}, + {120328, 16777219}, {120329, 16777475}, {120330, 16777731}, {120331, 16777987}, + {120332, 16778243}, {120333, 16778499}, {120334, 16778755}, {120335, 16779011}, + {120336, 16779267}, {120337, 16779523}, {120338, 16779779}, {120339, 16780035}, + {120340, 16780291}, {120341, 16780547}, {120342, 16780803}, {120343, 16781059}, + {120344, 16781315}, {120345, 16781571}, {120346, 16781827}, {120347, 16782083}, + {120348, 16782339}, {120349, 16782595}, {120350, 16782851}, {120351, 16783107}, + {120352, 16783363}, {120353, 16783619}, {120354, 16777219}, {120355, 16777475}, + {120356, 16777731}, {120357, 16777987}, {120358, 16778243}, {120359, 16778499}, + {120360, 16778755}, {120361, 16779011}, {120362, 16779267}, {120363, 16779523}, + {120364, 16779779}, {120365, 16780035}, {120366, 16780291}, {120367, 16780547}, + {120368, 16780803}, {120369, 16781059}, {120370, 16781315}, {120371, 16781571}, + {120372, 16781827}, {120373, 16782083}, {120374, 16782339}, {120375, 16782595}, + {120376, 16782851}, {120377, 16783107}, {120378, 16783363}, {120379, 16783619}, + {120380, 16777219}, {120381, 16777475}, {120382, 16777731}, {120383, 16777987}, + {120384, 16778243}, {120385, 16778499}, {120386, 16778755}, {120387, 16779011}, + {120388, 16779267}, {120389, 16779523}, {120390, 16779779}, {120391, 16780035}, + {120392, 16780291}, {120393, 16780547}, {120394, 16780803}, {120395, 16781059}, + {120396, 16781315}, {120397, 16781571}, {120398, 16781827}, {120399, 16782083}, + {120400, 16782339}, {120401, 16782595}, {120402, 16782851}, {120403, 16783107}, + {120404, 16783363}, {120405, 16783619}, {120406, 16777219}, {120407, 16777475}, + {120408, 16777731}, {120409, 16777987}, {120410, 16778243}, {120411, 16778499}, + {120412, 16778755}, {120413, 16779011}, {120414, 16779267}, {120415, 16779523}, + {120416, 16779779}, {120417, 16780035}, {120418, 16780291}, {120419, 16780547}, + {120420, 16780803}, {120421, 16781059}, {120422, 16781315}, {120423, 16781571}, + {120424, 16781827}, {120425, 16782083}, {120426, 16782339}, {120427, 16782595}, + {120428, 16782851}, {120429, 16783107}, {120430, 16783363}, {120431, 16783619}, + {120432, 16777219}, {120433, 16777475}, {120434, 16777731}, {120435, 16777987}, + {120436, 16778243}, {120437, 16778499}, {120438, 16778755}, {120439, 16779011}, + {120440, 16779267}, {120441, 16779523}, {120442, 16779779}, {120443, 16780035}, + {120444, 16780291}, {120445, 16780547}, {120446, 16780803}, {120447, 16781059}, + {120448, 16781315}, {120449, 16781571}, {120450, 16781827}, {120451, 16782083}, + {120452, 16782339}, {120453, 16782595}, {120454, 16782851}, {120455, 16783107}, + {120456, 16783363}, {120457, 16783619}, {120458, 16777219}, {120459, 16777475}, + {120460, 16777731}, {120461, 16777987}, {120462, 16778243}, {120463, 16778499}, + {120464, 16778755}, {120465, 16779011}, {120466, 16779267}, {120467, 16779523}, + {120468, 16779779}, {120469, 16780035}, {120470, 16780291}, {120471, 16780547}, + {120472, 16780803}, {120473, 16781059}, {120474, 16781315}, {120475, 16781571}, + {120476, 16781827}, {120477, 16782083}, {120478, 16782339}, {120479, 16782595}, + {120480, 16782851}, {120481, 16783107}, {120482, 16783363}, {120483, 16783619}, + {120484, 17961731}, {120485, 17961987}, {120486, 2}, {120488, 16851715}, + {120489, 16851971}, {120490, 16852227}, {120491, 16852483}, {120492, 16852739}, + {120493, 16852995}, {120494, 16853251}, {120495, 16853507}, {120496, 16846851}, + {120497, 16853763}, {120498, 16854019}, {120499, 16786179}, {120500, 16854275}, + {120501, 16854531}, {120502, 16854787}, {120503, 16855043}, {120504, 16855299}, + {120505, 16853507}, {120506, 16855555}, {120507, 16855811}, {120508, 16856067}, + {120509, 16856323}, {120510, 16856579}, {120511, 16856835}, {120512, 16857091}, + {120513, 17962243}, {120514, 16851715}, {120515, 16851971}, {120516, 16852227}, + {120517, 16852483}, {120518, 16852739}, {120519, 16852995}, {120520, 16853251}, + {120521, 16853507}, {120522, 16846851}, {120523, 16853763}, {120524, 16854019}, + {120525, 16786179}, {120526, 16854275}, {120527, 16854531}, {120528, 16854787}, + {120529, 16855043}, {120530, 16855299}, {120531, 16855555}, {120533, 16855811}, + {120534, 16856067}, {120535, 16856323}, {120536, 16856579}, {120537, 16856835}, + {120538, 16857091}, {120539, 17962499}, {120540, 16852739}, {120541, 16853507}, + {120542, 16853763}, {120543, 16856323}, {120544, 16855299}, {120545, 16855043}, + {120546, 16851715}, {120547, 16851971}, {120548, 16852227}, {120549, 16852483}, + {120550, 16852739}, {120551, 16852995}, {120552, 16853251}, {120553, 16853507}, + {120554, 16846851}, {120555, 16853763}, {120556, 16854019}, {120557, 16786179}, + {120558, 16854275}, {120559, 16854531}, {120560, 16854787}, {120561, 16855043}, + {120562, 16855299}, {120563, 16853507}, {120564, 16855555}, {120565, 16855811}, + {120566, 16856067}, {120567, 16856323}, {120568, 16856579}, {120569, 16856835}, + {120570, 16857091}, {120571, 17962243}, {120572, 16851715}, {120573, 16851971}, + {120574, 16852227}, {120575, 16852483}, {120576, 16852739}, {120577, 16852995}, + {120578, 16853251}, {120579, 16853507}, {120580, 16846851}, {120581, 16853763}, + {120582, 16854019}, {120583, 16786179}, {120584, 16854275}, {120585, 16854531}, + {120586, 16854787}, {120587, 16855043}, {120588, 16855299}, {120589, 16855555}, + {120591, 16855811}, {120592, 16856067}, {120593, 16856323}, {120594, 16856579}, + {120595, 16856835}, {120596, 16857091}, {120597, 17962499}, {120598, 16852739}, + {120599, 16853507}, {120600, 16853763}, {120601, 16856323}, {120602, 16855299}, + {120603, 16855043}, {120604, 16851715}, {120605, 16851971}, {120606, 16852227}, + {120607, 16852483}, {120608, 16852739}, {120609, 16852995}, {120610, 16853251}, + {120611, 16853507}, {120612, 16846851}, {120613, 16853763}, {120614, 16854019}, + {120615, 16786179}, {120616, 16854275}, {120617, 16854531}, {120618, 16854787}, + {120619, 16855043}, {120620, 16855299}, {120621, 16853507}, {120622, 16855555}, + {120623, 16855811}, {120624, 16856067}, {120625, 16856323}, {120626, 16856579}, + {120627, 16856835}, {120628, 16857091}, {120629, 17962243}, {120630, 16851715}, + {120631, 16851971}, {120632, 16852227}, {120633, 16852483}, {120634, 16852739}, + {120635, 16852995}, {120636, 16853251}, {120637, 16853507}, {120638, 16846851}, + {120639, 16853763}, {120640, 16854019}, {120641, 16786179}, {120642, 16854275}, + {120643, 16854531}, {120644, 16854787}, {120645, 16855043}, {120646, 16855299}, + {120647, 16855555}, {120649, 16855811}, {120650, 16856067}, {120651, 16856323}, + {120652, 16856579}, {120653, 16856835}, {120654, 16857091}, {120655, 17962499}, + {120656, 16852739}, {120657, 16853507}, {120658, 16853763}, {120659, 16856323}, + {120660, 16855299}, {120661, 16855043}, {120662, 16851715}, {120663, 16851971}, + {120664, 16852227}, {120665, 16852483}, {120666, 16852739}, {120667, 16852995}, + {120668, 16853251}, {120669, 16853507}, {120670, 16846851}, {120671, 16853763}, + {120672, 16854019}, {120673, 16786179}, {120674, 16854275}, {120675, 16854531}, + {120676, 16854787}, {120677, 16855043}, {120678, 16855299}, {120679, 16853507}, + {120680, 16855555}, {120681, 16855811}, {120682, 16856067}, {120683, 16856323}, + {120684, 16856579}, {120685, 16856835}, {120686, 16857091}, {120687, 17962243}, + {120688, 16851715}, {120689, 16851971}, {120690, 16852227}, {120691, 16852483}, + {120692, 16852739}, {120693, 16852995}, {120694, 16853251}, {120695, 16853507}, + {120696, 16846851}, {120697, 16853763}, {120698, 16854019}, {120699, 16786179}, + {120700, 16854275}, {120701, 16854531}, {120702, 16854787}, {120703, 16855043}, + {120704, 16855299}, {120705, 16855555}, {120707, 16855811}, {120708, 16856067}, + {120709, 16856323}, {120710, 16856579}, {120711, 16856835}, {120712, 16857091}, + {120713, 17962499}, {120714, 16852739}, {120715, 16853507}, {120716, 16853763}, + {120717, 16856323}, {120718, 16855299}, {120719, 16855043}, {120720, 16851715}, + {120721, 16851971}, {120722, 16852227}, {120723, 16852483}, {120724, 16852739}, + {120725, 16852995}, {120726, 16853251}, {120727, 16853507}, {120728, 16846851}, + {120729, 16853763}, {120730, 16854019}, {120731, 16786179}, {120732, 16854275}, + {120733, 16854531}, {120734, 16854787}, {120735, 16855043}, {120736, 16855299}, + {120737, 16853507}, {120738, 16855555}, {120739, 16855811}, {120740, 16856067}, + {120741, 16856323}, {120742, 16856579}, {120743, 16856835}, {120744, 16857091}, + {120745, 17962243}, {120746, 16851715}, {120747, 16851971}, {120748, 16852227}, + {120749, 16852483}, {120750, 16852739}, {120751, 16852995}, {120752, 16853251}, + {120753, 16853507}, {120754, 16846851}, {120755, 16853763}, {120756, 16854019}, + {120757, 16786179}, {120758, 16854275}, {120759, 16854531}, {120760, 16854787}, + {120761, 16855043}, {120762, 16855299}, {120763, 16855555}, {120765, 16855811}, + {120766, 16856067}, {120767, 16856323}, {120768, 16856579}, {120769, 16856835}, + {120770, 16857091}, {120771, 17962499}, {120772, 16852739}, {120773, 16853507}, + {120774, 16853763}, {120775, 16856323}, {120776, 16855299}, {120777, 16855043}, + {120778, 16858627}, {120780, 2}, {120782, 17045507}, {120783, 16786947}, + {120784, 16785155}, {120785, 16785411}, {120786, 16787715}, {120787, 17045763}, + {120788, 17046019}, {120789, 17046275}, {120790, 17046531}, {120791, 17046787}, + {120792, 17045507}, {120793, 16786947}, {120794, 16785155}, {120795, 16785411}, + {120796, 16787715}, {120797, 17045763}, {120798, 17046019}, {120799, 17046275}, + {120800, 17046531}, {120801, 17046787}, {120802, 17045507}, {120803, 16786947}, + {120804, 16785155}, {120805, 16785411}, {120806, 16787715}, {120807, 17045763}, + {120808, 17046019}, {120809, 17046275}, {120810, 17046531}, {120811, 17046787}, + {120812, 17045507}, {120813, 16786947}, {120814, 16785155}, {120815, 16785411}, + {120816, 16787715}, {120817, 17045763}, {120818, 17046019}, {120819, 17046275}, + {120820, 17046531}, {120821, 17046787}, {120822, 17045507}, {120823, 16786947}, + {120824, 16785155}, {120825, 16785411}, {120826, 16787715}, {120827, 17045763}, + {120828, 17046019}, {120829, 17046275}, {120830, 17046531}, {120831, 17046787}, + {120832, 1}, {121484, 2}, {121499, 1}, {121504, 2}, + {121505, 1}, {121520, 2}, {122624, 1}, {122655, 2}, + {122661, 1}, {122667, 2}, {122880, 1}, {122887, 2}, + {122888, 1}, {122905, 2}, {122907, 1}, {122914, 2}, + {122915, 1}, {122917, 2}, {122918, 1}, {122923, 2}, + {122928, 16866563}, {122929, 16866819}, {122930, 16867075}, {122931, 16867331}, + {122932, 16867587}, {122933, 16867843}, {122934, 16868099}, {122935, 16868355}, + {122936, 16868611}, {122937, 16869123}, {122938, 16869379}, {122939, 16869635}, + {122940, 16870147}, {122941, 16870403}, {122942, 16870659}, {122943, 16870915}, + {122944, 16871171}, {122945, 16871427}, {122946, 16871683}, {122947, 16871939}, + {122948, 16872195}, {122949, 16872451}, {122950, 16872707}, {122951, 16873475}, + {122952, 16873987}, {122953, 16874243}, {122954, 17505795}, {122955, 16889091}, + {122956, 16864003}, {122957, 16864515}, {122958, 16891139}, {122959, 16883715}, + {122960, 16886019}, {122961, 16866563}, {122962, 16866819}, {122963, 16867075}, + {122964, 16867331}, {122965, 16867587}, {122966, 16867843}, {122967, 16868099}, + {122968, 16868355}, {122969, 16868611}, {122970, 16869123}, {122971, 16869379}, + {122972, 16870147}, {122973, 16870403}, {122974, 16870915}, {122975, 16871427}, + {122976, 16871683}, {122977, 16871939}, {122978, 16872195}, {122979, 16872451}, + {122980, 16872707}, {122981, 16873219}, {122982, 16873475}, {122983, 16879875}, + {122984, 16864003}, {122985, 16863747}, {122986, 16866307}, {122987, 16883203}, + {122988, 17500931}, {122989, 16883971}, {122990, 2}, {123023, 1}, + {123024, 2}, {123136, 1}, {123181, 2}, {123184, 1}, + {123198, 2}, {123200, 1}, {123210, 2}, {123214, 1}, + {123216, 2}, {123536, 1}, {123567, 2}, {123584, 1}, + {123642, 2}, {123647, 1}, {123648, 2}, {124112, 1}, + {124154, 2}, {124368, 1}, {124411, 2}, {124415, 1}, + {124416, 2}, {124896, 1}, {124903, 2}, {124904, 1}, + {124908, 2}, {124909, 1}, {124911, 2}, {124912, 1}, + {124927, 2}, {124928, 1}, {125125, 2}, {125127, 1}, + {125143, 2}, {125184, 17962755}, {125185, 17963011}, {125186, 17963267}, + {125187, 17963523}, {125188, 17963779}, {125189, 17964035}, {125190, 17964291}, + {125191, 17964547}, {125192, 17964803}, {125193, 17965059}, {125194, 17965315}, + {125195, 17965571}, {125196, 17965827}, {125197, 17966083}, {125198, 17966339}, + {125199, 17966595}, {125200, 17966851}, {125201, 17967107}, {125202, 17967363}, + {125203, 17967619}, {125204, 17967875}, {125205, 17968131}, {125206, 17968387}, + {125207, 17968643}, {125208, 17968899}, {125209, 17969155}, {125210, 17969411}, + {125211, 17969667}, {125212, 17969923}, {125213, 17970179}, {125214, 17970435}, + {125215, 17970691}, {125216, 17970947}, {125217, 17971203}, {125218, 1}, + {125260, 2}, {125264, 1}, {125274, 2}, {125278, 1}, + {125280, 2}, {126065, 1}, {126133, 2}, {126209, 1}, + {126270, 2}, {126464, 16910595}, {126465, 17695235}, {126466, 17693443}, + {126467, 17846019}, {126468, 2}, {126469, 16911107}, {126470, 17743107}, + {126471, 17693955}, {126472, 17711619}, {126473, 16912131}, {126474, 17720323}, + {126475, 17722627}, {126476, 17694467}, {126477, 17729539}, {126478, 17706499}, + {126479, 17713155}, {126480, 17715203}, {126481, 17708547}, {126482, 17718275}, + {126483, 17736707}, {126484, 17756675}, {126485, 17698307}, {126486, 17701379}, + {126487, 17696515}, {126488, 17736195}, {126489, 17709571}, {126490, 17712643}, + {126491, 17714179}, {126492, 17971459}, {126493, 17684995}, {126494, 17971715}, + {126495, 17971971}, {126496, 2}, {126497, 17695235}, {126498, 17693443}, + {126499, 2}, {126500, 17732611}, {126501, 2}, {126503, 17693955}, + {126504, 2}, {126505, 16912131}, {126506, 17720323}, {126507, 17722627}, + {126508, 17694467}, {126509, 17729539}, {126510, 17706499}, {126511, 17713155}, + {126512, 17715203}, {126513, 17708547}, {126514, 17718275}, {126515, 2}, + {126516, 17756675}, {126517, 17698307}, {126518, 17701379}, {126519, 17696515}, + {126520, 2}, {126521, 17709571}, {126522, 2}, {126523, 17714179}, + {126524, 2}, {126530, 17693443}, {126531, 2}, {126535, 17693955}, + {126536, 2}, {126537, 16912131}, {126538, 2}, {126539, 17722627}, + {126540, 2}, {126541, 17729539}, {126542, 17706499}, {126543, 17713155}, + {126544, 2}, {126545, 17708547}, {126546, 17718275}, {126547, 2}, + {126548, 17756675}, {126549, 2}, {126551, 17696515}, {126552, 2}, + {126553, 17709571}, {126554, 2}, {126555, 17714179}, {126556, 2}, + {126557, 17684995}, {126558, 2}, {126559, 17971971}, {126560, 2}, + {126561, 17695235}, {126562, 17693443}, {126563, 2}, {126564, 17732611}, + {126565, 2}, {126567, 17693955}, {126568, 17711619}, {126569, 16912131}, + {126570, 17720323}, {126571, 2}, {126572, 17694467}, {126573, 17729539}, + {126574, 17706499}, {126575, 17713155}, {126576, 17715203}, {126577, 17708547}, + {126578, 17718275}, {126579, 2}, {126580, 17756675}, {126581, 17698307}, + {126582, 17701379}, {126583, 17696515}, {126584, 2}, {126585, 17709571}, + {126586, 17712643}, {126587, 17714179}, {126588, 17971459}, {126589, 2}, + {126590, 17971715}, {126591, 2}, {126592, 16910595}, {126593, 17695235}, + {126594, 17693443}, {126595, 17846019}, {126596, 17732611}, {126597, 16911107}, + {126598, 17743107}, {126599, 17693955}, {126600, 17711619}, {126601, 16912131}, + {126602, 2}, {126603, 17722627}, {126604, 17694467}, {126605, 17729539}, + {126606, 17706499}, {126607, 17713155}, {126608, 17715203}, {126609, 17708547}, + {126610, 17718275}, {126611, 17736707}, {126612, 17756675}, {126613, 17698307}, + {126614, 17701379}, {126615, 17696515}, {126616, 17736195}, {126617, 17709571}, + {126618, 17712643}, {126619, 17714179}, {126620, 2}, {126625, 17695235}, + {126626, 17693443}, {126627, 17846019}, {126628, 2}, {126629, 16911107}, + {126630, 17743107}, {126631, 17693955}, {126632, 17711619}, {126633, 16912131}, + {126634, 2}, {126635, 17722627}, {126636, 17694467}, {126637, 17729539}, + {126638, 17706499}, {126639, 17713155}, {126640, 17715203}, {126641, 17708547}, + {126642, 17718275}, {126643, 17736707}, {126644, 17756675}, {126645, 17698307}, + {126646, 17701379}, {126647, 17696515}, {126648, 17736195}, {126649, 17709571}, + {126650, 17712643}, {126651, 17714179}, {126652, 2}, {126704, 1}, + {126706, 2}, {126976, 1}, {127020, 2}, {127024, 1}, + {127124, 2}, {127136, 1}, {127151, 2}, {127153, 1}, + {127168, 2}, {127169, 1}, {127184, 2}, {127185, 1}, + {127222, 2}, {127233, 34749443}, {127234, 34749955}, {127235, 34750467}, + {127236, 34750979}, {127237, 34751491}, {127238, 34752003}, {127239, 34752515}, + {127240, 34753027}, {127241, 34753539}, {127242, 34754051}, {127243, 1}, + {127248, 50655491}, {127249, 50656259}, {127250, 50657027}, {127251, 50657795}, + {127252, 50658563}, {127253, 50659331}, {127254, 50660099}, {127255, 50660867}, + {127256, 50661635}, {127257, 50662403}, {127258, 50663171}, {127259, 50663939}, + {127260, 50664707}, {127261, 50665475}, {127262, 50666243}, {127263, 50667011}, + {127264, 50667779}, {127265, 50668547}, {127266, 50669315}, {127267, 50670083}, + {127268, 50670851}, {127269, 50671619}, {127270, 50672387}, {127271, 50673155}, + {127272, 50673923}, {127273, 50674691}, {127274, 51531779}, {127275, 16777731}, + {127276, 16781571}, {127277, 33554947}, {127278, 34755331}, {127279, 1}, + {127280, 16777219}, {127281, 16777475}, {127282, 16777731}, {127283, 16777987}, + {127284, 16778243}, {127285, 16778499}, {127286, 16778755}, {127287, 16779011}, + {127288, 16779267}, {127289, 16779523}, {127290, 16779779}, {127291, 16780035}, + {127292, 16780291}, {127293, 16780547}, {127294, 16780803}, {127295, 16781059}, + {127296, 16781315}, {127297, 16781571}, {127298, 16781827}, {127299, 16782083}, + {127300, 16782339}, {127301, 16782595}, {127302, 16782851}, {127303, 16783107}, + {127304, 16783363}, {127305, 16783619}, {127306, 34755843}, {127307, 34237187}, + {127308, 34756355}, {127309, 34756867}, {127310, 51534595}, {127311, 34758147}, + {127312, 1}, {127338, 34220035}, {127339, 34200067}, {127340, 34758659}, + {127341, 1}, {127376, 34759171}, {127377, 1}, {127406, 2}, + {127462, 1}, {127488, 34759683}, {127489, 34760195}, {127490, 17318403}, + {127491, 2}, {127504, 17168387}, {127505, 17983491}, {127506, 17983747}, + {127507, 17362179}, {127508, 17153795}, {127509, 17984003}, {127510, 17984259}, + {127511, 17235971}, {127512, 17984515}, {127513, 17984771}, {127514, 17985027}, + {127515, 17596163}, {127516, 17985283}, {127517, 17985539}, {127518, 17985795}, + {127519, 17986051}, {127520, 17986307}, {127521, 17986563}, {127522, 17177603}, + {127523, 17986819}, {127524, 17987075}, {127525, 17987331}, {127526, 17987587}, + {127527, 17987843}, {127528, 17988099}, {127529, 17152259}, {127530, 17233923}, + {127531, 17988355}, {127532, 17299203}, {127533, 17234691}, {127534, 17299459}, + {127535, 17988611}, {127536, 17191939}, {127537, 17988867}, {127538, 17989123}, + {127539, 17989379}, {127540, 17989635}, {127541, 17989891}, {127542, 17274883}, + {127543, 17170947}, {127544, 17990147}, {127545, 17990403}, {127546, 17990659}, + {127547, 17990915}, {127548, 2}, {127552, 51545603}, {127553, 51546371}, + {127554, 51547139}, {127555, 51547907}, {127556, 51548675}, {127557, 51549443}, + {127558, 51550211}, {127559, 51550979}, {127560, 51551747}, {127561, 2}, + {127568, 17998083}, {127569, 17998339}, {127570, 2}, {127584, 1}, + {127590, 2}, {127744, 1}, {128728, 2}, {128732, 1}, + {128749, 2}, {128752, 1}, {128765, 2}, {128768, 1}, + {128887, 2}, {128891, 1}, {128986, 2}, {128992, 1}, + {129004, 2}, {129008, 1}, {129009, 2}, {129024, 1}, + {129036, 2}, {129040, 1}, {129096, 2}, {129104, 1}, + {129114, 2}, {129120, 1}, {129160, 2}, {129168, 1}, + {129198, 2}, {129200, 1}, {129212, 2}, {129216, 1}, + {129218, 2}, {129280, 1}, {129620, 2}, {129632, 1}, + {129646, 2}, {129648, 1}, {129661, 2}, {129664, 1}, + {129674, 2}, {129679, 1}, {129735, 2}, {129742, 1}, + {129757, 2}, {129759, 1}, {129770, 2}, {129776, 1}, + {129785, 2}, {129792, 1}, {129939, 2}, {129940, 1}, + {130032, 17045507}, {130033, 16786947}, {130034, 16785155}, {130035, 16785411}, + {130036, 16787715}, {130037, 17045763}, {130038, 17046019}, {130039, 17046275}, + {130040, 17046531}, {130041, 17046787}, {130042, 2}, {131072, 1}, {173792, 2}, {173824, 1}, {177978, 2}, {177984, 1}, {178206, 2}, {178208, 1}, {183970, 2}, {183984, 1}, - {191457, 2}, {194560, 17981443}, {194561, 17981699}, {194562, 17981955}, - {194563, 17982211}, {194564, 17982467}, {194565, 17608451}, {194566, 17982723}, - {194567, 17982979}, {194568, 17983235}, {194569, 17983491}, {194570, 17608707}, - {194571, 17983747}, {194572, 17984003}, {194573, 17984259}, {194574, 17608963}, - {194575, 17984515}, {194576, 17984771}, {194577, 17985027}, {194578, 17985283}, - {194579, 17985539}, {194580, 17985795}, {194581, 17968643}, {194582, 17986051}, - {194583, 17986307}, {194584, 17986563}, {194585, 17986819}, {194586, 17987075}, - {194587, 17623043}, {194588, 17987331}, {194589, 17145859}, {194590, 17987587}, - {194591, 17987843}, {194592, 17988099}, {194593, 17988355}, {194594, 17973251}, - {194595, 17988611}, {194596, 17988867}, {194597, 17624323}, {194598, 17609219}, - {194599, 17609475}, {194600, 17624579}, {194601, 17989123}, {194602, 17989379}, - {194603, 17562883}, {194604, 17989635}, {194605, 17609731}, {194606, 17989891}, - {194607, 17990147}, {194608, 17990403}, {194609, 17990659}, {194612, 17990915}, - {194613, 17991171}, {194614, 17991427}, {194615, 17991683}, {194616, 17991939}, - {194617, 17992195}, {194618, 17992451}, {194619, 17992707}, {194620, 17992963}, - {194621, 17993219}, {194622, 17993475}, {194623, 17993731}, {194624, 17993987}, - {194625, 17994243}, {194626, 17994499}, {194627, 17994755}, {194628, 17995011}, - {194629, 17995267}, {194631, 17625091}, {194632, 17995523}, {194633, 17995779}, - {194634, 17996035}, {194635, 17996291}, {194636, 17610243}, {194637, 17996547}, - {194638, 17996803}, {194639, 17997059}, {194640, 17600003}, {194641, 17997315}, - {194642, 17997571}, {194643, 17997827}, {194644, 17998083}, {194645, 17998339}, - {194646, 17998595}, {194647, 17998851}, {194648, 17999107}, {194649, 17999363}, - {194650, 17999619}, {194651, 17999875}, {194652, 18000131}, {194653, 17966851}, - {194654, 18000387}, {194655, 18000643}, {194656, 18000899}, {194657, 18001155}, - {194658, 18001411}, {194659, 18001667}, {194660, 18001923}, {194661, 18002179}, - {194662, 18002435}, {194663, 18002691}, {194664, 2}, {194665, 18002947}, - {194666, 18003203}, {194668, 18003459}, {194669, 18003715}, {194670, 18003971}, - {194671, 17561859}, {194672, 18004227}, {194673, 18004483}, {194674, 18004739}, - {194675, 18004995}, {194676, 2}, {194677, 17152515}, {194678, 18005251}, - {194679, 18005507}, {194680, 17153027}, {194681, 18005763}, {194682, 18006019}, - {194683, 18006275}, {194684, 18006531}, {194685, 18006787}, {194686, 18007043}, - {194687, 18007299}, {194688, 18007555}, {194689, 18007811}, {194690, 18008067}, - {194691, 18008323}, {194692, 18008579}, {194693, 18008835}, {194694, 18009091}, - {194695, 18009347}, {194696, 18009603}, {194697, 18009859}, {194698, 18010115}, - {194699, 18010371}, {194700, 18010627}, {194701, 18010883}, {194702, 17548547}, - {194703, 18011139}, {194704, 17155587}, {194705, 18011395}, {194707, 18011651}, - {194708, 18011907}, {194710, 18012163}, {194711, 18012419}, {194712, 18012675}, - {194713, 18012931}, {194714, 18013187}, {194715, 18013443}, {194716, 18013699}, - {194717, 18013955}, {194718, 18014211}, {194719, 18014467}, {194720, 18014723}, - {194721, 18014979}, {194722, 18015235}, {194723, 17611523}, {194724, 18015491}, - {194725, 18015747}, {194726, 18016003}, {194727, 18016259}, {194728, 17628163}, - {194729, 18016259}, {194730, 18016515}, {194731, 17612035}, {194732, 18016771}, - {194733, 18017027}, {194734, 18017283}, {194735, 18017539}, {194736, 17612291}, - {194737, 17541635}, {194738, 17414915}, {194739, 18017795}, {194740, 18018051}, - {194741, 18018307}, {194742, 18018563}, {194743, 18018819}, {194744, 18019075}, - {194745, 18019331}, {194746, 18019587}, {194747, 18019843}, {194748, 18020099}, - {194749, 18020355}, {194750, 18020611}, {194751, 18020867}, {194752, 18021123}, - {194753, 18021379}, {194754, 18021635}, {194755, 18021891}, {194756, 18022147}, - {194757, 18022403}, {194758, 18022659}, {194759, 18022915}, {194760, 17612547}, - {194761, 18023171}, {194762, 18023427}, {194763, 18023683}, {194764, 18023939}, - {194765, 18024195}, {194766, 18024451}, {194767, 17613059}, {194768, 18024707}, - {194769, 18024963}, {194770, 18025219}, {194771, 18025475}, {194772, 18025731}, - {194773, 18025987}, {194774, 18026243}, {194775, 18026499}, {194776, 17548803}, - {194777, 17630211}, {194778, 18026755}, {194779, 18027011}, {194780, 18027267}, - {194781, 18027523}, {194782, 18027779}, {194783, 18028035}, {194784, 18028291}, - {194785, 18028547}, {194786, 17613315}, {194787, 18028803}, {194788, 18029059}, - {194789, 18029315}, {194790, 18029571}, {194791, 17640963}, {194792, 18029827}, - {194793, 18030083}, {194794, 18030339}, {194795, 18030595}, {194796, 18030851}, - {194797, 18031107}, {194798, 18031363}, {194799, 18031619}, {194800, 18031875}, - {194801, 18032131}, {194802, 18032387}, {194803, 18032643}, {194804, 18032899}, - {194805, 17566211}, {194806, 18033155}, {194807, 18033411}, {194808, 18033667}, - {194809, 18033923}, {194810, 18034179}, {194811, 18034435}, {194812, 18034691}, - {194813, 18034947}, {194814, 18035203}, {194815, 18035459}, {194816, 18035715}, - {194817, 17613571}, {194818, 17587203}, {194819, 18035971}, {194820, 18036227}, - {194821, 18036483}, {194822, 18036739}, {194823, 18036995}, {194824, 18037251}, - {194825, 18037507}, {194826, 18037763}, {194827, 17630979}, {194828, 18038019}, - {194829, 18038275}, {194830, 18038531}, {194831, 18038787}, {194832, 18039043}, - {194833, 18039299}, {194834, 18039555}, {194835, 18039811}, {194836, 17631235}, - {194837, 18040067}, {194838, 18040323}, {194839, 18040579}, {194840, 18040835}, - {194841, 18041091}, {194842, 18041347}, {194843, 18041603}, {194844, 18041859}, - {194845, 18042115}, {194846, 18042371}, {194847, 2}, {194848, 18042627}, - {194849, 17631747}, {194850, 18042883}, {194851, 18043139}, {194852, 18043395}, - {194853, 18043651}, {194854, 18043907}, {194855, 18044163}, {194856, 18044419}, - {194857, 18044675}, {194858, 18044931}, {194859, 18045187}, {194860, 18045443}, - {194862, 18045699}, {194863, 18045955}, {194864, 17632259}, {194865, 18046211}, - {194866, 18046467}, {194867, 18046723}, {194868, 18046979}, {194869, 18047235}, - {194870, 18047491}, {194871, 18047747}, {194872, 17562627}, {194873, 18048003}, - {194874, 18048259}, {194875, 18048515}, {194876, 18048771}, {194877, 18049027}, - {194878, 18049283}, {194879, 18049539}, {194880, 17633795}, {194881, 18049795}, - {194882, 18050051}, {194883, 18050307}, {194884, 18050563}, {194885, 18050819}, - {194886, 18051075}, {194888, 17634051}, {194889, 17641475}, {194890, 18051331}, - {194891, 18051587}, {194892, 18051843}, {194893, 18052099}, {194894, 18052355}, - {194895, 17553155}, {194896, 17634563}, {194897, 18052611}, {194898, 18052867}, - {194899, 17616131}, {194900, 18053123}, {194901, 18053379}, {194902, 17605123}, - {194903, 18053635}, {194904, 18053891}, {194905, 17616899}, {194906, 18054147}, - {194907, 18054403}, {194908, 18054659}, {194909, 18054915}, {194911, 2}, - {194912, 18055171}, {194913, 18055427}, {194914, 18055683}, {194915, 18055939}, - {194916, 18056195}, {194917, 18056451}, {194918, 18056707}, {194919, 18056963}, - {194920, 18057219}, {194921, 18057475}, {194922, 18057731}, {194923, 18057987}, - {194924, 18058243}, {194925, 18058499}, {194926, 18058755}, {194927, 18059011}, - {194928, 18059267}, {194929, 18059523}, {194930, 18059779}, {194931, 18060035}, - {194932, 18060291}, {194933, 18060547}, {194934, 18060803}, {194935, 18061059}, - {194936, 18061315}, {194937, 18061571}, {194938, 17618435}, {194939, 18061827}, - {194940, 18062083}, {194941, 18062339}, {194942, 18062595}, {194943, 18062851}, - {194944, 18063107}, {194945, 18063363}, {194946, 18063619}, {194947, 18063875}, - {194948, 18064131}, {194949, 18064387}, {194950, 18064643}, {194951, 18064899}, - {194952, 18065155}, {194953, 18065411}, {194954, 18065667}, {194955, 18011651}, - {194956, 18065923}, {194957, 18066179}, {194958, 18066435}, {194959, 18066691}, - {194960, 18066947}, {194961, 18067203}, {194962, 18067459}, {194963, 18067715}, - {194964, 18067971}, {194965, 18068227}, {194966, 18068483}, {194967, 18068739}, - {194968, 17566979}, {194969, 18068995}, {194970, 18069251}, {194971, 18069507}, - {194972, 18069763}, {194973, 18070019}, {194974, 18070275}, {194975, 17619203}, - {194976, 18070531}, {194977, 18070787}, {194978, 18071043}, {194979, 18071299}, - {194980, 18071555}, {194981, 18071811}, {194982, 18072067}, {194983, 18072323}, - {194984, 18072579}, {194985, 18072835}, {194986, 18073091}, {194987, 18073347}, - {194988, 18073603}, {194989, 18073859}, {194990, 18074115}, {194991, 18074371}, - {194992, 18074627}, {194993, 18074883}, {194994, 18075139}, {194995, 18075395}, - {194996, 17551875}, {194997, 18075651}, {194998, 18075907}, {194999, 18076163}, - {195000, 18076419}, {195001, 18076675}, {195002, 18076931}, {195003, 17636355}, - {195004, 18077187}, {195005, 18077443}, {195006, 18077699}, {195007, 2}, - {195008, 18077955}, {195009, 18078211}, {195010, 18078467}, {195011, 18078723}, - {195012, 17178627}, {195013, 18078979}, {195014, 18079235}, {195015, 18079491}, - {195016, 18079747}, {195017, 18080003}, {195018, 18080259}, {195019, 18080515}, - {195020, 18080771}, {195021, 18081027}, {195022, 18081283}, {195023, 18081539}, - {195024, 17637635}, {195025, 17637891}, {195026, 17180419}, {195027, 18081795}, - {195028, 18082051}, {195029, 18082307}, {195030, 18082563}, {195031, 18082819}, - {195032, 18083075}, {195033, 18083331}, {195034, 18083587}, {195035, 18083843}, - {195036, 18084099}, {195037, 18084355}, {195038, 18084611}, {195039, 17638147}, - {195040, 18084867}, {195041, 18085123}, {195042, 18085379}, {195043, 18085635}, - {195044, 18085891}, {195045, 18086147}, {195046, 18086403}, {195047, 18086659}, - {195048, 18086915}, {195049, 18087171}, {195050, 18087427}, {195051, 18087683}, - {195052, 18087939}, {195053, 18088195}, {195054, 18088451}, {195055, 18088707}, - {195056, 18088963}, {195057, 18089219}, {195058, 18089475}, {195059, 18089731}, - {195060, 18089987}, {195061, 18090243}, {195062, 18090499}, {195063, 18090755}, - {195064, 18091011}, {195065, 18091267}, {195066, 18091523}, {195067, 18091779}, - {195068, 18092035}, {195069, 18092291}, {195070, 17639683}, {195072, 18092547}, - {195073, 18092803}, {195074, 18093059}, {195075, 18093315}, {195076, 18093571}, - {195077, 18093827}, {195078, 18094083}, {195079, 18094339}, {195080, 18094595}, - {195081, 18094851}, {195082, 17639939}, {195083, 18095107}, {195084, 18095363}, - {195085, 18095619}, {195086, 18095875}, {195087, 18096131}, {195088, 18096387}, - {195089, 18096643}, {195090, 18096899}, {195091, 18097155}, {195092, 18097411}, - {195093, 17192707}, {195094, 18097667}, {195095, 17193731}, {195096, 18097923}, - {195097, 18098179}, {195098, 18098435}, {195099, 18098691}, {195100, 17195011}, - {195101, 18098947}, {195102, 2}, {196608, 1}, {201547, 2}, - {201552, 1}, {205744, 2}, {917760, 0}, {918000, 2} + {191457, 2}, {191472, 1}, {192094, 2}, {194560, 17998595}, + {194561, 17998851}, {194562, 17999107}, {194563, 17999363}, {194564, 17999619}, + {194565, 17619971}, {194566, 17999875}, {194567, 18000131}, {194568, 18000387}, + {194569, 18000643}, {194570, 17620227}, {194571, 18000899}, {194572, 18001155}, + {194573, 18001411}, {194574, 17620483}, {194575, 18001667}, {194576, 18001923}, + {194577, 18002179}, {194578, 18002435}, {194579, 18002691}, {194580, 18002947}, + {194581, 17985795}, {194582, 18003203}, {194583, 18003459}, {194584, 18003715}, + {194585, 18003971}, {194586, 18004227}, {194587, 17634563}, {194588, 18004483}, + {194589, 17156355}, {194590, 18004739}, {194591, 18004995}, {194592, 18005251}, + {194593, 18005507}, {194594, 17990403}, {194595, 18005763}, {194596, 18006019}, + {194597, 17635843}, {194598, 17620739}, {194599, 17620995}, {194600, 17636099}, + {194601, 18006275}, {194602, 18006531}, {194603, 17574403}, {194604, 18006787}, + {194605, 17621251}, {194606, 18007043}, {194607, 18007299}, {194608, 18007555}, + {194609, 18007811}, {194612, 18008067}, {194613, 18008323}, {194614, 18008579}, + {194615, 18008835}, {194616, 18009091}, {194617, 18009347}, {194618, 18009603}, + {194619, 18009859}, {194620, 18010115}, {194621, 18010371}, {194622, 18010627}, + {194623, 18010883}, {194624, 18011139}, {194625, 18011395}, {194626, 18011651}, + {194627, 18011907}, {194628, 18012163}, {194629, 18012419}, {194631, 17636611}, + {194632, 18012675}, {194633, 18012931}, {194634, 18013187}, {194635, 18013443}, + {194636, 17621763}, {194637, 18013699}, {194638, 18013955}, {194639, 18014211}, + {194640, 17611523}, {194641, 18014467}, {194642, 18014723}, {194643, 18014979}, + {194644, 18015235}, {194645, 18015491}, {194646, 18015747}, {194647, 18016003}, + {194648, 18016259}, {194649, 18016515}, {194650, 18016771}, {194651, 18017027}, + {194652, 18017283}, {194653, 17984003}, {194654, 18017539}, {194655, 18017795}, + {194656, 18018051}, {194657, 18018307}, {194658, 18018563}, {194659, 18018819}, + {194660, 18019075}, {194661, 18019331}, {194662, 18019587}, {194663, 18019843}, + {194664, 18020099}, {194665, 18020355}, {194666, 18020611}, {194668, 18020867}, + {194669, 18021123}, {194670, 18021379}, {194671, 17573379}, {194672, 18021635}, + {194673, 18021891}, {194674, 18022147}, {194675, 18022403}, {194676, 18022659}, + {194677, 17163011}, {194678, 18022915}, {194679, 18023171}, {194680, 17163523}, + {194681, 18023427}, {194682, 18023683}, {194683, 18023939}, {194684, 18024195}, + {194685, 18024451}, {194686, 18024707}, {194687, 18024963}, {194688, 18025219}, + {194689, 18025475}, {194690, 18025731}, {194691, 18025987}, {194692, 18026243}, + {194693, 18026499}, {194694, 18026755}, {194695, 18027011}, {194696, 18027267}, + {194697, 18027523}, {194698, 18027779}, {194699, 18028035}, {194700, 18028291}, + {194701, 18028547}, {194702, 17560067}, {194703, 18028803}, {194704, 17166083}, + {194705, 18029059}, {194707, 18029315}, {194708, 18029571}, {194710, 18029827}, + {194711, 18030083}, {194712, 18030339}, {194713, 18030595}, {194714, 18030851}, + {194715, 18031107}, {194716, 18031363}, {194717, 18031619}, {194718, 18031875}, + {194719, 18032131}, {194720, 18032387}, {194721, 18032643}, {194722, 18032899}, + {194723, 17623043}, {194724, 18033155}, {194725, 18033411}, {194726, 18033667}, + {194727, 18033923}, {194728, 17639683}, {194729, 18033923}, {194730, 18034179}, + {194731, 17623555}, {194732, 18034435}, {194733, 18034691}, {194734, 18034947}, + {194735, 18035203}, {194736, 17623811}, {194737, 17553155}, {194738, 17425411}, + {194739, 18035459}, {194740, 18035715}, {194741, 18035971}, {194742, 18036227}, + {194743, 18036483}, {194744, 18036739}, {194745, 18036995}, {194746, 18037251}, + {194747, 18037507}, {194748, 18037763}, {194749, 18038019}, {194750, 18038275}, + {194751, 18038531}, {194752, 18038787}, {194753, 18039043}, {194754, 18039299}, + {194755, 18039555}, {194756, 18039811}, {194757, 18040067}, {194758, 18040323}, + {194759, 18040579}, {194760, 17624067}, {194761, 18040835}, {194762, 18041091}, + {194763, 18041347}, {194764, 18041603}, {194765, 18041859}, {194766, 18042115}, + {194767, 17624579}, {194768, 18042371}, {194769, 18042627}, {194770, 18042883}, + {194771, 18043139}, {194772, 18043395}, {194773, 18043651}, {194774, 18043907}, + {194775, 18044163}, {194776, 17560323}, {194777, 17641731}, {194778, 18044419}, + {194779, 18044675}, {194780, 18044931}, {194781, 18045187}, {194782, 18045443}, + {194783, 18045699}, {194784, 18045955}, {194785, 18046211}, {194786, 17624835}, + {194787, 18046467}, {194788, 18046723}, {194789, 18046979}, {194790, 18047235}, + {194791, 17652483}, {194792, 18047491}, {194793, 18047747}, {194794, 18048003}, + {194795, 18048259}, {194796, 18048515}, {194797, 18048771}, {194798, 18049027}, + {194799, 18049283}, {194800, 18049539}, {194801, 18049795}, {194802, 18050051}, + {194803, 18050307}, {194804, 18050563}, {194805, 17577731}, {194806, 18050819}, + {194807, 18051075}, {194808, 18051331}, {194809, 18051587}, {194810, 18051843}, + {194811, 18052099}, {194812, 18052355}, {194813, 18052611}, {194814, 18052867}, + {194815, 18053123}, {194816, 18053379}, {194817, 17625091}, {194818, 17598723}, + {194819, 18053635}, {194820, 18053891}, {194821, 18054147}, {194822, 18054403}, + {194823, 18054659}, {194824, 18054915}, {194825, 18055171}, {194826, 18055427}, + {194827, 17642499}, {194828, 18055683}, {194829, 18055939}, {194830, 18056195}, + {194831, 18056451}, {194832, 18056707}, {194833, 18056963}, {194834, 18057219}, + {194835, 18057475}, {194836, 17642755}, {194837, 18057731}, {194838, 18057987}, + {194839, 18058243}, {194840, 18058499}, {194841, 18058755}, {194842, 18059011}, + {194843, 18059267}, {194844, 18059523}, {194845, 18059779}, {194846, 18060035}, + {194847, 18060291}, {194848, 18060547}, {194849, 17643267}, {194850, 18060803}, + {194851, 18061059}, {194852, 18061315}, {194853, 18061571}, {194854, 18061827}, + {194855, 18062083}, {194856, 18062339}, {194857, 18062595}, {194858, 18062851}, + {194859, 18063107}, {194860, 18063363}, {194862, 18063619}, {194863, 18063875}, + {194864, 17643779}, {194865, 18064131}, {194866, 18064387}, {194867, 18064643}, + {194868, 18064899}, {194869, 18065155}, {194870, 18065411}, {194871, 18065667}, + {194872, 17574147}, {194873, 18065923}, {194874, 18066179}, {194875, 18066435}, + {194876, 18066691}, {194877, 18066947}, {194878, 18067203}, {194879, 18067459}, + {194880, 17645315}, {194881, 18067715}, {194882, 18067971}, {194883, 18068227}, + {194884, 18068483}, {194885, 18068739}, {194886, 18068995}, {194888, 17645571}, + {194889, 17652995}, {194890, 18069251}, {194891, 18069507}, {194892, 18069763}, + {194893, 18070019}, {194894, 18070275}, {194895, 17564675}, {194896, 17646083}, + {194897, 18070531}, {194898, 18070787}, {194899, 17627651}, {194900, 18071043}, + {194901, 18071299}, {194902, 17616643}, {194903, 18071555}, {194904, 18071811}, + {194905, 17628419}, {194906, 18072067}, {194907, 18072323}, {194908, 18072579}, + {194909, 18072835}, {194911, 18073091}, {194912, 18073347}, {194913, 18073603}, + {194914, 18073859}, {194915, 18074115}, {194916, 18074371}, {194917, 18074627}, + {194918, 18074883}, {194919, 18075139}, {194920, 18075395}, {194921, 18075651}, + {194922, 18075907}, {194923, 18076163}, {194924, 18076419}, {194925, 18076675}, + {194926, 18076931}, {194927, 18077187}, {194928, 18077443}, {194929, 18077699}, + {194930, 18077955}, {194931, 18078211}, {194932, 18078467}, {194933, 18078723}, + {194934, 18078979}, {194935, 18079235}, {194936, 18079491}, {194937, 18079747}, + {194938, 17629955}, {194939, 18080003}, {194940, 18080259}, {194941, 18080515}, + {194942, 18080771}, {194943, 18081027}, {194944, 18081283}, {194945, 18081539}, + {194946, 18081795}, {194947, 18082051}, {194948, 18082307}, {194949, 18082563}, + {194950, 18082819}, {194951, 18083075}, {194952, 18083331}, {194953, 18083587}, + {194954, 18083843}, {194955, 18029315}, {194956, 18084099}, {194957, 18084355}, + {194958, 18084611}, {194959, 18084867}, {194960, 18085123}, {194961, 18085379}, + {194962, 18085635}, {194963, 18085891}, {194964, 18086147}, {194965, 18086403}, + {194966, 18086659}, {194967, 18086915}, {194968, 17578499}, {194969, 18087171}, + {194970, 18087427}, {194971, 18087683}, {194972, 18087939}, {194973, 18088195}, + {194974, 18088451}, {194975, 17630723}, {194976, 18088707}, {194977, 18088963}, + {194978, 18089219}, {194979, 18089475}, {194980, 18089731}, {194981, 18089987}, + {194982, 18090243}, {194983, 18090499}, {194984, 18090755}, {194985, 18091011}, + {194986, 18091267}, {194987, 18091523}, {194988, 18091779}, {194989, 18092035}, + {194990, 18092291}, {194991, 18092547}, {194992, 18092803}, {194993, 18093059}, + {194994, 18093315}, {194995, 18093571}, {194996, 17563395}, {194997, 18093827}, + {194998, 18094083}, {194999, 18094339}, {195000, 18094595}, {195001, 18094851}, + {195002, 18095107}, {195003, 17647875}, {195004, 18095363}, {195005, 18095619}, + {195006, 18095875}, {195007, 18096131}, {195008, 18096387}, {195009, 18096643}, + {195010, 18096899}, {195011, 18097155}, {195012, 17189123}, {195013, 18097411}, + {195014, 18097667}, {195015, 18097923}, {195016, 18098179}, {195017, 18098435}, + {195018, 18098691}, {195019, 18098947}, {195020, 18099203}, {195021, 18099459}, + {195022, 18099715}, {195023, 18099971}, {195024, 17649155}, {195025, 17649411}, + {195026, 17190915}, {195027, 18100227}, {195028, 18100483}, {195029, 18100739}, + {195030, 18100995}, {195031, 18101251}, {195032, 18101507}, {195033, 18101763}, + {195034, 18102019}, {195035, 18102275}, {195036, 18102531}, {195037, 18102787}, + {195038, 18103043}, {195039, 17649667}, {195040, 18103299}, {195041, 18103555}, + {195042, 18103811}, {195043, 18104067}, {195044, 18104323}, {195045, 18104579}, + {195046, 18104835}, {195047, 18105091}, {195048, 18105347}, {195049, 18105603}, + {195050, 18105859}, {195051, 18106115}, {195052, 18106371}, {195053, 18106627}, + {195054, 18106883}, {195055, 18107139}, {195056, 18107395}, {195057, 18107651}, + {195058, 18107907}, {195059, 18108163}, {195060, 18108419}, {195061, 18108675}, + {195062, 18108931}, {195063, 18109187}, {195064, 18109443}, {195065, 18109699}, + {195066, 18109955}, {195067, 18110211}, {195068, 18110467}, {195069, 18110723}, + {195070, 17651203}, {195072, 18110979}, {195073, 18111235}, {195074, 18111491}, + {195075, 18111747}, {195076, 18112003}, {195077, 18112259}, {195078, 18112515}, + {195079, 18112771}, {195080, 18113027}, {195081, 18113283}, {195082, 17651459}, + {195083, 18113539}, {195084, 18113795}, {195085, 18114051}, {195086, 18114307}, + {195087, 18114563}, {195088, 18114819}, {195089, 18115075}, {195090, 18115331}, + {195091, 18115587}, {195092, 18115843}, {195093, 17203203}, {195094, 18116099}, + {195095, 17204227}, {195096, 18116355}, {195097, 18116611}, {195098, 18116867}, + {195099, 18117123}, {195100, 17205507}, {195101, 18117379}, {195102, 2}, + {196608, 1}, {201547, 2}, {201552, 1}, {205744, 2}, + {917760, 0}, {918000, 2} }; } // namespace ada::idna #endif // ADA_IDNA_TABLES_H - /* end file src/mapping_tables.cpp */ namespace ada::idna { @@ -2753,30 +2797,6 @@ uint32_t find_range_index(uint32_t key) { return low == 0 ? 0 : low - 1; } -bool ascii_has_upper_case(char* input, size_t length) { - auto broadcast = [](uint8_t v) -> uint64_t { - return 0x101010101010101ull * v; - }; - uint64_t broadcast_80 = broadcast(0x80); - uint64_t broadcast_Ap = broadcast(128 - 'A'); - uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1); - size_t i = 0; - - uint64_t runner{0}; - - for (; i + 7 < length; i += 8) { - uint64_t word{}; - memcpy(&word, input + i, sizeof(word)); - runner |= (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80); - } - if (i < length) { - uint64_t word{}; - memcpy(&word, input + i, length - i); - runner |= (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80); - } - return runner != 0; -} - void ascii_map(char* input, size_t length) { auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101ull * v; @@ -7915,11 +7935,11 @@ void compose(std::u32string& input) { if (composition[1] != composition[0] && previous_ccc < ccc) { // Try finding a composition. - uint16_t left = composition[0]; - uint16_t right = composition[1]; + int left = composition[0]; + int right = composition[1]; while (left + 2 < right) { // mean without overflow - uint16_t middle = left + (((right - left) >> 1) & ~1); + int middle = left + (((right - left) >> 1) & ~1); if (composition_data[middle] <= input[input_count + 1]) { left = middle; } @@ -8002,6 +8022,10 @@ static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) { } bool punycode_to_utf32(std::string_view input, std::u32string &out) { + // See https://github.com/whatwg/url/issues/803 + if (input.starts_with("xn--")) { + return false; + } int32_t written_out{0}; out.reserve(out.size() + input.size()); uint32_t n = initial_n; @@ -8058,11 +8082,13 @@ bool punycode_to_utf32(std::string_view input, std::u32string &out) { written_out++; ++i; } - return true; } bool verify_punycode(std::string_view input) { + if (input.starts_with("xn--")) { + return false; + } size_t written_out{0}; uint32_t n = initial_n; int32_t i = 0; @@ -8132,7 +8158,7 @@ bool utf32_to_punycode(std::u32string_view input, std::string &out) { ++h; out.push_back(char(c)); } - if (c > 0x10ffff || (c >= 0xd880 && c < 0xe000)) { + if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) { return false; } } @@ -8969,7 +8995,7 @@ inline static direction find_direction(uint32_t code_point) noexcept { inline static size_t find_last_not_of_nsm( const std::u32string_view label) noexcept { - for (int i = label.size() - 1; i >= 0; i--) + for (int i = static_cast(label.size() - 1); i >= 0; i--) if (find_direction(label[i]) != direction::NSM) return i; return std::u32string_view::npos; @@ -9485,12 +9511,14 @@ bool is_label_valid(const std::u32string_view label) { for (size_t i = 0; i <= last_non_nsm_char; i++) { const direction d = find_direction(label[i]); + // NOLINTBEGIN(bugprone-assignment-in-if-condition) // In an RTL label, if an EN is present, no AN may be present, and vice // versa. if ((d == direction::EN && ((has_en = true) && has_an)) || (d == direction::AN && ((has_an = true) && has_en))) { return false; } + // NOLINTEND(bugprone-assignment-in-if-condition) if (!(d == direction::R || d == direction::AL || d == direction::AN || d == direction::EN || d == direction::ES || d == direction::CS || @@ -9519,25 +9547,14 @@ bool is_label_valid(const std::u32string_view label) { #include #include +#include -namespace ada::idna { - -bool begins_with(std::u32string_view view, std::u32string_view prefix) { - if (view.size() < prefix.size()) { - return false; - } - // constexpr as of C++20 - return std::equal(prefix.begin(), prefix.end(), view.begin()); -} +#ifdef ADA_USE_SIMDUTF +#include "simdutf.h" +#endif -bool begins_with(std::string_view view, std::string_view prefix) { - if (view.size() < prefix.size()) { - return false; - } - // constexpr as of C++20 - return std::equal(prefix.begin(), prefix.end(), view.begin()); -} +namespace ada::idna { bool constexpr is_ascii(std::u32string_view view) { for (uint32_t c : view) { @@ -9577,8 +9594,7 @@ inline bool is_forbidden_domain_code_point(const char c) noexcept { } bool contains_forbidden_domain_code_point(std::string_view view) { - return ( - std::any_of(view.begin(), view.end(), is_forbidden_domain_code_point)); + return std::ranges::any_of(view, is_forbidden_domain_code_point); } // We return "" on error. @@ -9601,7 +9617,7 @@ static std::string from_ascii_to_ascii(std::string_view ut8_string) { label_start += label_size_with_dot; if (label_size == 0) { // empty label? Nothing to do. - } else if (begins_with(label_view, "xn--")) { + } else if (label_view.starts_with("xn--")) { // The xn-- part is the expensive game. out.append(label_view); std::string_view puny_segment_ascii( @@ -9612,6 +9628,12 @@ static std::string from_ascii_to_ascii(std::string_view ut8_string) { if (!is_ok) { return error; } + // If the input is just ASCII, it should not have been encoded + // as punycode. + // https://github.com/whatwg/url/issues/760 + if (is_ascii(tmp_buffer)) { + return error; + } std::u32string post_map = ada::idna::map(tmp_buffer); if (tmp_buffer != post_map) { return error; @@ -9644,11 +9666,20 @@ std::string to_ascii(std::string_view ut8_string) { } static const std::string error = ""; // We convert to UTF-32 + +#ifdef ADA_USE_SIMDUTF + size_t utf32_length = + simdutf::utf32_length_from_utf8(ut8_string.data(), ut8_string.size()); + std::u32string utf32(utf32_length, '\0'); + size_t actual_utf32_length = simdutf::convert_utf8_to_utf32( + ut8_string.data(), ut8_string.size(), utf32.data()); +#else size_t utf32_length = ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size()); std::u32string utf32(utf32_length, '\0'); size_t actual_utf32_length = ada::idna::utf8_to_utf32( ut8_string.data(), ut8_string.size(), utf32.data()); +#endif if (actual_utf32_length == 0) { return error; } @@ -9668,7 +9699,7 @@ std::string to_ascii(std::string_view ut8_string) { label_start += label_size_with_dot; if (label_size == 0) { // empty label? Nothing to do. - } else if (begins_with(label_view, U"xn--")) { + } else if (label_view.starts_with(U"xn--")) { // we do not need to check, e.g., Xn-- because mapping goes to lower case for (char32_t c : label_view) { if (c >= 0x80) { @@ -9684,6 +9715,12 @@ std::string to_ascii(std::string_view ut8_string) { if (!is_ok) { return error; } + // If the input is just ASCII, it should not have been encoded + // as punycode. + // https://github.com/whatwg/url/issues/760 + if (is_ascii(tmp_buffer)) { + return error; + } std::u32string post_map = ada::idna::map(tmp_buffer); if (tmp_buffer != post_map) { return error; @@ -9734,6 +9771,10 @@ std::string to_ascii(std::string_view ut8_string) { #include +#ifdef ADA_USE_SIMDUTF +#include "simdutf.h" +#endif + namespace ada::idna { std::string to_unicode(std::string_view input) { std::string output; @@ -9747,17 +9788,24 @@ std::string to_unicode(std::string_view input) { is_last_label ? input.size() - label_start : loc_dot - label_start; auto label_view = std::string_view(input.data() + label_start, label_size); - if (ada::idna::begins_with(label_view, "xn--") && - ada::idna::is_ascii(label_view)) { + if (label_view.starts_with("xn--") && ada::idna::is_ascii(label_view)) { label_view.remove_prefix(4); if (ada::idna::verify_punycode(label_view)) { std::u32string tmp_buffer; if (ada::idna::punycode_to_utf32(label_view, tmp_buffer)) { +#ifdef ADA_USE_SIMDUTF + auto utf8_size = simdutf::utf8_length_from_utf32(tmp_buffer.data(), + tmp_buffer.size()); + std::string final_utf8(utf8_size, '\0'); + simdutf::convert_utf32_to_utf8(tmp_buffer.data(), tmp_buffer.size(), + final_utf8.data()); +#else auto utf8_size = ada::idna::utf8_length_from_utf32(tmp_buffer.data(), tmp_buffer.size()); std::string final_utf8(utf8_size, '\0'); ada::idna::utf32_to_utf8(tmp_buffer.data(), tmp_buffer.size(), final_utf8.data()); +#endif output.append(final_utf8); } else { // ToUnicode never fails. If any step fails, then the original input @@ -9783,6 +9831,613 @@ std::string to_unicode(std::string_view input) { } } // namespace ada::idna /* end file src/to_unicode.cpp */ +/* begin file src/identifier.cpp */ + +#include +#include +#include + +/* begin file src/id_tables.cpp */ +// IDNA 16.0.0 + +// clang-format off +#ifndef ADA_IDNA_IDENTIFIER_TABLES_H +#define ADA_IDNA_IDENTIFIER_TABLES_H +#include + +namespace ada::idna { + +const uint32_t id_continue[1393][2] = +{ + {48, 57}, {65, 90}, {95, 95}, {97, 122}, + {170, 170}, {181, 181}, {183, 183}, {186, 186}, + {192, 214}, {216, 246}, {248, 442}, {443, 443}, + {444, 447}, {448, 451}, {452, 659}, {660, 660}, + {661, 687}, {688, 705}, {710, 721}, {736, 740}, + {748, 748}, {750, 750}, {768, 879}, {880, 883}, + {884, 884}, {886, 887}, {890, 890}, {891, 893}, + {895, 895}, {902, 902}, {903, 903}, {904, 906}, + {908, 908}, {910, 929}, {931, 1013}, {1015, 1153}, + {1155, 1159}, {1162, 1327}, {1329, 1366}, {1369, 1369}, + {1376, 1416}, {1425, 1469}, {1471, 1471}, {1473, 1474}, + {1476, 1477}, {1479, 1479}, {1488, 1514}, {1519, 1522}, + {1552, 1562}, {1568, 1599}, {1600, 1600}, {1601, 1610}, + {1611, 1631}, {1632, 1641}, {1646, 1647}, {1648, 1648}, + {1649, 1747}, {1749, 1749}, {1750, 1756}, {1759, 1764}, + {1765, 1766}, {1767, 1768}, {1770, 1773}, {1774, 1775}, + {1776, 1785}, {1786, 1788}, {1791, 1791}, {1808, 1808}, + {1809, 1809}, {1810, 1839}, {1840, 1866}, {1869, 1957}, + {1958, 1968}, {1969, 1969}, {1984, 1993}, {1994, 2026}, + {2027, 2035}, {2036, 2037}, {2042, 2042}, {2045, 2045}, + {2048, 2069}, {2070, 2073}, {2074, 2074}, {2075, 2083}, + {2084, 2084}, {2085, 2087}, {2088, 2088}, {2089, 2093}, + {2112, 2136}, {2137, 2139}, {2144, 2154}, {2160, 2183}, + {2185, 2190}, {2199, 2207}, {2208, 2248}, {2249, 2249}, + {2250, 2273}, {2275, 2306}, {2307, 2307}, {2308, 2361}, + {2362, 2362}, {2363, 2363}, {2364, 2364}, {2365, 2365}, + {2366, 2368}, {2369, 2376}, {2377, 2380}, {2381, 2381}, + {2382, 2383}, {2384, 2384}, {2385, 2391}, {2392, 2401}, + {2402, 2403}, {2406, 2415}, {2417, 2417}, {2418, 2432}, + {2433, 2433}, {2434, 2435}, {2437, 2444}, {2447, 2448}, + {2451, 2472}, {2474, 2480}, {2482, 2482}, {2486, 2489}, + {2492, 2492}, {2493, 2493}, {2494, 2496}, {2497, 2500}, + {2503, 2504}, {2507, 2508}, {2509, 2509}, {2510, 2510}, + {2519, 2519}, {2524, 2525}, {2527, 2529}, {2530, 2531}, + {2534, 2543}, {2544, 2545}, {2556, 2556}, {2558, 2558}, + {2561, 2562}, {2563, 2563}, {2565, 2570}, {2575, 2576}, + {2579, 2600}, {2602, 2608}, {2610, 2611}, {2613, 2614}, + {2616, 2617}, {2620, 2620}, {2622, 2624}, {2625, 2626}, + {2631, 2632}, {2635, 2637}, {2641, 2641}, {2649, 2652}, + {2654, 2654}, {2662, 2671}, {2672, 2673}, {2674, 2676}, + {2677, 2677}, {2689, 2690}, {2691, 2691}, {2693, 2701}, + {2703, 2705}, {2707, 2728}, {2730, 2736}, {2738, 2739}, + {2741, 2745}, {2748, 2748}, {2749, 2749}, {2750, 2752}, + {2753, 2757}, {2759, 2760}, {2761, 2761}, {2763, 2764}, + {2765, 2765}, {2768, 2768}, {2784, 2785}, {2786, 2787}, + {2790, 2799}, {2809, 2809}, {2810, 2815}, {2817, 2817}, + {2818, 2819}, {2821, 2828}, {2831, 2832}, {2835, 2856}, + {2858, 2864}, {2866, 2867}, {2869, 2873}, {2876, 2876}, + {2877, 2877}, {2878, 2878}, {2879, 2879}, {2880, 2880}, + {2881, 2884}, {2887, 2888}, {2891, 2892}, {2893, 2893}, + {2901, 2902}, {2903, 2903}, {2908, 2909}, {2911, 2913}, + {2914, 2915}, {2918, 2927}, {2929, 2929}, {2946, 2946}, + {2947, 2947}, {2949, 2954}, {2958, 2960}, {2962, 2965}, + {2969, 2970}, {2972, 2972}, {2974, 2975}, {2979, 2980}, + {2984, 2986}, {2990, 3001}, {3006, 3007}, {3008, 3008}, + {3009, 3010}, {3014, 3016}, {3018, 3020}, {3021, 3021}, + {3024, 3024}, {3031, 3031}, {3046, 3055}, {3072, 3072}, + {3073, 3075}, {3076, 3076}, {3077, 3084}, {3086, 3088}, + {3090, 3112}, {3114, 3129}, {3132, 3132}, {3133, 3133}, + {3134, 3136}, {3137, 3140}, {3142, 3144}, {3146, 3149}, + {3157, 3158}, {3160, 3162}, {3165, 3165}, {3168, 3169}, + {3170, 3171}, {3174, 3183}, {3200, 3200}, {3201, 3201}, + {3202, 3203}, {3205, 3212}, {3214, 3216}, {3218, 3240}, + {3242, 3251}, {3253, 3257}, {3260, 3260}, {3261, 3261}, + {3262, 3262}, {3263, 3263}, {3264, 3268}, {3270, 3270}, + {3271, 3272}, {3274, 3275}, {3276, 3277}, {3285, 3286}, + {3293, 3294}, {3296, 3297}, {3298, 3299}, {3302, 3311}, + {3313, 3314}, {3315, 3315}, {3328, 3329}, {3330, 3331}, + {3332, 3340}, {3342, 3344}, {3346, 3386}, {3387, 3388}, + {3389, 3389}, {3390, 3392}, {3393, 3396}, {3398, 3400}, + {3402, 3404}, {3405, 3405}, {3406, 3406}, {3412, 3414}, + {3415, 3415}, {3423, 3425}, {3426, 3427}, {3430, 3439}, + {3450, 3455}, {3457, 3457}, {3458, 3459}, {3461, 3478}, + {3482, 3505}, {3507, 3515}, {3517, 3517}, {3520, 3526}, + {3530, 3530}, {3535, 3537}, {3538, 3540}, {3542, 3542}, + {3544, 3551}, {3558, 3567}, {3570, 3571}, {3585, 3632}, + {3633, 3633}, {3634, 3635}, {3636, 3642}, {3648, 3653}, + {3654, 3654}, {3655, 3662}, {3664, 3673}, {3713, 3714}, + {3716, 3716}, {3718, 3722}, {3724, 3747}, {3749, 3749}, + {3751, 3760}, {3761, 3761}, {3762, 3763}, {3764, 3772}, + {3773, 3773}, {3776, 3780}, {3782, 3782}, {3784, 3790}, + {3792, 3801}, {3804, 3807}, {3840, 3840}, {3864, 3865}, + {3872, 3881}, {3893, 3893}, {3895, 3895}, {3897, 3897}, + {3902, 3903}, {3904, 3911}, {3913, 3948}, {3953, 3966}, + {3967, 3967}, {3968, 3972}, {3974, 3975}, {3976, 3980}, + {3981, 3991}, {3993, 4028}, {4038, 4038}, {4096, 4138}, + {4139, 4140}, {4141, 4144}, {4145, 4145}, {4146, 4151}, + {4152, 4152}, {4153, 4154}, {4155, 4156}, {4157, 4158}, + {4159, 4159}, {4160, 4169}, {4176, 4181}, {4182, 4183}, + {4184, 4185}, {4186, 4189}, {4190, 4192}, {4193, 4193}, + {4194, 4196}, {4197, 4198}, {4199, 4205}, {4206, 4208}, + {4209, 4212}, {4213, 4225}, {4226, 4226}, {4227, 4228}, + {4229, 4230}, {4231, 4236}, {4237, 4237}, {4238, 4238}, + {4239, 4239}, {4240, 4249}, {4250, 4252}, {4253, 4253}, + {4256, 4293}, {4295, 4295}, {4301, 4301}, {4304, 4346}, + {4348, 4348}, {4349, 4351}, {4352, 4680}, {4682, 4685}, + {4688, 4694}, {4696, 4696}, {4698, 4701}, {4704, 4744}, + {4746, 4749}, {4752, 4784}, {4786, 4789}, {4792, 4798}, + {4800, 4800}, {4802, 4805}, {4808, 4822}, {4824, 4880}, + {4882, 4885}, {4888, 4954}, {4957, 4959}, {4969, 4977}, + {4992, 5007}, {5024, 5109}, {5112, 5117}, {5121, 5740}, + {5743, 5759}, {5761, 5786}, {5792, 5866}, {5870, 5872}, + {5873, 5880}, {5888, 5905}, {5906, 5908}, {5909, 5909}, + {5919, 5937}, {5938, 5939}, {5940, 5940}, {5952, 5969}, + {5970, 5971}, {5984, 5996}, {5998, 6000}, {6002, 6003}, + {6016, 6067}, {6068, 6069}, {6070, 6070}, {6071, 6077}, + {6078, 6085}, {6086, 6086}, {6087, 6088}, {6089, 6099}, + {6103, 6103}, {6108, 6108}, {6109, 6109}, {6112, 6121}, + {6155, 6157}, {6159, 6159}, {6160, 6169}, {6176, 6210}, + {6211, 6211}, {6212, 6264}, {6272, 6276}, {6277, 6278}, + {6279, 6312}, {6313, 6313}, {6314, 6314}, {6320, 6389}, + {6400, 6430}, {6432, 6434}, {6435, 6438}, {6439, 6440}, + {6441, 6443}, {6448, 6449}, {6450, 6450}, {6451, 6456}, + {6457, 6459}, {6470, 6479}, {6480, 6509}, {6512, 6516}, + {6528, 6571}, {6576, 6601}, {6608, 6617}, {6618, 6618}, + {6656, 6678}, {6679, 6680}, {6681, 6682}, {6683, 6683}, + {6688, 6740}, {6741, 6741}, {6742, 6742}, {6743, 6743}, + {6744, 6750}, {6752, 6752}, {6753, 6753}, {6754, 6754}, + {6755, 6756}, {6757, 6764}, {6765, 6770}, {6771, 6780}, + {6783, 6783}, {6784, 6793}, {6800, 6809}, {6823, 6823}, + {6832, 6845}, {6847, 6862}, {6912, 6915}, {6916, 6916}, + {6917, 6963}, {6964, 6964}, {6965, 6965}, {6966, 6970}, + {6971, 6971}, {6972, 6972}, {6973, 6977}, {6978, 6978}, + {6979, 6980}, {6981, 6988}, {6992, 7001}, {7019, 7027}, + {7040, 7041}, {7042, 7042}, {7043, 7072}, {7073, 7073}, + {7074, 7077}, {7078, 7079}, {7080, 7081}, {7082, 7082}, + {7083, 7085}, {7086, 7087}, {7088, 7097}, {7098, 7141}, + {7142, 7142}, {7143, 7143}, {7144, 7145}, {7146, 7148}, + {7149, 7149}, {7150, 7150}, {7151, 7153}, {7154, 7155}, + {7168, 7203}, {7204, 7211}, {7212, 7219}, {7220, 7221}, + {7222, 7223}, {7232, 7241}, {7245, 7247}, {7248, 7257}, + {7258, 7287}, {7288, 7293}, {7296, 7306}, {7312, 7354}, + {7357, 7359}, {7376, 7378}, {7380, 7392}, {7393, 7393}, + {7394, 7400}, {7401, 7404}, {7405, 7405}, {7406, 7411}, + {7412, 7412}, {7413, 7414}, {7415, 7415}, {7416, 7417}, + {7418, 7418}, {7424, 7467}, {7468, 7530}, {7531, 7543}, + {7544, 7544}, {7545, 7578}, {7579, 7615}, {7616, 7679}, + {7680, 7957}, {7960, 7965}, {7968, 8005}, {8008, 8013}, + {8016, 8023}, {8025, 8025}, {8027, 8027}, {8029, 8029}, + {8031, 8061}, {8064, 8116}, {8118, 8124}, {8126, 8126}, + {8130, 8132}, {8134, 8140}, {8144, 8147}, {8150, 8155}, + {8160, 8172}, {8178, 8180}, {8182, 8188}, {8204, 8205}, + {8255, 8256}, {8276, 8276}, {8305, 8305}, {8319, 8319}, + {8336, 8348}, {8400, 8412}, {8417, 8417}, {8421, 8432}, + {8450, 8450}, {8455, 8455}, {8458, 8467}, {8469, 8469}, + {8472, 8472}, {8473, 8477}, {8484, 8484}, {8486, 8486}, + {8488, 8488}, {8490, 8493}, {8494, 8494}, {8495, 8500}, + {8501, 8504}, {8505, 8505}, {8508, 8511}, {8517, 8521}, + {8526, 8526}, {8544, 8578}, {8579, 8580}, {8581, 8584}, + {11264, 11387}, {11388, 11389}, {11390, 11492}, {11499, 11502}, + {11503, 11505}, {11506, 11507}, {11520, 11557}, {11559, 11559}, + {11565, 11565}, {11568, 11623}, {11631, 11631}, {11647, 11647}, + {11648, 11670}, {11680, 11686}, {11688, 11694}, {11696, 11702}, + {11704, 11710}, {11712, 11718}, {11720, 11726}, {11728, 11734}, + {11736, 11742}, {11744, 11775}, {12293, 12293}, {12294, 12294}, + {12295, 12295}, {12321, 12329}, {12330, 12333}, {12334, 12335}, + {12337, 12341}, {12344, 12346}, {12347, 12347}, {12348, 12348}, + {12353, 12438}, {12441, 12442}, {12443, 12444}, {12445, 12446}, + {12447, 12447}, {12449, 12538}, {12539, 12539}, {12540, 12542}, + {12543, 12543}, {12549, 12591}, {12593, 12686}, {12704, 12735}, + {12784, 12799}, {13312, 19903}, {19968, 40980}, {40981, 40981}, + {40982, 42124}, {42192, 42231}, {42232, 42237}, {42240, 42507}, + {42508, 42508}, {42512, 42527}, {42528, 42537}, {42538, 42539}, + {42560, 42605}, {42606, 42606}, {42607, 42607}, {42612, 42621}, + {42623, 42623}, {42624, 42651}, {42652, 42653}, {42654, 42655}, + {42656, 42725}, {42726, 42735}, {42736, 42737}, {42775, 42783}, + {42786, 42863}, {42864, 42864}, {42865, 42887}, {42888, 42888}, + {42891, 42894}, {42895, 42895}, {42896, 42957}, {42960, 42961}, + {42963, 42963}, {42965, 42972}, {42994, 42996}, {42997, 42998}, + {42999, 42999}, {43000, 43001}, {43002, 43002}, {43003, 43009}, + {43010, 43010}, {43011, 43013}, {43014, 43014}, {43015, 43018}, + {43019, 43019}, {43020, 43042}, {43043, 43044}, {43045, 43046}, + {43047, 43047}, {43052, 43052}, {43072, 43123}, {43136, 43137}, + {43138, 43187}, {43188, 43203}, {43204, 43205}, {43216, 43225}, + {43232, 43249}, {43250, 43255}, {43259, 43259}, {43261, 43262}, + {43263, 43263}, {43264, 43273}, {43274, 43301}, {43302, 43309}, + {43312, 43334}, {43335, 43345}, {43346, 43347}, {43360, 43388}, + {43392, 43394}, {43395, 43395}, {43396, 43442}, {43443, 43443}, + {43444, 43445}, {43446, 43449}, {43450, 43451}, {43452, 43453}, + {43454, 43456}, {43471, 43471}, {43472, 43481}, {43488, 43492}, + {43493, 43493}, {43494, 43494}, {43495, 43503}, {43504, 43513}, + {43514, 43518}, {43520, 43560}, {43561, 43566}, {43567, 43568}, + {43569, 43570}, {43571, 43572}, {43573, 43574}, {43584, 43586}, + {43587, 43587}, {43588, 43595}, {43596, 43596}, {43597, 43597}, + {43600, 43609}, {43616, 43631}, {43632, 43632}, {43633, 43638}, + {43642, 43642}, {43643, 43643}, {43644, 43644}, {43645, 43645}, + {43646, 43695}, {43696, 43696}, {43697, 43697}, {43698, 43700}, + {43701, 43702}, {43703, 43704}, {43705, 43709}, {43710, 43711}, + {43712, 43712}, {43713, 43713}, {43714, 43714}, {43739, 43740}, + {43741, 43741}, {43744, 43754}, {43755, 43755}, {43756, 43757}, + {43758, 43759}, {43762, 43762}, {43763, 43764}, {43765, 43765}, + {43766, 43766}, {43777, 43782}, {43785, 43790}, {43793, 43798}, + {43808, 43814}, {43816, 43822}, {43824, 43866}, {43868, 43871}, + {43872, 43880}, {43881, 43881}, {43888, 43967}, {43968, 44002}, + {44003, 44004}, {44005, 44005}, {44006, 44007}, {44008, 44008}, + {44009, 44010}, {44012, 44012}, {44013, 44013}, {44016, 44025}, + {44032, 55203}, {55216, 55238}, {55243, 55291}, {63744, 64109}, + {64112, 64217}, {64256, 64262}, {64275, 64279}, {64285, 64285}, + {64286, 64286}, {64287, 64296}, {64298, 64310}, {64312, 64316}, + {64318, 64318}, {64320, 64321}, {64323, 64324}, {64326, 64433}, + {64467, 64829}, {64848, 64911}, {64914, 64967}, {65008, 65019}, + {65024, 65039}, {65056, 65071}, {65075, 65076}, {65101, 65103}, + {65136, 65140}, {65142, 65276}, {65296, 65305}, {65313, 65338}, + {65343, 65343}, {65345, 65370}, {65381, 65381}, {65382, 65391}, + {65392, 65392}, {65393, 65437}, {65438, 65439}, {65440, 65470}, + {65474, 65479}, {65482, 65487}, {65490, 65495}, {65498, 65500}, + {65536, 65547}, {65549, 65574}, {65576, 65594}, {65596, 65597}, + {65599, 65613}, {65616, 65629}, {65664, 65786}, {65856, 65908}, + {66045, 66045}, {66176, 66204}, {66208, 66256}, {66272, 66272}, + {66304, 66335}, {66349, 66368}, {66369, 66369}, {66370, 66377}, + {66378, 66378}, {66384, 66421}, {66422, 66426}, {66432, 66461}, + {66464, 66499}, {66504, 66511}, {66513, 66517}, {66560, 66639}, + {66640, 66717}, {66720, 66729}, {66736, 66771}, {66776, 66811}, + {66816, 66855}, {66864, 66915}, {66928, 66938}, {66940, 66954}, + {66956, 66962}, {66964, 66965}, {66967, 66977}, {66979, 66993}, + {66995, 67001}, {67003, 67004}, {67008, 67059}, {67072, 67382}, + {67392, 67413}, {67424, 67431}, {67456, 67461}, {67463, 67504}, + {67506, 67514}, {67584, 67589}, {67592, 67592}, {67594, 67637}, + {67639, 67640}, {67644, 67644}, {67647, 67669}, {67680, 67702}, + {67712, 67742}, {67808, 67826}, {67828, 67829}, {67840, 67861}, + {67872, 67897}, {67968, 68023}, {68030, 68031}, {68096, 68096}, + {68097, 68099}, {68101, 68102}, {68108, 68111}, {68112, 68115}, + {68117, 68119}, {68121, 68149}, {68152, 68154}, {68159, 68159}, + {68192, 68220}, {68224, 68252}, {68288, 68295}, {68297, 68324}, + {68325, 68326}, {68352, 68405}, {68416, 68437}, {68448, 68466}, + {68480, 68497}, {68608, 68680}, {68736, 68786}, {68800, 68850}, + {68864, 68899}, {68900, 68903}, {68912, 68921}, {68928, 68937}, + {68938, 68941}, {68942, 68942}, {68943, 68943}, {68944, 68965}, + {68969, 68973}, {68975, 68975}, {68976, 68997}, {69248, 69289}, + {69291, 69292}, {69296, 69297}, {69314, 69316}, {69372, 69375}, + {69376, 69404}, {69415, 69415}, {69424, 69445}, {69446, 69456}, + {69488, 69505}, {69506, 69509}, {69552, 69572}, {69600, 69622}, + {69632, 69632}, {69633, 69633}, {69634, 69634}, {69635, 69687}, + {69688, 69702}, {69734, 69743}, {69744, 69744}, {69745, 69746}, + {69747, 69748}, {69749, 69749}, {69759, 69761}, {69762, 69762}, + {69763, 69807}, {69808, 69810}, {69811, 69814}, {69815, 69816}, + {69817, 69818}, {69826, 69826}, {69840, 69864}, {69872, 69881}, + {69888, 69890}, {69891, 69926}, {69927, 69931}, {69932, 69932}, + {69933, 69940}, {69942, 69951}, {69956, 69956}, {69957, 69958}, + {69959, 69959}, {69968, 70002}, {70003, 70003}, {70006, 70006}, + {70016, 70017}, {70018, 70018}, {70019, 70066}, {70067, 70069}, + {70070, 70078}, {70079, 70080}, {70081, 70084}, {70089, 70092}, + {70094, 70094}, {70095, 70095}, {70096, 70105}, {70106, 70106}, + {70108, 70108}, {70144, 70161}, {70163, 70187}, {70188, 70190}, + {70191, 70193}, {70194, 70195}, {70196, 70196}, {70197, 70197}, + {70198, 70199}, {70206, 70206}, {70207, 70208}, {70209, 70209}, + {70272, 70278}, {70280, 70280}, {70282, 70285}, {70287, 70301}, + {70303, 70312}, {70320, 70366}, {70367, 70367}, {70368, 70370}, + {70371, 70378}, {70384, 70393}, {70400, 70401}, {70402, 70403}, + {70405, 70412}, {70415, 70416}, {70419, 70440}, {70442, 70448}, + {70450, 70451}, {70453, 70457}, {70459, 70460}, {70461, 70461}, + {70462, 70463}, {70464, 70464}, {70465, 70468}, {70471, 70472}, + {70475, 70477}, {70480, 70480}, {70487, 70487}, {70493, 70497}, + {70498, 70499}, {70502, 70508}, {70512, 70516}, {70528, 70537}, + {70539, 70539}, {70542, 70542}, {70544, 70581}, {70583, 70583}, + {70584, 70586}, {70587, 70592}, {70594, 70594}, {70597, 70597}, + {70599, 70602}, {70604, 70605}, {70606, 70606}, {70607, 70607}, + {70608, 70608}, {70609, 70609}, {70610, 70610}, {70611, 70611}, + {70625, 70626}, {70656, 70708}, {70709, 70711}, {70712, 70719}, + {70720, 70721}, {70722, 70724}, {70725, 70725}, {70726, 70726}, + {70727, 70730}, {70736, 70745}, {70750, 70750}, {70751, 70753}, + {70784, 70831}, {70832, 70834}, {70835, 70840}, {70841, 70841}, + {70842, 70842}, {70843, 70846}, {70847, 70848}, {70849, 70849}, + {70850, 70851}, {70852, 70853}, {70855, 70855}, {70864, 70873}, + {71040, 71086}, {71087, 71089}, {71090, 71093}, {71096, 71099}, + {71100, 71101}, {71102, 71102}, {71103, 71104}, {71128, 71131}, + {71132, 71133}, {71168, 71215}, {71216, 71218}, {71219, 71226}, + {71227, 71228}, {71229, 71229}, {71230, 71230}, {71231, 71232}, + {71236, 71236}, {71248, 71257}, {71296, 71338}, {71339, 71339}, + {71340, 71340}, {71341, 71341}, {71342, 71343}, {71344, 71349}, + {71350, 71350}, {71351, 71351}, {71352, 71352}, {71360, 71369}, + {71376, 71395}, {71424, 71450}, {71453, 71453}, {71454, 71454}, + {71455, 71455}, {71456, 71457}, {71458, 71461}, {71462, 71462}, + {71463, 71467}, {71472, 71481}, {71488, 71494}, {71680, 71723}, + {71724, 71726}, {71727, 71735}, {71736, 71736}, {71737, 71738}, + {71840, 71903}, {71904, 71913}, {71935, 71942}, {71945, 71945}, + {71948, 71955}, {71957, 71958}, {71960, 71983}, {71984, 71989}, + {71991, 71992}, {71995, 71996}, {71997, 71997}, {71998, 71998}, + {71999, 71999}, {72000, 72000}, {72001, 72001}, {72002, 72002}, + {72003, 72003}, {72016, 72025}, {72096, 72103}, {72106, 72144}, + {72145, 72147}, {72148, 72151}, {72154, 72155}, {72156, 72159}, + {72160, 72160}, {72161, 72161}, {72163, 72163}, {72164, 72164}, + {72192, 72192}, {72193, 72202}, {72203, 72242}, {72243, 72248}, + {72249, 72249}, {72250, 72250}, {72251, 72254}, {72263, 72263}, + {72272, 72272}, {72273, 72278}, {72279, 72280}, {72281, 72283}, + {72284, 72329}, {72330, 72342}, {72343, 72343}, {72344, 72345}, + {72349, 72349}, {72368, 72440}, {72640, 72672}, {72688, 72697}, + {72704, 72712}, {72714, 72750}, {72751, 72751}, {72752, 72758}, + {72760, 72765}, {72766, 72766}, {72767, 72767}, {72768, 72768}, + {72784, 72793}, {72818, 72847}, {72850, 72871}, {72873, 72873}, + {72874, 72880}, {72881, 72881}, {72882, 72883}, {72884, 72884}, + {72885, 72886}, {72960, 72966}, {72968, 72969}, {72971, 73008}, + {73009, 73014}, {73018, 73018}, {73020, 73021}, {73023, 73029}, + {73030, 73030}, {73031, 73031}, {73040, 73049}, {73056, 73061}, + {73063, 73064}, {73066, 73097}, {73098, 73102}, {73104, 73105}, + {73107, 73108}, {73109, 73109}, {73110, 73110}, {73111, 73111}, + {73112, 73112}, {73120, 73129}, {73440, 73458}, {73459, 73460}, + {73461, 73462}, {73472, 73473}, {73474, 73474}, {73475, 73475}, + {73476, 73488}, {73490, 73523}, {73524, 73525}, {73526, 73530}, + {73534, 73535}, {73536, 73536}, {73537, 73537}, {73538, 73538}, + {73552, 73561}, {73562, 73562}, {73648, 73648}, {73728, 74649}, + {74752, 74862}, {74880, 75075}, {77712, 77808}, {77824, 78895}, + {78912, 78912}, {78913, 78918}, {78919, 78933}, {78944, 82938}, + {82944, 83526}, {90368, 90397}, {90398, 90409}, {90410, 90412}, + {90413, 90415}, {90416, 90425}, {92160, 92728}, {92736, 92766}, + {92768, 92777}, {92784, 92862}, {92864, 92873}, {92880, 92909}, + {92912, 92916}, {92928, 92975}, {92976, 92982}, {92992, 92995}, + {93008, 93017}, {93027, 93047}, {93053, 93071}, {93504, 93506}, + {93507, 93546}, {93547, 93548}, {93552, 93561}, {93760, 93823}, + {93952, 94026}, {94031, 94031}, {94032, 94032}, {94033, 94087}, + {94095, 94098}, {94099, 94111}, {94176, 94177}, {94179, 94179}, + {94180, 94180}, {94192, 94193}, {94208, 100343}, {100352, 101589}, + {101631, 101640}, {110576, 110579}, {110581, 110587}, {110589, 110590}, + {110592, 110882}, {110898, 110898}, {110928, 110930}, {110933, 110933}, + {110948, 110951}, {110960, 111355}, {113664, 113770}, {113776, 113788}, + {113792, 113800}, {113808, 113817}, {113821, 113822}, {118000, 118009}, + {118528, 118573}, {118576, 118598}, {119141, 119142}, {119143, 119145}, + {119149, 119154}, {119163, 119170}, {119173, 119179}, {119210, 119213}, + {119362, 119364}, {119808, 119892}, {119894, 119964}, {119966, 119967}, + {119970, 119970}, {119973, 119974}, {119977, 119980}, {119982, 119993}, + {119995, 119995}, {119997, 120003}, {120005, 120069}, {120071, 120074}, + {120077, 120084}, {120086, 120092}, {120094, 120121}, {120123, 120126}, + {120128, 120132}, {120134, 120134}, {120138, 120144}, {120146, 120485}, + {120488, 120512}, {120514, 120538}, {120540, 120570}, {120572, 120596}, + {120598, 120628}, {120630, 120654}, {120656, 120686}, {120688, 120712}, + {120714, 120744}, {120746, 120770}, {120772, 120779}, {120782, 120831}, + {121344, 121398}, {121403, 121452}, {121461, 121461}, {121476, 121476}, + {121499, 121503}, {121505, 121519}, {122624, 122633}, {122634, 122634}, + {122635, 122654}, {122661, 122666}, {122880, 122886}, {122888, 122904}, + {122907, 122913}, {122915, 122916}, {122918, 122922}, {122928, 122989}, + {123023, 123023}, {123136, 123180}, {123184, 123190}, {123191, 123197}, + {123200, 123209}, {123214, 123214}, {123536, 123565}, {123566, 123566}, + {123584, 123627}, {123628, 123631}, {123632, 123641}, {124112, 124138}, + {124139, 124139}, {124140, 124143}, {124144, 124153}, {124368, 124397}, + {124398, 124399}, {124400, 124400}, {124401, 124410}, {124896, 124902}, + {124904, 124907}, {124909, 124910}, {124912, 124926}, {124928, 125124}, + {125136, 125142}, {125184, 125251}, {125252, 125258}, {125259, 125259}, + {125264, 125273}, {126464, 126467}, {126469, 126495}, {126497, 126498}, + {126500, 126500}, {126503, 126503}, {126505, 126514}, {126516, 126519}, + {126521, 126521}, {126523, 126523}, {126530, 126530}, {126535, 126535}, + {126537, 126537}, {126539, 126539}, {126541, 126543}, {126545, 126546}, + {126548, 126548}, {126551, 126551}, {126553, 126553}, {126555, 126555}, + {126557, 126557}, {126559, 126559}, {126561, 126562}, {126564, 126564}, + {126567, 126570}, {126572, 126578}, {126580, 126583}, {126585, 126588}, + {126590, 126590}, {126592, 126601}, {126603, 126619}, {126625, 126627}, + {126629, 126633}, {126635, 126651}, {130032, 130041}, {131072, 173791}, + {173824, 177977}, {177984, 178205}, {178208, 183969}, {183984, 191456}, + {191472, 192093}, {194560, 195101}, {196608, 201546}, {201552, 205743}, + {917760, 917999} +}; +const uint32_t id_start[763][2] = +{ + {65, 90}, {97, 122}, {170, 170}, {181, 181}, + {186, 186}, {192, 214}, {216, 246}, {248, 442}, + {443, 443}, {444, 447}, {448, 451}, {452, 659}, + {660, 660}, {661, 687}, {688, 705}, {710, 721}, + {736, 740}, {748, 748}, {750, 750}, {880, 883}, + {884, 884}, {886, 887}, {890, 890}, {891, 893}, + {895, 895}, {902, 902}, {904, 906}, {908, 908}, + {910, 929}, {931, 1013}, {1015, 1153}, {1162, 1327}, + {1329, 1366}, {1369, 1369}, {1376, 1416}, {1488, 1514}, + {1519, 1522}, {1568, 1599}, {1600, 1600}, {1601, 1610}, + {1646, 1647}, {1649, 1747}, {1749, 1749}, {1765, 1766}, + {1774, 1775}, {1786, 1788}, {1791, 1791}, {1808, 1808}, + {1810, 1839}, {1869, 1957}, {1969, 1969}, {1994, 2026}, + {2036, 2037}, {2042, 2042}, {2048, 2069}, {2074, 2074}, + {2084, 2084}, {2088, 2088}, {2112, 2136}, {2144, 2154}, + {2160, 2183}, {2185, 2190}, {2208, 2248}, {2249, 2249}, + {2308, 2361}, {2365, 2365}, {2384, 2384}, {2392, 2401}, + {2417, 2417}, {2418, 2432}, {2437, 2444}, {2447, 2448}, + {2451, 2472}, {2474, 2480}, {2482, 2482}, {2486, 2489}, + {2493, 2493}, {2510, 2510}, {2524, 2525}, {2527, 2529}, + {2544, 2545}, {2556, 2556}, {2565, 2570}, {2575, 2576}, + {2579, 2600}, {2602, 2608}, {2610, 2611}, {2613, 2614}, + {2616, 2617}, {2649, 2652}, {2654, 2654}, {2674, 2676}, + {2693, 2701}, {2703, 2705}, {2707, 2728}, {2730, 2736}, + {2738, 2739}, {2741, 2745}, {2749, 2749}, {2768, 2768}, + {2784, 2785}, {2809, 2809}, {2821, 2828}, {2831, 2832}, + {2835, 2856}, {2858, 2864}, {2866, 2867}, {2869, 2873}, + {2877, 2877}, {2908, 2909}, {2911, 2913}, {2929, 2929}, + {2947, 2947}, {2949, 2954}, {2958, 2960}, {2962, 2965}, + {2969, 2970}, {2972, 2972}, {2974, 2975}, {2979, 2980}, + {2984, 2986}, {2990, 3001}, {3024, 3024}, {3077, 3084}, + {3086, 3088}, {3090, 3112}, {3114, 3129}, {3133, 3133}, + {3160, 3162}, {3165, 3165}, {3168, 3169}, {3200, 3200}, + {3205, 3212}, {3214, 3216}, {3218, 3240}, {3242, 3251}, + {3253, 3257}, {3261, 3261}, {3293, 3294}, {3296, 3297}, + {3313, 3314}, {3332, 3340}, {3342, 3344}, {3346, 3386}, + {3389, 3389}, {3406, 3406}, {3412, 3414}, {3423, 3425}, + {3450, 3455}, {3461, 3478}, {3482, 3505}, {3507, 3515}, + {3517, 3517}, {3520, 3526}, {3585, 3632}, {3634, 3635}, + {3648, 3653}, {3654, 3654}, {3713, 3714}, {3716, 3716}, + {3718, 3722}, {3724, 3747}, {3749, 3749}, {3751, 3760}, + {3762, 3763}, {3773, 3773}, {3776, 3780}, {3782, 3782}, + {3804, 3807}, {3840, 3840}, {3904, 3911}, {3913, 3948}, + {3976, 3980}, {4096, 4138}, {4159, 4159}, {4176, 4181}, + {4186, 4189}, {4193, 4193}, {4197, 4198}, {4206, 4208}, + {4213, 4225}, {4238, 4238}, {4256, 4293}, {4295, 4295}, + {4301, 4301}, {4304, 4346}, {4348, 4348}, {4349, 4351}, + {4352, 4680}, {4682, 4685}, {4688, 4694}, {4696, 4696}, + {4698, 4701}, {4704, 4744}, {4746, 4749}, {4752, 4784}, + {4786, 4789}, {4792, 4798}, {4800, 4800}, {4802, 4805}, + {4808, 4822}, {4824, 4880}, {4882, 4885}, {4888, 4954}, + {4992, 5007}, {5024, 5109}, {5112, 5117}, {5121, 5740}, + {5743, 5759}, {5761, 5786}, {5792, 5866}, {5870, 5872}, + {5873, 5880}, {5888, 5905}, {5919, 5937}, {5952, 5969}, + {5984, 5996}, {5998, 6000}, {6016, 6067}, {6103, 6103}, + {6108, 6108}, {6176, 6210}, {6211, 6211}, {6212, 6264}, + {6272, 6276}, {6277, 6278}, {6279, 6312}, {6314, 6314}, + {6320, 6389}, {6400, 6430}, {6480, 6509}, {6512, 6516}, + {6528, 6571}, {6576, 6601}, {6656, 6678}, {6688, 6740}, + {6823, 6823}, {6917, 6963}, {6981, 6988}, {7043, 7072}, + {7086, 7087}, {7098, 7141}, {7168, 7203}, {7245, 7247}, + {7258, 7287}, {7288, 7293}, {7296, 7306}, {7312, 7354}, + {7357, 7359}, {7401, 7404}, {7406, 7411}, {7413, 7414}, + {7418, 7418}, {7424, 7467}, {7468, 7530}, {7531, 7543}, + {7544, 7544}, {7545, 7578}, {7579, 7615}, {7680, 7957}, + {7960, 7965}, {7968, 8005}, {8008, 8013}, {8016, 8023}, + {8025, 8025}, {8027, 8027}, {8029, 8029}, {8031, 8061}, + {8064, 8116}, {8118, 8124}, {8126, 8126}, {8130, 8132}, + {8134, 8140}, {8144, 8147}, {8150, 8155}, {8160, 8172}, + {8178, 8180}, {8182, 8188}, {8305, 8305}, {8319, 8319}, + {8336, 8348}, {8450, 8450}, {8455, 8455}, {8458, 8467}, + {8469, 8469}, {8472, 8472}, {8473, 8477}, {8484, 8484}, + {8486, 8486}, {8488, 8488}, {8490, 8493}, {8494, 8494}, + {8495, 8500}, {8501, 8504}, {8505, 8505}, {8508, 8511}, + {8517, 8521}, {8526, 8526}, {8544, 8578}, {8579, 8580}, + {8581, 8584}, {11264, 11387}, {11388, 11389}, {11390, 11492}, + {11499, 11502}, {11506, 11507}, {11520, 11557}, {11559, 11559}, + {11565, 11565}, {11568, 11623}, {11631, 11631}, {11648, 11670}, + {11680, 11686}, {11688, 11694}, {11696, 11702}, {11704, 11710}, + {11712, 11718}, {11720, 11726}, {11728, 11734}, {11736, 11742}, + {12293, 12293}, {12294, 12294}, {12295, 12295}, {12321, 12329}, + {12337, 12341}, {12344, 12346}, {12347, 12347}, {12348, 12348}, + {12353, 12438}, {12443, 12444}, {12445, 12446}, {12447, 12447}, + {12449, 12538}, {12540, 12542}, {12543, 12543}, {12549, 12591}, + {12593, 12686}, {12704, 12735}, {12784, 12799}, {13312, 19903}, + {19968, 40980}, {40981, 40981}, {40982, 42124}, {42192, 42231}, + {42232, 42237}, {42240, 42507}, {42508, 42508}, {42512, 42527}, + {42538, 42539}, {42560, 42605}, {42606, 42606}, {42623, 42623}, + {42624, 42651}, {42652, 42653}, {42656, 42725}, {42726, 42735}, + {42775, 42783}, {42786, 42863}, {42864, 42864}, {42865, 42887}, + {42888, 42888}, {42891, 42894}, {42895, 42895}, {42896, 42957}, + {42960, 42961}, {42963, 42963}, {42965, 42972}, {42994, 42996}, + {42997, 42998}, {42999, 42999}, {43000, 43001}, {43002, 43002}, + {43003, 43009}, {43011, 43013}, {43015, 43018}, {43020, 43042}, + {43072, 43123}, {43138, 43187}, {43250, 43255}, {43259, 43259}, + {43261, 43262}, {43274, 43301}, {43312, 43334}, {43360, 43388}, + {43396, 43442}, {43471, 43471}, {43488, 43492}, {43494, 43494}, + {43495, 43503}, {43514, 43518}, {43520, 43560}, {43584, 43586}, + {43588, 43595}, {43616, 43631}, {43632, 43632}, {43633, 43638}, + {43642, 43642}, {43646, 43695}, {43697, 43697}, {43701, 43702}, + {43705, 43709}, {43712, 43712}, {43714, 43714}, {43739, 43740}, + {43741, 43741}, {43744, 43754}, {43762, 43762}, {43763, 43764}, + {43777, 43782}, {43785, 43790}, {43793, 43798}, {43808, 43814}, + {43816, 43822}, {43824, 43866}, {43868, 43871}, {43872, 43880}, + {43881, 43881}, {43888, 43967}, {43968, 44002}, {44032, 55203}, + {55216, 55238}, {55243, 55291}, {63744, 64109}, {64112, 64217}, + {64256, 64262}, {64275, 64279}, {64285, 64285}, {64287, 64296}, + {64298, 64310}, {64312, 64316}, {64318, 64318}, {64320, 64321}, + {64323, 64324}, {64326, 64433}, {64467, 64829}, {64848, 64911}, + {64914, 64967}, {65008, 65019}, {65136, 65140}, {65142, 65276}, + {65313, 65338}, {65345, 65370}, {65382, 65391}, {65392, 65392}, + {65393, 65437}, {65438, 65439}, {65440, 65470}, {65474, 65479}, + {65482, 65487}, {65490, 65495}, {65498, 65500}, {65536, 65547}, + {65549, 65574}, {65576, 65594}, {65596, 65597}, {65599, 65613}, + {65616, 65629}, {65664, 65786}, {65856, 65908}, {66176, 66204}, + {66208, 66256}, {66304, 66335}, {66349, 66368}, {66369, 66369}, + {66370, 66377}, {66378, 66378}, {66384, 66421}, {66432, 66461}, + {66464, 66499}, {66504, 66511}, {66513, 66517}, {66560, 66639}, + {66640, 66717}, {66736, 66771}, {66776, 66811}, {66816, 66855}, + {66864, 66915}, {66928, 66938}, {66940, 66954}, {66956, 66962}, + {66964, 66965}, {66967, 66977}, {66979, 66993}, {66995, 67001}, + {67003, 67004}, {67008, 67059}, {67072, 67382}, {67392, 67413}, + {67424, 67431}, {67456, 67461}, {67463, 67504}, {67506, 67514}, + {67584, 67589}, {67592, 67592}, {67594, 67637}, {67639, 67640}, + {67644, 67644}, {67647, 67669}, {67680, 67702}, {67712, 67742}, + {67808, 67826}, {67828, 67829}, {67840, 67861}, {67872, 67897}, + {67968, 68023}, {68030, 68031}, {68096, 68096}, {68112, 68115}, + {68117, 68119}, {68121, 68149}, {68192, 68220}, {68224, 68252}, + {68288, 68295}, {68297, 68324}, {68352, 68405}, {68416, 68437}, + {68448, 68466}, {68480, 68497}, {68608, 68680}, {68736, 68786}, + {68800, 68850}, {68864, 68899}, {68938, 68941}, {68942, 68942}, + {68943, 68943}, {68944, 68965}, {68975, 68975}, {68976, 68997}, + {69248, 69289}, {69296, 69297}, {69314, 69316}, {69376, 69404}, + {69415, 69415}, {69424, 69445}, {69488, 69505}, {69552, 69572}, + {69600, 69622}, {69635, 69687}, {69745, 69746}, {69749, 69749}, + {69763, 69807}, {69840, 69864}, {69891, 69926}, {69956, 69956}, + {69959, 69959}, {69968, 70002}, {70006, 70006}, {70019, 70066}, + {70081, 70084}, {70106, 70106}, {70108, 70108}, {70144, 70161}, + {70163, 70187}, {70207, 70208}, {70272, 70278}, {70280, 70280}, + {70282, 70285}, {70287, 70301}, {70303, 70312}, {70320, 70366}, + {70405, 70412}, {70415, 70416}, {70419, 70440}, {70442, 70448}, + {70450, 70451}, {70453, 70457}, {70461, 70461}, {70480, 70480}, + {70493, 70497}, {70528, 70537}, {70539, 70539}, {70542, 70542}, + {70544, 70581}, {70583, 70583}, {70609, 70609}, {70611, 70611}, + {70656, 70708}, {70727, 70730}, {70751, 70753}, {70784, 70831}, + {70852, 70853}, {70855, 70855}, {71040, 71086}, {71128, 71131}, + {71168, 71215}, {71236, 71236}, {71296, 71338}, {71352, 71352}, + {71424, 71450}, {71488, 71494}, {71680, 71723}, {71840, 71903}, + {71935, 71942}, {71945, 71945}, {71948, 71955}, {71957, 71958}, + {71960, 71983}, {71999, 71999}, {72001, 72001}, {72096, 72103}, + {72106, 72144}, {72161, 72161}, {72163, 72163}, {72192, 72192}, + {72203, 72242}, {72250, 72250}, {72272, 72272}, {72284, 72329}, + {72349, 72349}, {72368, 72440}, {72640, 72672}, {72704, 72712}, + {72714, 72750}, {72768, 72768}, {72818, 72847}, {72960, 72966}, + {72968, 72969}, {72971, 73008}, {73030, 73030}, {73056, 73061}, + {73063, 73064}, {73066, 73097}, {73112, 73112}, {73440, 73458}, + {73474, 73474}, {73476, 73488}, {73490, 73523}, {73648, 73648}, + {73728, 74649}, {74752, 74862}, {74880, 75075}, {77712, 77808}, + {77824, 78895}, {78913, 78918}, {78944, 82938}, {82944, 83526}, + {90368, 90397}, {92160, 92728}, {92736, 92766}, {92784, 92862}, + {92880, 92909}, {92928, 92975}, {92992, 92995}, {93027, 93047}, + {93053, 93071}, {93504, 93506}, {93507, 93546}, {93547, 93548}, + {93760, 93823}, {93952, 94026}, {94032, 94032}, {94099, 94111}, + {94176, 94177}, {94179, 94179}, {94208, 100343}, {100352, 101589}, + {101631, 101640}, {110576, 110579}, {110581, 110587}, {110589, 110590}, + {110592, 110882}, {110898, 110898}, {110928, 110930}, {110933, 110933}, + {110948, 110951}, {110960, 111355}, {113664, 113770}, {113776, 113788}, + {113792, 113800}, {113808, 113817}, {119808, 119892}, {119894, 119964}, + {119966, 119967}, {119970, 119970}, {119973, 119974}, {119977, 119980}, + {119982, 119993}, {119995, 119995}, {119997, 120003}, {120005, 120069}, + {120071, 120074}, {120077, 120084}, {120086, 120092}, {120094, 120121}, + {120123, 120126}, {120128, 120132}, {120134, 120134}, {120138, 120144}, + {120146, 120485}, {120488, 120512}, {120514, 120538}, {120540, 120570}, + {120572, 120596}, {120598, 120628}, {120630, 120654}, {120656, 120686}, + {120688, 120712}, {120714, 120744}, {120746, 120770}, {120772, 120779}, + {122624, 122633}, {122634, 122634}, {122635, 122654}, {122661, 122666}, + {122928, 122989}, {123136, 123180}, {123191, 123197}, {123214, 123214}, + {123536, 123565}, {123584, 123627}, {124112, 124138}, {124139, 124139}, + {124368, 124397}, {124400, 124400}, {124896, 124902}, {124904, 124907}, + {124909, 124910}, {124912, 124926}, {124928, 125124}, {125184, 125251}, + {125259, 125259}, {126464, 126467}, {126469, 126495}, {126497, 126498}, + {126500, 126500}, {126503, 126503}, {126505, 126514}, {126516, 126519}, + {126521, 126521}, {126523, 126523}, {126530, 126530}, {126535, 126535}, + {126537, 126537}, {126539, 126539}, {126541, 126543}, {126545, 126546}, + {126548, 126548}, {126551, 126551}, {126553, 126553}, {126555, 126555}, + {126557, 126557}, {126559, 126559}, {126561, 126562}, {126564, 126564}, + {126567, 126570}, {126572, 126578}, {126580, 126583}, {126585, 126588}, + {126590, 126590}, {126592, 126601}, {126603, 126619}, {126625, 126627}, + {126629, 126633}, {126635, 126651}, {131072, 173791}, {173824, 177977}, + {177984, 178205}, {178208, 183969}, {183984, 191456}, {191472, 192093}, + {194560, 195101}, {196608, 201546}, {201552, 205743} +}; + + +} // namespace ada::idna +#endif // ADA_IDNA_IDENTIFIER_TABLES_H +/* end file src/id_tables.cpp */ + +namespace ada::idna { +constexpr bool is_ascii_letter(char32_t c) noexcept { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +constexpr bool is_ascii_letter_or_digit(char32_t c) noexcept { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9'); +} + +bool valid_name_code_point(char32_t code_point, bool first) { + // https://tc39.es/ecma262/#prod-IdentifierStart + // Fast paths: + if (first && + (code_point == '$' || code_point == '_' || is_ascii_letter(code_point))) { + return true; + } + if (!first && (code_point == '$' || is_ascii_letter_or_digit(code_point))) { + return true; + } + // Slow path... + if (code_point == 0xffffffff) { + return false; // minimal error handling + } + if (first) { + auto iter = std::lower_bound( + std::begin(ada::idna::id_start), std::end(ada::idna::id_start), + code_point, + [](const uint32_t* range, uint32_t cp) { return range[1] < cp; }); + return iter != std::end(id_start) && code_point >= (*iter)[0]; + } else { + auto iter = std::lower_bound( + std::begin(id_continue), std::end(id_continue), code_point, + [](const uint32_t* range, uint32_t cp) { return range[1] < cp; }); + return iter != std::end(id_start) && code_point >= (*iter)[0]; + } +} +} // namespace ada::idna +/* end file src/identifier.cpp */ /* end file src/idna.cpp */ /* end file src/ada_idna.cpp */ ADA_POP_DISABLE_WARNINGS @@ -9792,8 +10447,12 @@ ADA_POP_DISABLE_WARNINGS #include #elif ADA_SSE2 #include +#elif ADA_LSX +#include #endif +#include + namespace ada::unicode { constexpr bool is_tabs_or_newline(char c) noexcept { @@ -9834,8 +10493,7 @@ ada_really_inline bool has_tabs_or_newline( std::string_view user_input) noexcept { // first check for short strings in which case we do it naively. if (user_input.size() < 16) { // slow path - return std::any_of(user_input.begin(), user_input.end(), - is_tabs_or_newline); + return std::ranges::any_of(user_input, is_tabs_or_newline); } // fast path for long strings (expected to be common) size_t i = 0; @@ -9873,8 +10531,7 @@ ada_really_inline bool has_tabs_or_newline( std::string_view user_input) noexcept { // first check for short strings in which case we do it naively. if (user_input.size() < 16) { // slow path - return std::any_of(user_input.begin(), user_input.end(), - is_tabs_or_newline); + return std::ranges::any_of(user_input, is_tabs_or_newline); } // fast path for long strings (expected to be common) size_t i = 0; @@ -9900,6 +10557,38 @@ ada_really_inline bool has_tabs_or_newline( } return _mm_movemask_epi8(running) != 0; } +#elif ADA_LSX +ada_really_inline bool has_tabs_or_newline( + std::string_view user_input) noexcept { + // first check for short strings in which case we do it naively. + if (user_input.size() < 16) { // slow path + return std::ranges::any_of(user_input, is_tabs_or_newline); + } + // fast path for long strings (expected to be common) + size_t i = 0; + const __m128i mask1 = __lsx_vrepli_b('\r'); + const __m128i mask2 = __lsx_vrepli_b('\n'); + const __m128i mask3 = __lsx_vrepli_b('\t'); + // If we supported SSSE3, we could use the algorithm that we use for NEON. + __m128i running{0}; + for (; i + 15 < user_input.size(); i += 16) { + __m128i word = __lsx_vld((const __m128i*)(user_input.data() + i), 0); + running = __lsx_vor_v( + __lsx_vor_v(running, __lsx_vor_v(__lsx_vseq_b(word, mask1), + __lsx_vseq_b(word, mask2))), + __lsx_vseq_b(word, mask3)); + } + if (i < user_input.size()) { + __m128i word = __lsx_vld( + (const __m128i*)(user_input.data() + user_input.length() - 16), 0); + running = __lsx_vor_v( + __lsx_vor_v(running, __lsx_vor_v(__lsx_vseq_b(word, mask1), + __lsx_vseq_b(word, mask2))), + __lsx_vseq_b(word, mask3)); + } + if (__lsx_bz_v(running)) return false; + return true; +} #else ada_really_inline bool has_tabs_or_newline( std::string_view user_input) noexcept { @@ -9936,7 +10625,7 @@ ada_really_inline bool has_tabs_or_newline( // U+003F (?), U+0040 (@), U+005B ([), U+005C (\), U+005D (]), U+005E (^), or // U+007C (|). constexpr static std::array is_forbidden_host_code_point_table = - []() constexpr { + []() consteval { std::array result{}; for (uint8_t c : {'\0', '\x09', '\x0a', '\x0d', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|'}) { @@ -9951,7 +10640,7 @@ ada_really_inline constexpr bool is_forbidden_host_code_point( } constexpr static std::array is_forbidden_domain_code_point_table = - []() constexpr { + []() consteval { std::array result{}; for (uint8_t c : {'\0', '\x09', '\x0a', '\x0d', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|', '%'}) { @@ -9990,7 +10679,7 @@ ada_really_inline constexpr bool contains_forbidden_domain_code_point( } constexpr static std::array - is_forbidden_domain_code_point_table_or_upper = []() constexpr { + is_forbidden_domain_code_point_table_or_upper = []() consteval { std::array result{}; for (uint8_t c : {'\0', '\x09', '\x0a', '\x0d', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|', '%'}) { @@ -10031,7 +10720,7 @@ contains_forbidden_domain_code_point_or_upper(const char* input, } // std::isalnum(c) || c == '+' || c == '-' || c == '.') is true for -constexpr static std::array is_alnum_plus_table = []() constexpr { +constexpr static std::array is_alnum_plus_table = []() consteval { std::array result{}; for (size_t c = 0; c < 256; c++) { result[c] = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || @@ -10052,6 +10741,17 @@ ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept { (c >= 'a' && c <= 'f'); } +ada_really_inline constexpr bool is_ascii_digit(const char c) noexcept { + // An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9), + // inclusive. + return (c >= '0' && c <= '9'); +} + +ada_really_inline constexpr bool is_ascii(const char32_t c) noexcept { + // If code point is between U+0000 and U+007F inclusive, then return true. + return c <= 0x7F; +} + ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept { return (unsigned char)c <= ' '; } @@ -10064,7 +10764,7 @@ ada_really_inline constexpr bool is_ascii_tab_or_newline( constexpr std::string_view table_is_double_dot_path_segment[] = { "..", "%2e.", ".%2e", "%2e%2e"}; -ada_really_inline ada_constexpr bool is_double_dot_path_segment( +ada_really_inline constexpr bool is_double_dot_path_segment( std::string_view input) noexcept { // This will catch most cases: // The length must be 2,4 or 6. @@ -10152,7 +10852,6 @@ std::string percent_decode(const std::string_view input, size_t first_percent) { !is_ascii_hex_digit(pointer[2])))) { dest += ch; pointer++; - continue; } else { unsigned a = convert_hex_to_binary(pointer[1]); unsigned b = convert_hex_to_binary(pointer[2]); @@ -10166,10 +10865,9 @@ std::string percent_decode(const std::string_view input, size_t first_percent) { std::string percent_encode(const std::string_view input, const uint8_t character_set[]) { - auto pointer = - std::find_if(input.begin(), input.end(), [character_set](const char c) { - return character_sets::bit_at(character_set, c); - }); + auto pointer = std::ranges::find_if(input, [character_set](const char c) { + return character_sets::bit_at(character_set, c); + }); // Optimization: Don't iterate if percent encode is not required if (pointer == input.end()) { return std::string(input); @@ -10196,10 +10894,9 @@ bool percent_encode(const std::string_view input, const uint8_t character_set[], std::string& out) { ada_log("percent_encode ", input, " to output string while ", append ? "appending" : "overwriting"); - auto pointer = - std::find_if(input.begin(), input.end(), [character_set](const char c) { - return character_sets::bit_at(character_set, c); - }); + auto pointer = std::ranges::find_if(input, [character_set](const char c) { + return character_sets::bit_at(character_set, c); + }); ada_log("percent_encode done checking, moved to ", std::distance(input.begin(), pointer)); @@ -10208,11 +10905,12 @@ bool percent_encode(const std::string_view input, const uint8_t character_set[], ada_log("percent_encode encoding not needed."); return false; } - if (!append) { + if constexpr (!append) { out.clear(); } ada_log("percent_encode appending ", std::distance(input.begin(), pointer), " bytes"); + // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) out.append(input.data(), std::distance(input.begin(), pointer)); ada_log("percent_encode processing ", std::distance(pointer, input.end()), " bytes"); @@ -10247,6 +10945,7 @@ bool to_ascii(std::optional& out, const std::string_view plain, std::string percent_encode(const std::string_view input, const uint8_t character_set[], size_t index) { std::string out; + // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) out.append(input.data(), index); auto pointer = input.begin() + index; for (; pointer != input.end(); pointer++) { @@ -10262,8 +10961,8 @@ std::string percent_encode(const std::string_view input, } // namespace ada::unicode /* end file src/unicode.cpp */ /* begin file src/serializers.cpp */ - #include +#include #include namespace ada::serializers { @@ -10343,18 +11042,19 @@ std::string ipv4(const uint64_t address) noexcept { } // namespace ada::serializers /* end file src/serializers.cpp */ /* begin file src/implementation.cpp */ + #include namespace ada { template -ada_warn_unused tl::expected parse( +ada_warn_unused tl::expected parse( std::string_view input, const result_type* base_url) { result_type u = ada::parser::parse_url_impl(input, base_url); if (!u.is_valid) { - return tl::unexpected(errors::generic_error); + return tl::unexpected(errors::type_error); } return u; } @@ -10406,7 +11106,7 @@ bool can_parse(std::string_view input, const std::string_view* base_input) { return result.is_valid; } -ada_warn_unused std::string to_string(ada::encoding_type type) { +ada_warn_unused std::string_view to_string(ada::encoding_type type) { switch (type) { case ada::encoding_type::UTF8: return "UTF-8"; @@ -10423,8 +11123,6 @@ ada_warn_unused std::string to_string(ada::encoding_type type) { /* end file src/implementation.cpp */ /* begin file src/helpers.cpp */ -#include -#include #include #include @@ -10518,13 +11216,11 @@ ada_really_inline std::optional prune_hash( ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) noexcept { - size_t first_delimiter = path.find_first_of('/', 1); - // Let path be url's path. // If url's scheme is "file", path's size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos && !path.empty()) { + path.find('/', 1) == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( helpers::substring(path, 1))) { return false; @@ -10543,13 +11239,11 @@ ada_really_inline bool shorten_path(std::string& path, ada_really_inline bool shorten_path(std::string_view& path, ada::scheme::type type) noexcept { - size_t first_delimiter = path.find_first_of('/', 1); - // Let path be url's path. // If url's scheme is "file", path's size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos && !path.empty()) { + path.find('/', 1) == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( helpers::substring(path, 1))) { return false; @@ -10572,15 +11266,11 @@ ada_really_inline void remove_ascii_tab_or_newline( std::string& input) noexcept { // if this ever becomes a performance issue, we could use an approach similar // to has_tabs_or_newline - input.erase(std::remove_if(input.begin(), input.end(), - [](char c) { - return ada::unicode::is_ascii_tab_or_newline(c); - }), - input.end()); + std::erase_if(input, ada::unicode::is_ascii_tab_or_newline); } -ada_really_inline std::string_view substring(std::string_view input, - size_t pos) noexcept { +ada_really_inline constexpr std::string_view substring(std::string_view input, + size_t pos) noexcept { ADA_ASSERT_TRUE(pos <= input.size()); // The following is safer but unneeded if we have the above line: // return pos > input.size() ? std::string_view() : input.substr(pos); @@ -10734,10 +11424,62 @@ ada_really_inline size_t find_next_host_delimiter_special( } return size_t(view.length()); } +#elif ADA_LSX +ada_really_inline size_t find_next_host_delimiter_special( + std::string_view view, size_t location) noexcept { + // first check for short strings in which case we do it naively. + if (view.size() - location < 16) { // slow path + for (size_t i = location; i < view.size(); i++) { + if (view[i] == ':' || view[i] == '/' || view[i] == '\\' || + view[i] == '?' || view[i] == '[') { + return i; + } + } + return size_t(view.size()); + } + // fast path for long strings (expected to be common) + size_t i = location; + const __m128i mask1 = __lsx_vrepli_b(':'); + const __m128i mask2 = __lsx_vrepli_b('/'); + const __m128i mask3 = __lsx_vrepli_b('\\'); + const __m128i mask4 = __lsx_vrepli_b('?'); + const __m128i mask5 = __lsx_vrepli_b('['); + + for (; i + 15 < view.size(); i += 16) { + __m128i word = __lsx_vld((const __m128i*)(view.data() + i), 0); + __m128i m1 = __lsx_vseq_b(word, mask1); + __m128i m2 = __lsx_vseq_b(word, mask2); + __m128i m3 = __lsx_vseq_b(word, mask3); + __m128i m4 = __lsx_vseq_b(word, mask4); + __m128i m5 = __lsx_vseq_b(word, mask5); + __m128i m = + __lsx_vor_v(__lsx_vor_v(__lsx_vor_v(m1, m2), __lsx_vor_v(m3, m4)), m5); + int mask = __lsx_vpickve2gr_hu(__lsx_vmsknz_b(m), 0); + if (mask != 0) { + return i + trailing_zeroes(mask); + } + } + if (i < view.size()) { + __m128i word = + __lsx_vld((const __m128i*)(view.data() + view.length() - 16), 0); + __m128i m1 = __lsx_vseq_b(word, mask1); + __m128i m2 = __lsx_vseq_b(word, mask2); + __m128i m3 = __lsx_vseq_b(word, mask3); + __m128i m4 = __lsx_vseq_b(word, mask4); + __m128i m5 = __lsx_vseq_b(word, mask5); + __m128i m = + __lsx_vor_v(__lsx_vor_v(__lsx_vor_v(m1, m2), __lsx_vor_v(m3, m4)), m5); + int mask = __lsx_vpickve2gr_hu(__lsx_vmsknz_b(m), 0); + if (mask != 0) { + return view.length() - 16 + trailing_zeroes(mask); + } + } + return size_t(view.length()); +} #else // : / [ \\ ? static constexpr std::array special_host_delimiters = - []() constexpr { + []() consteval { std::array result{}; for (int i : {':', '/', '[', '\\', '?'}) { result[i] = 1; @@ -10867,9 +11609,56 @@ ada_really_inline size_t find_next_host_delimiter(std::string_view view, } return size_t(view.length()); } +#elif ADA_LSX +ada_really_inline size_t find_next_host_delimiter(std::string_view view, + size_t location) noexcept { + // first check for short strings in which case we do it naively. + if (view.size() - location < 16) { // slow path + for (size_t i = location; i < view.size(); i++) { + if (view[i] == ':' || view[i] == '/' || view[i] == '?' || + view[i] == '[') { + return i; + } + } + return size_t(view.size()); + } + // fast path for long strings (expected to be common) + size_t i = location; + const __m128i mask1 = __lsx_vrepli_b(':'); + const __m128i mask2 = __lsx_vrepli_b('/'); + const __m128i mask4 = __lsx_vrepli_b('?'); + const __m128i mask5 = __lsx_vrepli_b('['); + + for (; i + 15 < view.size(); i += 16) { + __m128i word = __lsx_vld((const __m128i*)(view.data() + i), 0); + __m128i m1 = __lsx_vseq_b(word, mask1); + __m128i m2 = __lsx_vseq_b(word, mask2); + __m128i m4 = __lsx_vseq_b(word, mask4); + __m128i m5 = __lsx_vseq_b(word, mask5); + __m128i m = __lsx_vor_v(__lsx_vor_v(m1, m2), __lsx_vor_v(m4, m5)); + int mask = __lsx_vpickve2gr_hu(__lsx_vmsknz_b(m), 0); + if (mask != 0) { + return i + trailing_zeroes(mask); + } + } + if (i < view.size()) { + __m128i word = + __lsx_vld((const __m128i*)(view.data() + view.length() - 16), 0); + __m128i m1 = __lsx_vseq_b(word, mask1); + __m128i m2 = __lsx_vseq_b(word, mask2); + __m128i m4 = __lsx_vseq_b(word, mask4); + __m128i m5 = __lsx_vseq_b(word, mask5); + __m128i m = __lsx_vor_v(__lsx_vor_v(m1, m2), __lsx_vor_v(m4, m5)); + int mask = __lsx_vpickve2gr_hu(__lsx_vmsknz_b(m), 0); + if (mask != 0) { + return view.length() - 16 + trailing_zeroes(mask); + } + } + return size_t(view.length()); +} #else // : / [ ? -static constexpr std::array host_delimiters = []() constexpr { +static constexpr std::array host_delimiters = []() consteval { std::array result{}; for (int i : {':', '/', '?', '['}) { result[i] = 1; @@ -10969,7 +11758,7 @@ ada_really_inline std::pair get_host_delimiter_location( return {location, found_colon}; } -ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept { +void trim_c0_whitespace(std::string_view& input) noexcept { while (!input.empty() && ada::unicode::is_c0_control_or_space(input.front())) { input.remove_prefix(1); @@ -11009,15 +11798,20 @@ ada_really_inline void parse_prepared_path(std::string_view input, // Note: input cannot be empty, it must at least contain one character ('.') // Note: we know that '\' is not present. if (input[0] != '.') { - size_t slashdot = input.find("/."); - if (slashdot == std::string_view::npos) { // common case - trivial_path = true; - } else { // uncommon - // only three cases matter: /./, /.. or a final / - trivial_path = - !(slashdot + 2 == input.size() || input[slashdot + 2] == '.' || - input[slashdot + 2] == '/'); + size_t slashdot = 0; + bool dot_is_file = true; + for (;;) { + slashdot = input.find("/.", slashdot); + if (slashdot == std::string_view::npos) { // common case + break; + } else { // uncommon + // only three cases matter: /./, /.. or a final / + slashdot += 2; + dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' || + input[slashdot] == '/'); + } } + trivial_path = dot_is_file; } } if (trivial_path) { @@ -11106,8 +11900,8 @@ ada_really_inline void parse_prepared_path(std::string_view input, ? path_buffer_tmp : path_view; if (unicode::is_double_dot_path_segment(path_buffer)) { - if ((helpers::shorten_path(path, type) || special) && - location == std::string_view::npos) { + helpers::shorten_path(path, type); + if (location == std::string_view::npos) { path += '/'; } } else if (unicode::is_single_dot_path_segment(path_buffer) && @@ -11164,7 +11958,7 @@ ada_really_inline void strip_trailing_spaces_from_opaque_path( // @ / \\ ? static constexpr std::array authority_delimiter_special = - []() constexpr { + []() consteval { std::array result{}; for (uint8_t i : {'@', '/', '\\', '?'}) { result[i] = 1; @@ -11185,7 +11979,7 @@ find_authority_delimiter_special(std::string_view view) noexcept { } // @ / ? -static constexpr std::array authority_delimiter = []() constexpr { +static constexpr std::array authority_delimiter = []() consteval { std::array result{}; for (uint8_t i : {'@', '/', '?'}) { result[i] = 1; @@ -11218,14 +12012,16 @@ ada_warn_unused std::string to_string(ada::state state) { #include #include +#include +#include #include +#include namespace ada { bool url::parse_opaque_host(std::string_view input) { ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]"); - if (std::any_of(input.begin(), input.end(), - ada::unicode::is_forbidden_host_code_point)) { + if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) { return is_valid = false; } @@ -11550,31 +12346,31 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { *http, https), in which case, we can go really fast. **/ if (is_input_special) { // fast path!!! - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is not a special scheme and buffer is a special scheme, // then return. if (is_special() != is_input_special) { - return true; + return false; } // If url includes credentials or has a non-null port, and buffer is // "file", then return. if ((has_credentials() || port.has_value()) && parsed_type == ada::scheme::type::FILE) { - return true; + return false; } // If url's scheme is "file" and its host is an empty host, then return. // An empty host is the empty string. if (type == ada::scheme::type::FILE && host.has_value() && host.value().empty()) { - return true; + return false; } } type = parsed_type; - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -11594,7 +12390,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { // bool is_ascii = unicode::to_lower_ascii(_buffer.data(), _buffer.size()); - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is a special scheme and buffer is not a special scheme, // then return. If url's scheme is not a special scheme and buffer is a // special scheme, then return. @@ -11618,7 +12414,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { set_scheme(std::move(_buffer)); - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -11727,18 +12523,14 @@ ada_really_inline void url::parse_path(std::string_view input) { path = "/"; } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) { helpers::parse_prepared_path(internal_input.substr(1), type, path); - return; } else { helpers::parse_prepared_path(internal_input, type, path); - return; } } else if (!internal_input.empty()) { if (internal_input[0] == '/') { helpers::parse_prepared_path(internal_input.substr(1), type, path); - return; } else { helpers::parse_prepared_path(internal_input, type, path); - return; } } else { if (!host.has_value()) { @@ -11783,6 +12575,7 @@ ada_really_inline void url::parse_path(std::string_view input) { if (has_search()) { answer.append(",\n"); answer.append("\t\"query\":\""); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) helpers::encode_json(query.value(), back); answer.append("\""); } @@ -11803,17 +12596,6 @@ ada_really_inline void url::parse_path(std::string_view input) { return checkers::verify_dns_length(host.value()); } -} // namespace ada -/* end file src/url.cpp */ -/* begin file src/url-getters.cpp */ -/** - * @file url-getters.cpp - * Includes all the getters of `ada::url` - */ - -#include - -namespace ada { [[nodiscard]] std::string url::get_origin() const noexcept { if (is_special()) { // Return a new opaque origin. @@ -11866,10 +12648,6 @@ namespace ada { return host.value_or(""); } -[[nodiscard]] std::string_view url::get_pathname() const noexcept { - return path; -} - [[nodiscard]] std::string url::get_search() const noexcept { // If this's URL's query is either null or the empty string, then return the // empty string. Return U+003F (?), followed by this's URL's query. @@ -11896,19 +12674,6 @@ namespace ada { : "#" + hash.value(); } -} // namespace ada -/* end file src/url-getters.cpp */ -/* begin file src/url-setters.cpp */ -/** - * @file url-setters.cpp - * Includes all the setters of `ada::url` - */ - -#include -#include - -namespace ada { - template bool url::set_host_or_hostname(const std::string_view input) { if (has_opaque_path) { @@ -11936,35 +12701,67 @@ bool url::set_host_or_hostname(const std::string_view input) { // Note: the 'found_colon' value is true if and only if a colon was // encountered while not inside brackets. if (found_colon) { - if (override_hostname) { + // If buffer is the empty string, host-missing validation error, return + // failure. + std::string_view buffer = host_view.substr(0, location); + if (buffer.empty()) { + return false; + } + + // If state override is given and state override is hostname state, then + // return failure. + if constexpr (override_hostname) { return false; } - std::string_view buffer = new_host.substr(location + 1); - if (!buffer.empty()) { - set_port(buffer); + + // Let host be the result of host parsing buffer with url is not special. + bool succeeded = parse_host(buffer); + if (!succeeded) { + host = std::move(previous_host); + update_base_port(previous_port); + return false; } - } - // If url is special and host_view is the empty string, validation error, - // return failure. Otherwise, if state override is given, host_view is the - // empty string, and either url includes credentials or url's port is - // non-null, return. - else if (host_view.empty() && - (is_special() || has_credentials() || port.has_value())) { - return false; - } - // Let host be the result of host parsing host_view with url is not special. - if (host_view.empty() && !is_special()) { - host = ""; + // Set url's host to host, buffer to the empty string, and state to port + // state. + std::string_view port_buffer = new_host.substr(location + 1); + if (!port_buffer.empty()) { + set_port(port_buffer); + } return true; } + // Otherwise, if one of the following is true: + // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) + // - url is special and c is U+005C (\) + else { + // If url is special and host_view is the empty string, host-missing + // validation error, return failure. + if (host_view.empty() && is_special()) { + return false; + } - bool succeeded = parse_host(host_view); - if (!succeeded) { - host = previous_host; - update_base_port(previous_port); + // Otherwise, if state override is given, host_view is the empty string, + // and either url includes credentials or url's port is non-null, then + // return failure. + if (host_view.empty() && (has_credentials() || port.has_value())) { + return false; + } + + // Let host be the result of host parsing host_view with url is not + // special. + if (host_view.empty() && !is_special()) { + host = ""; + return true; + } + + bool succeeded = parse_host(host_view); + if (!succeeded) { + host = std::move(previous_host); + update_base_port(previous_port); + return false; + } + return true; } - return succeeded; } size_t location = new_host.find_first_of("/\\?"); @@ -11978,13 +12775,13 @@ bool url::set_host_or_hostname(const std::string_view input) { } else { // Let host be the result of host parsing buffer with url is not special. if (!parse_host(new_host)) { - host = previous_host; + host = std::move(previous_host); update_base_port(previous_port); return false; } // If host is "localhost", then set host to the empty string. - if (host.has_value() && host.value() == "localhost") { + if (host == "localhost") { host = ""; } } @@ -12021,28 +12818,37 @@ bool url::set_port(const std::string_view input) { if (cannot_have_credentials_or_port()) { return false; } + + if (input.empty()) { + port = std::nullopt; + return true; + } + std::string trimmed(input); helpers::remove_ascii_tab_or_newline(trimmed); + if (trimmed.empty()) { - port = std::nullopt; return true; } - // Input should not start with control characters. - if (ada::unicode::is_c0_control_or_space(trimmed.front())) { - return false; - } - // Input should contain at least one ascii digit. - if (input.find_first_of("0123456789") == std::string_view::npos) { + + // Input should not start with a non-digit character. + if (!ada::unicode::is_ascii_digit(trimmed.front())) { return false; } + // Find the first non-digit character to determine the length of digits + auto first_non_digit = + std::ranges::find_if_not(trimmed, ada::unicode::is_ascii_digit); + std::string_view digits_to_parse = + std::string_view(trimmed.data(), first_non_digit - trimmed.begin()); + // Revert changes if parse_port fails. std::optional previous_port = port; - parse_port(trimmed); + parse_port(digits_to_parse); if (is_valid) { return true; } - port = previous_port; + port = std::move(previous_port); is_valid = true; return false; } @@ -12076,15 +12882,14 @@ void url::set_search(const std::string_view input) { is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE : ada::character_sets::QUERY_PERCENT_ENCODE; - query = ada::unicode::percent_encode(std::string_view(new_value), - query_percent_encode_set); + query = ada::unicode::percent_encode(new_value, query_percent_encode_set); } bool url::set_pathname(const std::string_view input) { if (has_opaque_path) { return false; } - path = ""; + path.clear(); parse_path(input); return true; } @@ -12104,7 +12909,7 @@ bool url::set_protocol(const std::string_view input) { view.append(":"); std::string::iterator pointer = - std::find_if_not(view.begin(), view.end(), unicode::is_alnum_plus); + std::ranges::find_if_not(view, unicode::is_alnum_plus); if (pointer != view.end() && *pointer == ':') { return parse_scheme( @@ -12117,26 +12922,18 @@ bool url::set_href(const std::string_view input) { ada::result out = ada::parse(input); if (out) { - username = out->username; - password = out->password; - host = out->host; - port = out->port; - path = out->path; - query = out->query; - hash = out->hash; - type = out->type; - non_special_scheme = out->non_special_scheme; - has_opaque_path = out->has_opaque_path; + *this = *out; } return out.has_value(); } } // namespace ada -/* end file src/url-setters.cpp */ +/* end file src/url.cpp */ /* begin file src/parser.cpp */ #include +#include namespace ada::parser { @@ -12149,10 +12946,9 @@ result_type parse_url_impl(std::string_view user_input, // means that doing if constexpr(result_type_is_ada_url) { something } else { // something else } is free (at runtime). This means that ada::url_aggregator // and ada::url **do not have to support the exact same API**. - constexpr bool result_type_is_ada_url = - std::is_same::value; + constexpr bool result_type_is_ada_url = std::is_same_v; constexpr bool result_type_is_ada_url_aggregator = - std::is_same::value; + std::is_same_v; static_assert(result_type_is_ada_url || result_type_is_ada_url_aggregator); // We don't support // anything else for now. @@ -12161,12 +12957,12 @@ result_type parse_url_impl(std::string_view user_input, " bytes],", (base_url != nullptr ? base_url->to_string() : "null"), ")"); - ada::state state = ada::state::SCHEME_START; + state state = state::SCHEME_START; result_type url{}; // We refuse to parse URL strings that exceed 4GB. Such strings are almost // surely the result of a bug or are otherwise a security concern. - if (user_input.size() > std::numeric_limits::max()) { + if (user_input.size() > std::numeric_limits::max()) [[unlikely]] { url.is_valid = false; } // Going forward, user_input.size() is in [0, @@ -12197,20 +12993,19 @@ result_type parse_url_impl(std::string_view user_input, url.reserve(reserve_capacity); } std::string tmp_buffer; - std::string_view internal_input; - if (unicode::has_tabs_or_newline(user_input)) { + std::string_view url_data; + if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] { tmp_buffer = user_input; // Optimization opportunity: Instead of copying and then pruning, we could // just directly build the string from user_input. helpers::remove_ascii_tab_or_newline(tmp_buffer); - internal_input = tmp_buffer; - } else { - internal_input = user_input; + url_data = tmp_buffer; + } else [[likely]] { + url_data = user_input; } // Leading and trailing control characters are uncommon and easy to deal with // (no performance concern). - std::string_view url_data = internal_input; helpers::trim_c0_whitespace(url_data); // Optimization opportunity. Most websites do not have fragment. @@ -12233,27 +13028,27 @@ result_type parse_url_impl(std::string_view user_input, ada_log("In parsing at ", input_position, " out of ", input_size, " in state ", ada::to_string(state)); switch (state) { - case ada::state::SCHEME_START: { + case state::SCHEME_START: { ada_log("SCHEME_START ", helpers::substring(url_data, input_position)); // If c is an ASCII alpha, append c, lowercased, to buffer, and set // state to scheme state. if ((input_position != input_size) && checkers::is_alpha(url_data[input_position])) { - state = ada::state::SCHEME; + state = state::SCHEME; input_position++; } else { // Otherwise, if state override is not given, set state to no scheme // state and decrease pointer by 1. - state = ada::state::NO_SCHEME; + state = state::NO_SCHEME; } break; } - case ada::state::SCHEME: { + case state::SCHEME: { ada_log("SCHEME ", helpers::substring(url_data, input_position)); // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.), // append c, lowercased, to buffer. while ((input_position != input_size) && - (ada::unicode::is_alnum_plus(url_data[input_position]))) { + (unicode::is_alnum_plus(url_data[input_position]))) { input_position++; } // Otherwise, if c is U+003A (:), then: @@ -12275,9 +13070,9 @@ result_type parse_url_impl(std::string_view user_input, ada_log("SCHEME the scheme is ", url.get_protocol()); // If url's scheme is "file", then: - if (url.type == ada::scheme::type::FILE) { + if (url.type == scheme::type::FILE) { // Set state to file state. - state = ada::state::FILE; + state = state::FILE; } // Otherwise, if url is special, base is non-null, and base's scheme // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url @@ -12285,38 +13080,38 @@ result_type parse_url_impl(std::string_view user_input, else if (url.is_special() && base_url != nullptr && base_url->type == url.type) { // Set state to special relative or authority state. - state = ada::state::SPECIAL_RELATIVE_OR_AUTHORITY; + state = state::SPECIAL_RELATIVE_OR_AUTHORITY; } // Otherwise, if url is special, set state to special authority // slashes state. else if (url.is_special()) { - state = ada::state::SPECIAL_AUTHORITY_SLASHES; + state = state::SPECIAL_AUTHORITY_SLASHES; } // Otherwise, if remaining starts with an U+002F (/), set state to // path or authority state and increase pointer by 1. else if (input_position + 1 < input_size && url_data[input_position + 1] == '/') { - state = ada::state::PATH_OR_AUTHORITY; + state = state::PATH_OR_AUTHORITY; input_position++; } // Otherwise, set url's path to the empty string and set state to // opaque path state. else { - state = ada::state::OPAQUE_PATH; + state = state::OPAQUE_PATH; } } // Otherwise, if state override is not given, set buffer to the empty // string, state to no scheme state, and start over (from the first code // point in input). else { - state = ada::state::NO_SCHEME; + state = state::NO_SCHEME; input_position = 0; break; } input_position++; break; } - case ada::state::NO_SCHEME: { + case state::NO_SCHEME: { ada_log("NO_SCHEME ", helpers::substring(url_data, input_position)); // If base is null, or base has an opaque path and c is not U+0023 (#), // validation error, return failure. @@ -12347,18 +13142,18 @@ result_type parse_url_impl(std::string_view user_input, } // Otherwise, if base's scheme is not "file", set state to relative // state and decrease pointer by 1. - else if (base_url->type != ada::scheme::type::FILE) { + else if (base_url->type != scheme::type::FILE) { ada_log("NO_SCHEME non-file relative path"); - state = ada::state::RELATIVE_SCHEME; + state = state::RELATIVE_SCHEME; } // Otherwise, set state to file state and decrease pointer by 1. else { ada_log("NO_SCHEME file base type"); - state = ada::state::FILE; + state = state::FILE; } break; } - case ada::state::AUTHORITY: { + case state::AUTHORITY: { ada_log("AUTHORITY ", helpers::substring(url_data, input_position)); // most URLs have no @. Having no @ tells us that we don't have to worry // about AUTHORITY. Of course, we could have @ and still not have to @@ -12368,11 +13163,10 @@ result_type parse_url_impl(std::string_view user_input, // TODO: We could do various processing early on, using a single pass // over the string to collect information about it, e.g., telling us // whether there is a @ and if so, where (or how many). - const bool contains_ampersand = - (url_data.find('@', input_position) != std::string_view::npos); - if (!contains_ampersand) { - state = ada::state::HOST; + // Check if url data contains an @. + if (url_data.find('@', input_position) == std::string_view::npos) { + state = state::HOST; break; } bool at_sign_seen{false}; @@ -12383,12 +13177,12 @@ result_type parse_url_impl(std::string_view user_input, * --------^ */ do { - std::string_view view = helpers::substring(url_data, input_position); + std::string_view view = url_data.substr(input_position); // The delimiters are @, /, ? \\. size_t location = url.is_special() ? helpers::find_authority_delimiter_special(view) : helpers::find_authority_delimiter(view); - std::string_view authority_view(view.data(), location); + std::string_view authority_view = view.substr(0, location); size_t end_of_authority = input_position + authority_view.size(); // If c is U+0040 (@), then: if ((end_of_authority != input_size) && @@ -12469,7 +13263,7 @@ result_type parse_url_impl(std::string_view user_input, url.is_valid = false; return url; } - state = ada::state::HOST; + state = state::HOST; break; } if (end_of_authority == input_size) { @@ -12485,42 +13279,41 @@ result_type parse_url_impl(std::string_view user_input, break; } - case ada::state::SPECIAL_RELATIVE_OR_AUTHORITY: { + case state::SPECIAL_RELATIVE_OR_AUTHORITY: { ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ", helpers::substring(url_data, input_position)); // If c is U+002F (/) and remaining starts with U+002F (/), // then set state to special authority ignore slashes state and increase // pointer by 1. - std::string_view view = helpers::substring(url_data, input_position); - if (ada::checkers::begins_with(view, "//")) { - state = ada::state::SPECIAL_AUTHORITY_IGNORE_SLASHES; + if (url_data.substr(input_position, 2) == "//") { + state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES; input_position += 2; } else { // Otherwise, validation error, set state to relative state and // decrease pointer by 1. - state = ada::state::RELATIVE_SCHEME; + state = state::RELATIVE_SCHEME; } break; } - case ada::state::PATH_OR_AUTHORITY: { + case state::PATH_OR_AUTHORITY: { ada_log("PATH_OR_AUTHORITY ", helpers::substring(url_data, input_position)); // If c is U+002F (/), then set state to authority state. if ((input_position != input_size) && (url_data[input_position] == '/')) { - state = ada::state::AUTHORITY; + state = state::AUTHORITY; input_position++; } else { // Otherwise, set state to path state, and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; } break; } - case ada::state::RELATIVE_SCHEME: { + case state::RELATIVE_SCHEME: { ada_log("RELATIVE_SCHEME ", helpers::substring(url_data, input_position)); @@ -12529,11 +13322,12 @@ result_type parse_url_impl(std::string_view user_input, // If c is U+002F (/), then set state to relative slash state. if ((input_position != input_size) && + // NOLINTNEXTLINE(bugprone-branch-clone) (url_data[input_position] == '/')) { ada_log( "RELATIVE_SCHEME if c is U+002F (/), then set state to relative " "slash state"); - state = ada::state::RELATIVE_SLASH; + state = state::RELATIVE_SLASH; } else if (url.is_special() && (input_position != input_size) && (url_data[input_position] == '\\')) { // Otherwise, if url is special and c is U+005C (\), validation error, @@ -12541,7 +13335,7 @@ result_type parse_url_impl(std::string_view user_input, ada_log( "RELATIVE_SCHEME if url is special and c is U+005C, validation " "error, set state to relative slash state"); - state = ada::state::RELATIVE_SLASH; + state = state::RELATIVE_SLASH; } else { ada_log("RELATIVE_SCHEME otherwise"); // Set url's username to base's username, url's password to base's @@ -12560,9 +13354,7 @@ result_type parse_url_impl(std::string_view user_input, } else { url.update_base_authority(base_url->get_href(), base_url->get_components()); - // TODO: Get rid of set_hostname and replace it with - // update_base_hostname - url.set_hostname(base_url->get_hostname()); + url.update_host_to_base_host(base_url->get_hostname()); url.update_base_port(base_url->retrieve_base_port()); // cloning the base path includes cloning the has_opaque_path flag url.has_opaque_path = base_url->has_opaque_path; @@ -12576,7 +13368,7 @@ result_type parse_url_impl(std::string_view user_input, // state to query state. if ((input_position != input_size) && (url_data[input_position] == '?')) { - state = ada::state::QUERY; + state = state::QUERY; } // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { @@ -12588,18 +13380,18 @@ result_type parse_url_impl(std::string_view user_input, } else { std::string_view path = url.get_pathname(); if (helpers::shorten_path(path, url.type)) { - url.update_base_pathname(std::string(path)); + url.update_base_pathname(std::move(std::string(path))); } } // Set state to path state and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; break; } } input_position++; break; } - case ada::state::RELATIVE_SLASH: { + case state::RELATIVE_SLASH: { ada_log("RELATIVE_SLASH ", helpers::substring(url_data, input_position)); @@ -12608,12 +13400,12 @@ result_type parse_url_impl(std::string_view user_input, (url_data[input_position] == '/' || url_data[input_position] == '\\')) { // Set state to special authority ignore slashes state. - state = ada::state::SPECIAL_AUTHORITY_IGNORE_SLASHES; + state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES; } // Otherwise, if c is U+002F (/), then set state to authority state. else if ((input_position != input_size) && (url_data[input_position] == '/')) { - state = ada::state::AUTHORITY; + state = state::AUTHORITY; } // Otherwise, set // - url's username to base's username, @@ -12630,33 +13422,30 @@ result_type parse_url_impl(std::string_view user_input, } else { url.update_base_authority(base_url->get_href(), base_url->get_components()); - // TODO: Get rid of set_hostname and replace it with - // update_base_hostname - url.set_hostname(base_url->get_hostname()); + url.update_host_to_base_host(base_url->get_hostname()); url.update_base_port(base_url->retrieve_base_port()); } - state = ada::state::PATH; + state = state::PATH; break; } input_position++; break; } - case ada::state::SPECIAL_AUTHORITY_SLASHES: { + case state::SPECIAL_AUTHORITY_SLASHES: { ada_log("SPECIAL_AUTHORITY_SLASHES ", helpers::substring(url_data, input_position)); // If c is U+002F (/) and remaining starts with U+002F (/), // then set state to special authority ignore slashes state and increase // pointer by 1. - std::string_view view = helpers::substring(url_data, input_position); - if (ada::checkers::begins_with(view, "//")) { + if (url_data.substr(input_position, 2) == "//") { input_position += 2; } [[fallthrough]]; } - case ada::state::SPECIAL_AUTHORITY_IGNORE_SLASHES: { + case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: { ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ", helpers::substring(url_data, input_position)); @@ -12667,23 +13456,22 @@ result_type parse_url_impl(std::string_view user_input, (url_data[input_position] == '\\'))) { input_position++; } - state = ada::state::AUTHORITY; + state = state::AUTHORITY; break; } - case ada::state::QUERY: { + case state::QUERY: { ada_log("QUERY ", helpers::substring(url_data, input_position)); if constexpr (store_values) { // Let queryPercentEncodeSet be the special-query percent-encode set // if url is special; otherwise the query percent-encode set. const uint8_t* query_percent_encode_set = - url.is_special() - ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE - : ada::character_sets::QUERY_PERCENT_ENCODE; + url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE + : character_sets::QUERY_PERCENT_ENCODE; // Percent-encode after encoding, with encoding, buffer, and // queryPercentEncodeSet, and append the result to url's query. - url.update_base_search(helpers::substring(url_data, input_position), + url.update_base_search(url_data.substr(input_position), query_percent_encode_set); ada_log("QUERY update_base_search completed "); if (fragment.has_value()) { @@ -12692,11 +13480,10 @@ result_type parse_url_impl(std::string_view user_input, } return url; } - case ada::state::HOST: { + case state::HOST: { ada_log("HOST ", helpers::substring(url_data, input_position)); - std::string_view host_view = - helpers::substring(url_data, input_position); + std::string_view host_view = url_data.substr(input_position); auto [location, found_colon] = helpers::get_host_delimiter_location(url.is_special(), host_view); input_position = (location != std::string_view::npos) @@ -12716,7 +13503,7 @@ result_type parse_url_impl(std::string_view user_input, ada_log("HOST parsing results in ", url.get_hostname()); // Set url's host to host, buffer to the empty string, and state to // port state. - state = ada::state::PORT; + state = state::PORT; input_position++; } // Otherwise, if one of the following is true: @@ -12727,7 +13514,7 @@ result_type parse_url_impl(std::string_view user_input, else { // If url is special and host_view is the empty string, validation // error, return failure. - if (url.is_special() && host_view.empty()) { + if (host_view.empty() && url.is_special()) { url.is_valid = false; return url; } @@ -12743,50 +13530,56 @@ result_type parse_url_impl(std::string_view user_input, " href=", url.get_href()); // Set url's host to host, and state to path start state. - state = ada::state::PATH_START; + state = state::PATH_START; } break; } - case ada::state::OPAQUE_PATH: { + case state::OPAQUE_PATH: { ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position)); - std::string_view view = helpers::substring(url_data, input_position); + std::string_view view = url_data.substr(input_position); // If c is U+003F (?), then set url's query to the empty string and // state to query state. size_t location = view.find('?'); if (location != std::string_view::npos) { view.remove_suffix(view.size() - location); - state = ada::state::QUERY; + state = state::QUERY; input_position += location + 1; } else { input_position = input_size + 1; } url.has_opaque_path = true; + // This is a really unlikely scenario in real world. We should not seek // to optimize it. - url.update_base_pathname(unicode::percent_encode( - view, character_sets::C0_CONTROL_PERCENT_ENCODE)); + if (view.ends_with(' ')) { + std::string modified_view = + std::string(view.substr(0, view.size() - 1)) + "%20"; + url.update_base_pathname(unicode::percent_encode( + modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE)); + } else { + url.update_base_pathname(unicode::percent_encode( + view, character_sets::C0_CONTROL_PERCENT_ENCODE)); + } break; } - case ada::state::PORT: { + case state::PORT: { ada_log("PORT ", helpers::substring(url_data, input_position)); - std::string_view port_view = - helpers::substring(url_data, input_position); - size_t consumed_bytes = url.parse_port(port_view, true); - input_position += consumed_bytes; + std::string_view port_view = url_data.substr(input_position); + input_position += url.parse_port(port_view, true); if (!url.is_valid) { return url; } state = state::PATH_START; [[fallthrough]]; } - case ada::state::PATH_START: { + case state::PATH_START: { ada_log("PATH_START ", helpers::substring(url_data, input_position)); // If url is special, then: if (url.is_special()) { // Set state to path state. - state = ada::state::PATH; + state = state::PATH; // Optimization: Avoiding going into PATH state improves the // performance of urls ending with /. @@ -12811,12 +13604,12 @@ result_type parse_url_impl(std::string_view user_input, // set url's query to the empty string and state to query state. else if ((input_position != input_size) && (url_data[input_position] == '?')) { - state = ada::state::QUERY; + state = state::QUERY; } // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { // Set state to path state. - state = ada::state::PATH; + state = state::PATH; // If c is not U+002F (/), then decrease pointer by 1. if (url_data[input_position] != '/') { @@ -12827,15 +13620,15 @@ result_type parse_url_impl(std::string_view user_input, input_position++; break; } - case ada::state::PATH: { - std::string_view view = helpers::substring(url_data, input_position); + case state::PATH: { ada_log("PATH ", helpers::substring(url_data, input_position)); + std::string_view view = url_data.substr(input_position); // Most time, we do not need percent encoding. // Furthermore, we can immediately locate the '?'. size_t locofquestionmark = view.find('?'); if (locofquestionmark != std::string_view::npos) { - state = ada::state::QUERY; + state = state::QUERY; view.remove_suffix(view.size() - locofquestionmark); input_position += locofquestionmark + 1; } else { @@ -12851,7 +13644,7 @@ result_type parse_url_impl(std::string_view user_input, } break; } - case ada::state::FILE_SLASH: { + case state::FILE_SLASH: { ada_log("FILE_SLASH ", helpers::substring(url_data, input_position)); // If c is U+002F (/) or U+005C (\), then: @@ -12860,21 +13653,19 @@ result_type parse_url_impl(std::string_view user_input, url_data[input_position] == '\\')) { ada_log("FILE_SLASH c is U+002F or U+005C"); // Set state to file host state. - state = ada::state::FILE_HOST; + state = state::FILE_HOST; input_position++; } else { ada_log("FILE_SLASH otherwise"); // If base is non-null and base's scheme is "file", then: // Note: it is unsafe to do base_url->scheme unless you know that // base_url_has_value() is true. - if (base_url != nullptr && - base_url->type == ada::scheme::type::FILE) { + if (base_url != nullptr && base_url->type == scheme::type::FILE) { // Set url's host to base's host. if constexpr (result_type_is_ada_url) { url.host = base_url->host; } else { - // TODO: Optimization opportunity. - url.set_host(base_url->get_host()); + url.update_host_to_base_host(base_url->get_host()); } // If the code point substring from pointer to the end of input does // not start with a Windows drive letter and base's path[0] is a @@ -12882,7 +13673,7 @@ result_type parse_url_impl(std::string_view user_input, // url's path. if (!base_url->get_pathname().empty()) { if (!checkers::is_windows_drive_letter( - helpers::substring(url_data, input_position))) { + url_data.substr(input_position))) { std::string_view first_base_url_path = base_url->get_pathname().substr(1); size_t loc = first_base_url_path.find('/'); @@ -12904,14 +13695,14 @@ result_type parse_url_impl(std::string_view user_input, } // Set state to path state, and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; } break; } - case ada::state::FILE_HOST: { - std::string_view view = helpers::substring(url_data, input_position); + case state::FILE_HOST: { ada_log("FILE_HOST ", helpers::substring(url_data, input_position)); + std::string_view view = url_data.substr(input_position); size_t location = view.find_first_of("/\\?"); std::string_view file_host_buffer( @@ -12919,7 +13710,7 @@ result_type parse_url_impl(std::string_view user_input, (location != std::string_view::npos) ? location : view.size()); if (checkers::is_windows_drive_letter(file_host_buffer)) { - state = ada::state::PATH; + state = state::PATH; } else if (file_host_buffer.empty()) { // Set url's host to the empty string. if constexpr (result_type_is_ada_url) { @@ -12928,7 +13719,7 @@ result_type parse_url_impl(std::string_view user_input, url.update_base_hostname(""); } // Set state to path start state. - state = ada::state::PATH_START; + state = state::PATH_START; } else { size_t consumed_bytes = file_host_buffer.size(); input_position += consumed_bytes; @@ -12950,15 +13741,14 @@ result_type parse_url_impl(std::string_view user_input, } // Set buffer to the empty string and state to path start state. - state = ada::state::PATH_START; + state = state::PATH_START; } break; } - case ada::state::FILE: { + case state::FILE: { ada_log("FILE ", helpers::substring(url_data, input_position)); - std::string_view file_view = - helpers::substring(url_data, input_position); + std::string_view file_view = url_data.substr(input_position); url.set_protocol_as_file(); if constexpr (result_type_is_ada_url) { @@ -12973,11 +13763,10 @@ result_type parse_url_impl(std::string_view user_input, url_data[input_position] == '\\')) { ada_log("FILE c is U+002F or U+005C"); // Set state to file slash state. - state = ada::state::FILE_SLASH; + state = state::FILE_SLASH; } // Otherwise, if base is non-null and base's scheme is "file": - else if (base_url != nullptr && - base_url->type == ada::scheme::type::FILE) { + else if (base_url != nullptr && base_url->type == scheme::type::FILE) { // Set url's host to base's host, url's path to a clone of base's // path, and url's query to base's query. ada_log("FILE base non-null"); @@ -12986,9 +13775,7 @@ result_type parse_url_impl(std::string_view user_input, url.path = base_url->path; url.query = base_url->query; } else { - // TODO: Get rid of set_hostname and replace it with - // update_base_hostname - url.set_hostname(base_url->get_hostname()); + url.update_host_to_base_host(base_url->get_hostname()); url.update_base_pathname(base_url->get_pathname()); url.update_base_search(base_url->get_search()); } @@ -12997,7 +13784,7 @@ result_type parse_url_impl(std::string_view user_input, // If c is U+003F (?), then set url's query to the empty string and // state to query state. if (input_position != input_size && url_data[input_position] == '?') { - state = ada::state::QUERY; + state = state::QUERY; } // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { @@ -13011,7 +13798,7 @@ result_type parse_url_impl(std::string_view user_input, } else { std::string_view path = url.get_pathname(); if (helpers::shorten_path(path, url.type)) { - url.update_base_pathname(std::string(path)); + url.update_base_pathname(std::move(std::string(path))); } } } @@ -13023,14 +13810,14 @@ result_type parse_url_impl(std::string_view user_input, } // Set state to path state and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; break; } } // Otherwise, set state to path state, and decrease pointer by 1. else { ada_log("FILE go to path"); - state = ada::state::PATH; + state = state::PATH; break; } @@ -13038,7 +13825,7 @@ result_type parse_url_impl(std::string_view user_input, break; } default: - ada::unreachable(); + unreachable(); } } if constexpr (store_values) { @@ -13068,86 +13855,11 @@ template url_aggregator parse_url( /* end file src/parser.cpp */ /* begin file src/url_components.cpp */ -#include +#include #include namespace ada { -[[nodiscard]] bool url_components::check_offset_consistency() const noexcept { - /** - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - // These conditions can be made more strict. - uint32_t index = 0; - - if (protocol_end == url_components::omitted) { - return false; - } - if (protocol_end < index) { - return false; - } - index = protocol_end; - - if (username_end == url_components::omitted) { - return false; - } - if (username_end < index) { - return false; - } - index = username_end; - - if (host_start == url_components::omitted) { - return false; - } - if (host_start < index) { - return false; - } - index = host_start; - - if (port != url_components::omitted) { - if (port > 0xffff) { - return false; - } - uint32_t port_length = helpers::fast_digit_count(port) + 1; - if (index + port_length < index) { - return false; - } - index += port_length; - } - - if (pathname_start == url_components::omitted) { - return false; - } - if (pathname_start < index) { - return false; - } - index = pathname_start; - - if (search_start != url_components::omitted) { - if (search_start < index) { - return false; - } - index = search_start; - } - - if (hash_start != url_components::omitted) { - if (hash_start < index) { - return false; - } - } - - return true; -} - [[nodiscard]] std::string url_components::to_string() const { std::string answer; auto back = std::back_insert_iterator(answer); @@ -13193,6 +13905,8 @@ namespace ada { /* end file src/url_components.cpp */ /* begin file src/url_aggregator.cpp */ +#include +#include #include #include @@ -13206,38 +13920,38 @@ template std::string_view input{input_with_colon}; input.remove_suffix(1); auto parsed_type = ada::scheme::get_scheme_type(input); - bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL); + const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL); /** * In the common case, we will immediately recognize a special scheme (e.g., *http, https), in which case, we can go really fast. **/ if (is_input_special) { // fast path!!! - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is not a special scheme and buffer is a special scheme, // then return. if (is_special() != is_input_special) { - return true; + return false; } // If url includes credentials or has a non-null port, and buffer is // "file", then return. if ((has_credentials() || components.port != url_components::omitted) && parsed_type == ada::scheme::type::FILE) { - return true; + return false; } // If url's scheme is "file" and its host is an empty host, then return. // An empty host is the empty string. if (type == ada::scheme::type::FILE && components.host_start == components.host_end) { - return true; + return false; } } type = parsed_type; set_scheme_from_view_with_colon(input_with_colon); - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -13254,7 +13968,7 @@ template // need to check the return value. unicode::to_lower_ascii(_buffer.data(), _buffer.size()); - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is a special scheme and buffer is not a special scheme, // then return. If url's scheme is not a special scheme and buffer is a // special scheme, then return. @@ -13279,7 +13993,7 @@ template set_scheme(_buffer); - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -13408,11 +14122,11 @@ bool url_aggregator::set_protocol(const std::string_view input) { view.append(":"); std::string::iterator pointer = - std::find_if_not(view.begin(), view.end(), unicode::is_alnum_plus); + std::ranges::find_if_not(view, unicode::is_alnum_plus); if (pointer != view.end() && *pointer == ':') { return parse_scheme_with_colon( - std::string_view(view.data(), pointer - view.begin() + 1)); + view.substr(0, pointer - view.begin() + 1)); } return false; } @@ -13464,24 +14178,33 @@ bool url_aggregator::set_port(const std::string_view input) { if (cannot_have_credentials_or_port()) { return false; } + + if (input.empty()) { + clear_port(); + return true; + } + std::string trimmed(input); helpers::remove_ascii_tab_or_newline(trimmed); + if (trimmed.empty()) { - clear_port(); return true; } - // Input should not start with control characters. - if (ada::unicode::is_c0_control_or_space(trimmed.front())) { - return false; - } - // Input should contain at least one ascii digit. - if (input.find_first_of("0123456789") == std::string_view::npos) { + + // Input should not start with a non-digit character. + if (!ada::unicode::is_ascii_digit(trimmed.front())) { return false; } + // Find the first non-digit character to determine the length of digits + auto first_non_digit = + std::ranges::find_if_not(trimmed, ada::unicode::is_ascii_digit); + std::string_view digits_to_parse = + std::string_view(trimmed.data(), first_non_digit - trimmed.begin()); + // Revert changes if parse_port fails. uint32_t previous_port = components.port; - parse_port(trimmed); + parse_port(digits_to_parse); if (is_valid) { return true; } @@ -13500,8 +14223,7 @@ bool url_aggregator::set_pathname(const std::string_view input) { } clear_pathname(); parse_path(input); - if (checkers::begins_with(get_pathname(), "//") && !has_authority() && - !has_dash_dot()) { + if (get_pathname().starts_with("//") && !has_authority() && !has_dash_dot()) { buffer.insert(components.pathname_start, "/."); components.pathname_start += 2; } @@ -13678,8 +14400,8 @@ ada_really_inline bool url_aggregator::parse_host(std::string_view input) { ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(), " bytes]"); - if (std::any_of(host.value().begin(), host.value().end(), - ada::unicode::is_forbidden_domain_code_point)) { + if (std::ranges::any_of(host.value(), + ada::unicode::is_forbidden_domain_code_point)) { return is_valid = false; } @@ -13725,43 +14447,75 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // Note: the 'found_colon' value is true if and only if a colon was // encountered while not inside brackets. if (found_colon) { - if (override_hostname) { + // If buffer is the empty string, host-missing validation error, return + // failure. + std::string_view host_buffer = host_view.substr(0, location); + if (host_buffer.empty()) { return false; } - std::string_view sub_buffer = new_host.substr(location + 1); - if (!sub_buffer.empty()) { - set_port(sub_buffer); + + // If state override is given and state override is hostname state, then + // return failure. + if constexpr (override_hostname) { + return false; } + + // Let host be the result of host parsing buffer with url is not special. + bool succeeded = parse_host(host_buffer); + if (!succeeded) { + update_base_hostname(previous_host); + update_base_port(previous_port); + return false; + } + + // Set url's host to host, buffer to the empty string, and state to port + // state. + std::string_view port_buffer = new_host.substr(location + 1); + if (!port_buffer.empty()) { + set_port(port_buffer); + } + return true; } - // If url is special and host_view is the empty string, validation error, - // return failure. Otherwise, if state override is given, host_view is the - // empty string, and either url includes credentials or url's port is - // non-null, return. - else if (host_view.empty() && - (is_special() || has_credentials() || has_port())) { - return false; - } + // Otherwise, if one of the following is true: + // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) + // - url is special and c is U+005C (\) + else { + // If url is special and host_view is the empty string, host-missing + // validation error, return failure. + if (host_view.empty() && is_special()) { + return false; + } + + // Otherwise, if state override is given, host_view is the empty string, + // and either url includes credentials or url's port is non-null, then + // return failure. + if (host_view.empty() && (has_credentials() || has_port())) { + return false; + } + + // Let host be the result of host parsing host_view with url is not + // special. + if (host_view.empty() && !is_special()) { + if (has_hostname()) { + clear_hostname(); // easy! + } else if (has_dash_dot()) { + add_authority_slashes_if_needed(); + delete_dash_dot(); + } + return true; + } - // Let host be the result of host parsing host_view with url is not special. - if (host_view.empty() && !is_special()) { - if (has_hostname()) { - clear_hostname(); // easy! + bool succeeded = parse_host(host_view); + if (!succeeded) { + update_base_hostname(previous_host); + update_base_port(previous_port); + return false; } else if (has_dash_dot()) { - add_authority_slashes_if_needed(); + // Should remove dash_dot from pathname delete_dash_dot(); } return true; } - - bool succeeded = parse_host(host_view); - if (!succeeded) { - update_base_hostname(previous_host); - update_base_port(previous_port); - } else if (has_dash_dot()) { - // Should remove dash_dot from pathname - delete_dash_dot(); - } - return succeeded; } size_t location = new_host.find_first_of("/\\?"); @@ -13831,7 +14585,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return "null"; } -[[nodiscard]] std::string_view url_aggregator::get_username() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_username() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_username"); if (has_non_empty_username()) { return helpers::substring(buffer, components.protocol_end + 2, @@ -13840,7 +14595,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return ""; } -[[nodiscard]] std::string_view url_aggregator::get_password() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_password() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_password"); if (has_non_empty_password()) { return helpers::substring(buffer, components.username_end + 1, @@ -13849,7 +14605,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return ""; } -[[nodiscard]] std::string_view url_aggregator::get_port() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_port() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_port"); if (components.port == url_components::omitted) { return ""; @@ -13858,7 +14615,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { components.pathname_start); } -[[nodiscard]] std::string_view url_aggregator::get_hash() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_hash() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_hash"); // If this's URL's fragment is either null or the empty string, then return // the empty string. Return U+0023 (#), followed by this's URL's fragment. @@ -13871,7 +14629,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return helpers::substring(buffer, components.hash_start); } -[[nodiscard]] std::string_view url_aggregator::get_host() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_host() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_host"); // Technically, we should check if there is a hostname, but // the code below works even if there isn't. @@ -13889,7 +14648,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return helpers::substring(buffer, start, components.pathname_start); } -[[nodiscard]] std::string_view url_aggregator::get_hostname() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_hostname() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_hostname"); // Technically, we should check if there is a hostname, but // the code below works even if there isn't. @@ -13903,21 +14663,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return helpers::substring(buffer, start, components.host_end); } -[[nodiscard]] std::string_view url_aggregator::get_pathname() const noexcept { - ada_log("url_aggregator::get_pathname pathname_start = ", - components.pathname_start, " buffer.size() = ", buffer.size(), - " components.search_start = ", components.search_start, - " components.hash_start = ", components.hash_start); - auto ending_index = uint32_t(buffer.size()); - if (components.search_start != url_components::omitted) { - ending_index = components.search_start; - } else if (components.hash_start != url_components::omitted) { - ending_index = components.hash_start; - } - return helpers::substring(buffer, components.pathname_start, ending_index); -} - -[[nodiscard]] std::string_view url_aggregator::get_search() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_search() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_search"); // If this's URL's query is either null or the empty string, then return the // empty string. Return U+003F (?), followed by this's URL's query. @@ -13934,7 +14681,8 @@ bool url_aggregator::set_hostname(const std::string_view input) { return helpers::substring(buffer, components.search_start, ending_index); } -[[nodiscard]] std::string_view url_aggregator::get_protocol() const noexcept { +[[nodiscard]] std::string_view url_aggregator::get_protocol() const noexcept + ada_lifetime_bound { ada_log("url_aggregator::get_protocol"); return helpers::substring(buffer, 0, components.protocol_end); } @@ -14377,8 +15125,7 @@ bool url_aggregator::parse_opaque_host(std::string_view input) { ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]"); ADA_ASSERT_TRUE(validate()); ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); - if (std::any_of(input.begin(), input.end(), - ada::unicode::is_forbidden_host_code_point)) { + if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) { return is_valid = false; } @@ -14554,176 +15301,6 @@ bool url_aggregator::parse_opaque_host(std::string_view input) { return answer; } -[[nodiscard]] bool url_aggregator::validate() const noexcept { - if (!is_valid) { - return true; - } - if (!components.check_offset_consistency()) { - ada_log("url_aggregator::validate inconsistent components \n", - to_diagram()); - return false; - } - // We have a credible components struct, but let us investivate more - // carefully: - /** - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - if (components.protocol_end == url_components::omitted) { - ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram()); - return false; - } - if (components.username_end == url_components::omitted) { - ada_log("url_aggregator::validate omitted username_end \n", to_diagram()); - return false; - } - if (components.host_start == url_components::omitted) { - ada_log("url_aggregator::validate omitted host_start \n", to_diagram()); - return false; - } - if (components.host_end == url_components::omitted) { - ada_log("url_aggregator::validate omitted host_end \n", to_diagram()); - return false; - } - if (components.pathname_start == url_components::omitted) { - ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram()); - return false; - } - - if (components.protocol_end > buffer.size()) { - ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram()); - return false; - } - if (components.username_end > buffer.size()) { - ada_log("url_aggregator::validate username_end overflow \n", to_diagram()); - return false; - } - if (components.host_start > buffer.size()) { - ada_log("url_aggregator::validate host_start overflow \n", to_diagram()); - return false; - } - if (components.host_end > buffer.size()) { - ada_log("url_aggregator::validate host_end overflow \n", to_diagram()); - return false; - } - if (components.pathname_start > buffer.size()) { - ada_log("url_aggregator::validate pathname_start overflow \n", - to_diagram()); - return false; - } - - if (components.protocol_end > 0) { - if (buffer[components.protocol_end - 1] != ':') { - ada_log( - "url_aggregator::validate missing : at the end of the protocol \n", - to_diagram()); - return false; - } - } - - if (components.username_end != buffer.size() && - components.username_end > components.protocol_end + 2) { - if (buffer[components.username_end] != ':' && - buffer[components.username_end] != '@') { - ada_log( - "url_aggregator::validate missing : or @ at the end of the username " - "\n", - to_diagram()); - return false; - } - } - - if (components.host_start != buffer.size()) { - if (components.host_start > components.username_end) { - if (buffer[components.host_start] != '@') { - ada_log( - "url_aggregator::validate missing @ at the end of the password \n", - to_diagram()); - return false; - } - } else if (components.host_start == components.username_end && - components.host_end > components.host_start) { - if (components.host_start == components.protocol_end + 2) { - if (buffer[components.protocol_end] != '/' || - buffer[components.protocol_end + 1] != '/') { - ada_log( - "url_aggregator::validate missing // between protocol and host " - "\n", - to_diagram()); - return false; - } - } else { - if (components.host_start > components.protocol_end && - buffer[components.host_start] != '@') { - ada_log( - "url_aggregator::validate missing @ at the end of the username " - "\n", - to_diagram()); - return false; - } - } - } else { - if (components.host_end != components.host_start) { - ada_log("url_aggregator::validate expected omitted host \n", - to_diagram()); - return false; - } - } - } - if (components.host_end != buffer.size() && - components.pathname_start > components.host_end) { - if (components.pathname_start == components.host_end + 2 && - buffer[components.host_end] == '/' && - buffer[components.host_end + 1] == '.') { - if (components.pathname_start + 1 >= buffer.size() || - buffer[components.pathname_start] != '/' || - buffer[components.pathname_start + 1] != '/') { - ada_log( - "url_aggregator::validate expected the path to begin with // \n", - to_diagram()); - return false; - } - } else if (buffer[components.host_end] != ':') { - ada_log("url_aggregator::validate missing : at the port \n", - to_diagram()); - return false; - } - } - if (components.pathname_start != buffer.size() && - components.pathname_start < components.search_start && - components.pathname_start < components.hash_start && !has_opaque_path) { - if (buffer[components.pathname_start] != '/') { - ada_log("url_aggregator::validate missing / at the path \n", - to_diagram()); - return false; - } - } - if (components.search_start != url_components::omitted) { - if (buffer[components.search_start] != '?') { - ada_log("url_aggregator::validate missing ? at the search \n", - to_diagram()); - return false; - } - } - if (components.hash_start != url_components::omitted) { - if (buffer[components.hash_start] != '#') { - ada_log("url_aggregator::validate missing # at the hash \n", - to_diagram()); - return false; - } - } - - return true; -} - void url_aggregator::delete_dash_dot() { ada_log("url_aggregator::delete_dash_dot"); ADA_ASSERT_TRUE(validate()); @@ -14777,15 +15354,20 @@ inline void url_aggregator::consume_prepared_path(std::string_view input) { // Note: input cannot be empty, it must at least contain one character ('.') // Note: we know that '\' is not present. if (input[0] != '.') { - size_t slashdot = input.find("/."); - if (slashdot == std::string_view::npos) { // common case - trivial_path = true; - } else { // uncommon - // only three cases matter: /./, /.. or a final / - trivial_path = - !(slashdot + 2 == input.size() || input[slashdot + 2] == '.' || - input[slashdot + 2] == '/'); + size_t slashdot = 0; + bool dot_is_file = true; + for (;;) { + slashdot = input.find("/.", slashdot); + if (slashdot == std::string_view::npos) { // common case + break; + } else { // uncommon + // only three cases matter: /./, /.. or a final / + slashdot += 2; + dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' || + input[slashdot] == '/'); + } } + trivial_path = dot_is_file; } } if (trivial_path && is_at_path()) { @@ -14879,8 +15461,8 @@ inline void url_aggregator::consume_prepared_path(std::string_view input) { ? path_buffer_tmp : path_view; if (unicode::is_double_dot_path_segment(path_buffer)) { - if ((helpers::shorten_path(path, type) || special) && - location == std::string_view::npos) { + helpers::shorten_path(path, type); + if (location == std::string_view::npos) { path += '/'; } } else if (unicode::is_single_dot_path_segment(path_buffer) && @@ -14914,6 +15496,1470 @@ inline void url_aggregator::consume_prepared_path(std::string_view input) { } } // namespace ada /* end file src/url_aggregator.cpp */ + +#if ADA_INCLUDE_URL_PATTERN +/* begin file src/url_pattern.cpp */ +#if ADA_INCLUDE_URL_PATTERN + + +#include +#include +#include + +namespace ada { + +tl::expected url_pattern_init::process( + const url_pattern_init& init, url_pattern_init::process_type type, + std::optional protocol, + std::optional username, + std::optional password, + std::optional hostname, + std::optional port, + std::optional pathname, + std::optional search, + std::optional hash) { + // Let result be the result of creating a new URLPatternInit. + auto result = url_pattern_init{}; + + // If protocol is not null, set result["protocol"] to protocol. + if (protocol.has_value()) result.protocol = *protocol; + + // If username is not null, set result["username"] to username. + if (username.has_value()) result.username = *username; + + // If password is not null, set result["password"] to password. + if (password.has_value()) result.password = *password; + + // If hostname is not null, set result["hostname"] to hostname. + if (hostname.has_value()) result.hostname = *hostname; + + // If port is not null, set result["port"] to port. + if (port.has_value()) result.port = *port; + + // If pathname is not null, set result["pathname"] to pathname. + if (pathname.has_value()) result.pathname = *pathname; + + // If search is not null, set result["search"] to search. + if (search.has_value()) result.search = *search; + + // If hash is not null, set result["hash"] to hash. + if (hash.has_value()) result.hash = *hash; + + // Let baseURL be null. + std::optional base_url{}; + + // If init["baseURL"] exists: + if (init.base_url.has_value()) { + // Set baseURL to the result of parsing init["baseURL"]. + auto parsing_result = ada::parse(*init.base_url); + // If baseURL is failure, then throw a TypeError. + if (!parsing_result) { + return tl::unexpected(errors::type_error); + } + base_url = std::move(*parsing_result); + + // If init["protocol"] does not exist, then set result["protocol"] to the + // result of processing a base URL string given baseURL's scheme and type. + if (!init.protocol.has_value()) { + ADA_ASSERT_TRUE(base_url.has_value()); + std::string_view base_url_protocol = base_url->get_protocol(); + if (base_url_protocol.ends_with(":")) base_url_protocol.remove_suffix(1); + result.protocol = + url_pattern_helpers::process_base_url_string(base_url_protocol, type); + } + + // If type is not "pattern" and init contains none of "protocol", + // "hostname", "port" and "username", then set result["username"] to the + // result of processing a base URL string given baseURL's username and type. + if (type != process_type::pattern && !init.protocol && !init.hostname && + !init.port && !init.username) { + result.username = url_pattern_helpers::process_base_url_string( + base_url->get_username(), type); + } + + // TODO: Optimization opportunity: Merge this with the previous check. + // If type is not "pattern" and init contains none of "protocol", + // "hostname", "port", "username" and "password", then set + // result["password"] to the result of processing a base URL string given + // baseURL's password and type. + if (type != process_type::pattern && !init.protocol && !init.hostname && + !init.port && !init.username && !init.password) { + result.password = url_pattern_helpers::process_base_url_string( + base_url->get_password(), type); + } + + // If init contains neither "protocol" nor "hostname", then: + if (!init.protocol && !init.hostname) { + // Let baseHost be baseURL's host. + // If baseHost is null, then set baseHost to the empty string. + auto base_host = base_url->get_hostname(); + // Set result["hostname"] to the result of processing a base URL string + // given baseHost and type. + result.hostname = + url_pattern_helpers::process_base_url_string(base_host, type); + } + + // If init contains none of "protocol", "hostname", and "port", then: + if (!init.protocol && !init.hostname && !init.port) { + // If baseURL's port is null, then set result["port"] to the empty string. + // Otherwise, set result["port"] to baseURL's port, serialized. + result.port = base_url->get_port(); + } + + // If init contains none of "protocol", "hostname", "port", and "pathname", + // then set result["pathname"] to the result of processing a base URL string + // given the result of URL path serializing baseURL and type. + if (!init.protocol && !init.hostname && !init.port && !init.pathname) { + result.pathname = url_pattern_helpers::process_base_url_string( + base_url->get_pathname(), type); + } + + // If init contains none of "protocol", "hostname", "port", "pathname", and + // "search", then: + if (!init.protocol && !init.hostname && !init.port && !init.pathname && + !init.search) { + // Let baseQuery be baseURL's query. + // Set result["search"] to the result of processing a base URL string + // given baseQuery and type. + result.search = url_pattern_helpers::process_base_url_string( + base_url->get_search(), type); + } + + // If init contains none of "protocol", "hostname", "port", "pathname", + // "search", and "hash", then: + if (!init.protocol && !init.hostname && !init.port && !init.pathname && + !init.search && !init.hash) { + // Let baseFragment be baseURL's fragment. + // Set result["hash"] to the result of processing a base URL string given + // baseFragment and type. + result.hash = url_pattern_helpers::process_base_url_string( + base_url->get_hash(), type); + } + } + + // If init["protocol"] exists, then set result["protocol"] to the result of + // process protocol for init given init["protocol"] and type. + if (init.protocol) { + auto process_result = process_protocol(*init.protocol, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.protocol = std::move(*process_result); + } + + // If init["username"] exists, then set result["username"] to the result of + // process username for init given init["username"] and type. + if (init.username.has_value()) { + auto process_result = process_username(*init.username, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.username = std::move(*process_result); + } + + // If init["password"] exists, then set result["password"] to the result of + // process password for init given init["password"] and type. + if (init.password.has_value()) { + auto process_result = process_password(*init.password, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.password = std::move(*process_result); + } + + // If init["hostname"] exists, then set result["hostname"] to the result of + // process hostname for init given init["hostname"] and type. + if (init.hostname.has_value()) { + auto process_result = process_hostname(*init.hostname, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.hostname = std::move(*process_result); + } + + // If init["port"] exists, then set result["port"] to the result of process + // port for init given init["port"], result["protocol"], and type. + if (init.port) { + auto process_result = + process_port(*init.port, result.protocol.value_or("fake"), type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.port = std::move(*process_result); + } + + // If init["pathname"] exists: + if (init.pathname.has_value()) { + // Set result["pathname"] to init["pathname"]. + result.pathname = init.pathname; + + // If the following are all true: + // - baseURL is not null; + // - baseURL has an opaque path; and + // - the result of running is an absolute pathname given result["pathname"] + // and type is false, + if (base_url && !base_url->has_opaque_path && + !url_pattern_helpers::is_absolute_pathname(*result.pathname, type)) { + // Let baseURLPath be the result of running process a base URL string + // given the result of URL path serializing baseURL and type. + // TODO: Optimization opportunity: Avoid returning a string if no slash + // exist. + std::string base_url_path = url_pattern_helpers::process_base_url_string( + base_url->get_pathname(), type); + + // Let slash index be the index of the last U+002F (/) code point found in + // baseURLPath, interpreted as a sequence of code points, or null if there + // are no instances of the code point. + auto slash_index = base_url_path.find_last_of('/'); + + // If slash index is not null: + if (slash_index != std::string::npos) { + // Let new pathname be the code point substring from 0 to slash index + + // 1 within baseURLPath. + base_url_path.resize(slash_index + 1); + // Append result["pathname"] to the end of new pathname. + ADA_ASSERT_TRUE(result.pathname.has_value()); + base_url_path.append(std::move(*result.pathname)); + // Set result["pathname"] to new pathname. + result.pathname = std::move(base_url_path); + } + } + + // Set result["pathname"] to the result of process pathname for init given + // result["pathname"], result["protocol"], and type. + auto pathname_processing_result = + process_pathname(*result.pathname, result.protocol.value_or(""), type); + if (!pathname_processing_result) { + return tl::unexpected(pathname_processing_result.error()); + } + result.pathname = std::move(*pathname_processing_result); + } + + // If init["search"] exists then set result["search"] to the result of process + // search for init given init["search"] and type. + if (init.search) { + auto process_result = process_search(*init.search, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.search = std::move(*process_result); + } + + // If init["hash"] exists then set result["hash"] to the result of process + // hash for init given init["hash"] and type. + if (init.hash) { + auto process_result = process_hash(*init.hash, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.hash = std::move(*process_result); + } + // Return result. + return result; +} + +tl::expected url_pattern_init::process_protocol( + std::string_view value, process_type type) { + ada_log("process_protocol=", value, " [", type, "]"); + // Let strippedValue be the given value with a single trailing U+003A (:) + // removed, if any. + if (value.ends_with(":")) { + value.remove_suffix(1); + } + // If type is "pattern" then return strippedValue. + if (type == process_type::pattern) { + return std::string(value); + } + // Return the result of running canonicalize a protocol given strippedValue. + return url_pattern_helpers::canonicalize_protocol(value); +} + +tl::expected url_pattern_init::process_username( + std::string_view value, process_type type) { + // If type is "pattern" then return value. + if (type == process_type::pattern) { + return std::string(value); + } + // Return the result of running canonicalize a username given value. + return url_pattern_helpers::canonicalize_username(value); +} + +tl::expected url_pattern_init::process_password( + std::string_view value, process_type type) { + // If type is "pattern" then return value. + if (type == process_type::pattern) { + return std::string(value); + } + // Return the result of running canonicalize a password given value. + return url_pattern_helpers::canonicalize_password(value); +} + +tl::expected url_pattern_init::process_hostname( + std::string_view value, process_type type) { + ada_log("process_hostname value=", value, " type=", type); + // If type is "pattern" then return value. + if (type == process_type::pattern) { + return std::string(value); + } + // Return the result of running canonicalize a hostname given value. + return url_pattern_helpers::canonicalize_hostname(value); +} + +tl::expected url_pattern_init::process_port( + std::string_view port, std::string_view protocol, process_type type) { + // If type is "pattern" then return portValue. + if (type == process_type::pattern) { + return std::string(port); + } + // Return the result of running canonicalize a port given portValue and + // protocolValue. + return url_pattern_helpers::canonicalize_port_with_protocol(port, protocol); +} + +tl::expected url_pattern_init::process_pathname( + std::string_view value, std::string_view protocol, process_type type) { + // If type is "pattern" then return pathnameValue. + if (type == process_type::pattern) { + return std::string(value); + } + + // If protocolValue is a special scheme or the empty string, then return the + // result of running canonicalize a pathname given pathnameValue. + if (protocol.empty() || scheme::is_special(protocol)) { + return url_pattern_helpers::canonicalize_pathname(value); + } + + // Return the result of running canonicalize an opaque pathname given + // pathnameValue. + return url_pattern_helpers::canonicalize_opaque_pathname(value); +} + +tl::expected url_pattern_init::process_search( + std::string_view value, process_type type) { + // Let strippedValue be the given value with a single leading U+003F (?) + // removed, if any. + if (value.starts_with("?")) { + value.remove_prefix(1); + } + // We cannot assert that the value is no longer starting with a single + // question mark because technically it can start. The question is whether or + // not we should remove the first question mark. Ref: + // https://github.com/ada-url/ada/pull/992 The spec is not clear on this. + + // If type is "pattern" then return strippedValue. + if (type == process_type::pattern) { + return std::string(value); + } + // Return the result of running canonicalize a search given strippedValue. + return url_pattern_helpers::canonicalize_search(value); +} + +tl::expected url_pattern_init::process_hash( + std::string_view value, process_type type) { + // Let strippedValue be the given value with a single leading U+0023 (#) + // removed, if any. + if (value.starts_with("#")) { + value.remove_prefix(1); + } + ADA_ASSERT_TRUE(!value.starts_with("#")); + // If type is "pattern" then return strippedValue. + if (type == process_type::pattern) { + return std::string(value); + } + // Return the result of running canonicalize a hash given strippedValue. + return url_pattern_helpers::canonicalize_hash(value); +} + +} // namespace ada + +#endif // ADA_INCLUDE_URL_PATTERN +/* end file src/url_pattern.cpp */ +/* begin file src/url_pattern_helpers.cpp */ +#if ADA_INCLUDE_URL_PATTERN + +#include +#include +#include + +namespace ada::url_pattern_helpers { + +std::tuple> +generate_regular_expression_and_name_list( + const std::vector& part_list, + url_pattern_compile_component_options options) { + // Let result be "^" + std::string result = "^"; + + // Let name list be a new list + std::vector name_list{}; + + // For each part of part list: + for (const url_pattern_part& part : part_list) { + // If part's type is "fixed-text": + if (part.type == url_pattern_part_type::FIXED_TEXT) { + // If part's modifier is "none" + if (part.modifier == url_pattern_part_modifier::none) { + // Append the result of running escape a regexp string given part's + // value + result += escape_regexp_string(part.value); + } else { + // A "fixed-text" part with a modifier uses a non capturing group + // (?:) + // Append "(?:" to the end of result. + result.append("(?:"); + // Append the result of running escape a regexp string given part's + // value to the end of result. + result.append(escape_regexp_string(part.value)); + // Append ")" to the end of result. + result.append(")"); + // Append the result of running convert a modifier to a string given + // part's modifier to the end of result. + result.append(convert_modifier_to_string(part.modifier)); + } + continue; + } + + // Assert: part's name is not the empty string + ADA_ASSERT_TRUE(!part.name.empty()); + + // Append part's name to name list + name_list.push_back(part.name); + + // Let regexp value be part's value + std::string regexp_value = part.value; + + // If part's type is "segment-wildcard" + if (part.type == url_pattern_part_type::SEGMENT_WILDCARD) { + // then set regexp value to the result of running generate a segment + // wildcard regexp given options. + regexp_value = generate_segment_wildcard_regexp(options); + } + // Otherwise if part's type is "full-wildcard" + else if (part.type == url_pattern_part_type::FULL_WILDCARD) { + // then set regexp value to full wildcard regexp value. + regexp_value = ".*"; + } + + // If part's prefix is the empty string and part's suffix is the empty + // string + if (part.prefix.empty() && part.suffix.empty()) { + // If part's modifier is "none" or "optional" + if (part.modifier == url_pattern_part_modifier::none || + part.modifier == url_pattern_part_modifier::optional) { + // () + result += "(" + regexp_value + ")" + + convert_modifier_to_string(part.modifier); + } else { + // ((?:)) + result += "((?:" + regexp_value + ")" + + convert_modifier_to_string(part.modifier) + ")"; + } + continue; + } + + // If part's modifier is "none" or "optional" + if (part.modifier == url_pattern_part_modifier::none || + part.modifier == url_pattern_part_modifier::optional) { + // (?:()) + result += "(?:" + escape_regexp_string(part.prefix) + "(" + regexp_value + + ")" + escape_regexp_string(part.suffix) + ")" + + convert_modifier_to_string(part.modifier); + continue; + } + + // Assert: part's modifier is "zero-or-more" or "one-or-more" + ADA_ASSERT_TRUE(part.modifier == url_pattern_part_modifier::zero_or_more || + part.modifier == url_pattern_part_modifier::one_or_more); + + // Assert: part's prefix is not the empty string or part's suffix is not the + // empty string + ADA_ASSERT_TRUE(!part.prefix.empty() || !part.suffix.empty()); + + // (?:((?:)(?:(?:))*))? + // Append "(?:" to the end of result. + result.append("(?:"); + // Append the result of running escape a regexp string given part's prefix + // to the end of result. + result.append(escape_regexp_string(part.prefix)); + // Append "((?:" to the end of result. + result.append("((?:"); + // Append regexp value to the end of result. + result.append(regexp_value); + // Append ")(?:" to the end of result. + result.append(")(?:"); + // Append the result of running escape a regexp string given part's suffix + // to the end of result. + result.append(escape_regexp_string(part.suffix)); + // Append the result of running escape a regexp string given part's prefix + // to the end of result. + result.append(escape_regexp_string(part.prefix)); + // Append "(?:" to the end of result. + result.append("(?:"); + // Append regexp value to the end of result. + result.append(regexp_value); + // Append "))*)" to the end of result. + result.append("))*)"); + // Append the result of running escape a regexp string given part's suffix + // to the end of result. + result.append(escape_regexp_string(part.suffix)); + // Append ")" to the end of result. + result.append(")"); + + // If part's modifier is "zero-or-more" then append "?" to the end of result + if (part.modifier == url_pattern_part_modifier::zero_or_more) { + result += "?"; + } + } + + // Append "$" to the end of result + result += "$"; + + // Return (result, name list) + return {std::move(result), std::move(name_list)}; +} + +bool is_ipv6_address(std::string_view input) noexcept { + // If input's code point length is less than 2, then return false. + if (input.size() < 2) return false; + + // Let input code points be input interpreted as a list of code points. + // If input code points[0] is U+005B ([), then return true. + if (input.front() == '[') return true; + // If input code points[0] is U+007B ({) and input code points[1] is U+005B + // ([), then return true. + if (input.starts_with("{[")) return true; + // If input code points[0] is U+005C (\) and input code points[1] is U+005B + // ([), then return true. + return input.starts_with("\\["); +} + +std::string convert_modifier_to_string(url_pattern_part_modifier modifier) { + // TODO: Optimize this. + switch (modifier) { + // If modifier is "zero-or-more", then return "*". + case url_pattern_part_modifier::zero_or_more: + return "*"; + // If modifier is "optional", then return "?". + case url_pattern_part_modifier::optional: + return "?"; + // If modifier is "one-or-more", then return "+". + case url_pattern_part_modifier::one_or_more: + return "+"; + // Return the empty string. + default: + return ""; + } +} + +std::string generate_segment_wildcard_regexp( + url_pattern_compile_component_options options) { + // Let result be "[^". + std::string result = "[^"; + // Append the result of running escape a regexp string given options's + // delimiter code point to the end of result. + result.append(escape_regexp_string(options.get_delimiter())); + // Append "]+?" to the end of result. + result.append("]+?"); + // Return result. + ada_log("generate_segment_wildcard_regexp result: ", result); + return result; +} + +tl::expected canonicalize_protocol( + std::string_view input) { + ada_log("canonicalize_protocol called with input=", input); + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + + // IMPORTANT: Deviation from the spec. We remove the trailing ':' here. + if (input.ends_with(":")) { + input.remove_suffix(1); + } + + // Let dummyURL be a new URL record. + // Let parseResult be the result of running the basic URL parser given value + // followed by "://dummy.test", with dummyURL as url. + if (auto dummy_url = ada::parse( + std::string(input) + "://dummy.test", nullptr)) { + // IMPORTANT: Deviation from the spec. We remove the trailing ':' here. + // Since URL parser always return protocols ending with `:` + auto protocol = dummy_url->get_protocol(); + protocol.remove_suffix(1); + return std::string(protocol); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_username( + std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url.has_value()); + // Set the username given dummyURL and value. + if (!url->set_username(input)) { + return tl::unexpected(errors::type_error); + } + // Return dummyURL's username. + return std::string(url->get_username()); +} + +tl::expected canonicalize_password( + std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set the password given dummyURL and value. + auto url = ada::parse("fake://dummy.test", nullptr); + + ADA_ASSERT_TRUE(url.has_value()); + if (!url->set_password(input)) { + return tl::unexpected(errors::type_error); + } + // Return dummyURL's password. + return std::string(url->get_password()); +} + +tl::expected canonicalize_hostname( + std::string_view input) { + ada_log("canonicalize_hostname input=", input); + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Let parseResult be the result of running the basic URL parser given value + // with dummyURL as url and hostname state as state override. + + // IMPORTANT: The protocol needs to be a special protocol, otherwise the + // hostname will not be converted using IDNA. + auto url = ada::parse("https://dummy.test", nullptr); + ADA_ASSERT_TRUE(url); + // if (!isValidHostnameInput(hostname)) return kj::none; + if (!url->set_hostname(input)) { + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); + } + // Return dummyURL's host, serialized, or empty string if it is null. + return std::string(url->get_hostname()); +} + +tl::expected canonicalize_ipv6_hostname( + std::string_view input) { + ada_log("canonicalize_ipv6_hostname input=", input); + // TODO: Optimization opportunity: Use lookup table to speed up checking + if (std::ranges::any_of(input, [](char c) { + return c != '[' && c != ']' && c != ':' && + !unicode::is_ascii_hex_digit(c); + })) { + return tl::unexpected(errors::type_error); + } + // Append the result of running ASCII lowercase given code point to the end of + // result. + auto hostname = std::string(input); + unicode::to_lower_ascii(hostname.data(), hostname.size()); + return hostname; +} + +tl::expected canonicalize_port( + std::string_view port_value) { + // If portValue is the empty string, return portValue. + if (port_value.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // If protocolValue was given, then set dummyURL's scheme to protocolValue. + // Let parseResult be the result of running basic URL parser given portValue + // with dummyURL as url and port state as state override. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url); + if (url->set_port(port_value)) { + // Return dummyURL's port, serialized, or empty string if it is null. + return std::string(url->get_port()); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_port_with_protocol( + std::string_view port_value, std::string_view protocol) { + // If portValue is the empty string, return portValue. + if (port_value.empty()) [[unlikely]] { + return ""; + } + + // TODO: Remove this + // We have an empty protocol because get_protocol() returns an empty string + // We should handle this in the caller rather than here. + if (protocol.empty()) { + protocol = "fake"; + } else if (protocol.ends_with(":")) { + protocol.remove_suffix(1); + } + // Let dummyURL be a new URL record. + // If protocolValue was given, then set dummyURL's scheme to protocolValue. + // Let parseResult be the result of running basic URL parser given portValue + // with dummyURL as url and port state as state override. + auto url = ada::parse(std::string(protocol) + "://dummy.test", + nullptr); + // TODO: Remove has_port() check. + // This is actually a bug with url parser where set_port() returns true for + // "invalid80" port value. + if (url && url->set_port(port_value) && url->has_port()) { + // Return dummyURL's port, serialized, or empty string if it is null. + return std::string(url->get_port()); + } + // TODO: Remove this once the previous has_port() check is removed. + if (url) { + if (scheme::is_special(protocol) && url->get_port().empty()) { + return ""; + } + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_pathname( + std::string_view input) { + // If value is the empty string, then return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let leading slash be true if the first code point in value is U+002F (/) + // and otherwise false. + const bool leading_slash = input.starts_with("/"); + // Let modified value be "/-" if leading slash is false and otherwise the + // empty string. + const auto modified_value = leading_slash ? "" : "/-"; + const auto full_url = + std::string("fake://fake-url") + modified_value + std::string(input); + if (auto url = ada::parse(full_url, nullptr)) { + const auto pathname = url->get_pathname(); + // If leading slash is false, then set result to the code point substring + // from 2 to the end of the string within result. + return leading_slash ? std::string(pathname) + : std::string(pathname.substr(2)); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_opaque_pathname( + std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set dummyURL's path to the empty string. + // Let parseResult be the result of running URL parsing given value with + // dummyURL as url and opaque path state as state override. + if (auto url = + ada::parse("fake:" + std::string(input), nullptr)) { + // Return the result of URL path serializing dummyURL. + return std::string(url->get_pathname()); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_search(std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set dummyURL's query to the empty string. + // Let parseResult be the result of running basic URL parser given value with + // dummyURL as url and query state as state override. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url.has_value()); + url->set_search(input); + if (url->has_search()) { + const auto search = url->get_search(); + if (!search.empty()) { + return std::string(search.substr(1)); + } + return ""; + } + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_hash(std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set dummyURL's fragment to the empty string. + // Let parseResult be the result of running basic URL parser given value with + // dummyURL as url and fragment state as state override. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url.has_value()); + url->set_hash(input); + // Return dummyURL's fragment. + if (url->has_hash()) { + const auto hash = url->get_hash(); + if (!hash.empty()) { + return std::string(hash.substr(1)); + } + return ""; + } + return tl::unexpected(errors::type_error); +} + +tl::expected, errors> tokenize(std::string_view input, + token_policy policy) { + ada_log("tokenize input: ", input); + // Let tokenizer be a new tokenizer. + // Set tokenizer's input to input. + // Set tokenizer's policy to policy. + auto tokenizer = Tokenizer(input, policy); + // While tokenizer's index is less than tokenizer's input's code point length: + while (tokenizer.index < tokenizer.input.size()) { + // Run seek and get the next code point given tokenizer and tokenizer's + // index. + tokenizer.seek_and_get_next_code_point(tokenizer.index); + + // If tokenizer's code point is U+002A (*): + if (tokenizer.code_point == '*') { + // Run add a token with default position and length given tokenizer and + // "asterisk". + tokenizer.add_token_with_defaults(token_type::ASTERISK); + ada_log("add ASTERISK token"); + // Continue. + continue; + } + + // If tokenizer's code point is U+002B (+) or U+003F (?): + if (tokenizer.code_point == '+' || tokenizer.code_point == '?') { + // Run add a token with default position and length given tokenizer and + // "other-modifier". + tokenizer.add_token_with_defaults(token_type::OTHER_MODIFIER); + // Continue. + continue; + } + + // If tokenizer's code point is U+005C (\): + if (tokenizer.code_point == '\\') { + // If tokenizer's index is equal to tokenizer's input's code point length + // - 1: + if (tokenizer.index == tokenizer.input.size() - 1) { + // Run process a tokenizing error given tokenizer, tokenizer's next + // index, and tokenizer's index. + if (auto error = tokenizer.process_tokenizing_error( + tokenizer.next_index, tokenizer.index)) { + ada_log("process_tokenizing_error failed"); + return tl::unexpected(*error); + } + continue; + } + + // Let escaped index be tokenizer's next index. + auto escaped_index = tokenizer.next_index; + // Run get the next code point given tokenizer. + tokenizer.get_next_code_point(); + // Run add a token with default length given tokenizer, "escaped-char", + // tokenizer's next index, and escaped index. + tokenizer.add_token_with_default_length( + token_type::ESCAPED_CHAR, tokenizer.next_index, escaped_index); + ada_log("add ESCAPED_CHAR token on next_index ", tokenizer.next_index, + " with escaped index ", escaped_index); + // Continue. + continue; + } + + // If tokenizer's code point is U+007B ({): + if (tokenizer.code_point == '{') { + // Run add a token with default position and length given tokenizer and + // "open". + tokenizer.add_token_with_defaults(token_type::OPEN); + ada_log("add OPEN token"); + continue; + } + + // If tokenizer's code point is U+007D (}): + if (tokenizer.code_point == '}') { + // Run add a token with default position and length given tokenizer and + // "close". + tokenizer.add_token_with_defaults(token_type::CLOSE); + ada_log("add CLOSE token"); + continue; + } + + // If tokenizer's code point is U+003A (:): + if (tokenizer.code_point == ':') { + // Let name position be tokenizer's next index. + auto name_position = tokenizer.next_index; + // Let name start be name position. + auto name_start = name_position; + // While name position is less than tokenizer's input's code point length: + while (name_position < tokenizer.input.size()) { + // Run seek and get the next code point given tokenizer and name + // position. + tokenizer.seek_and_get_next_code_point(name_position); + // Let first code point be true if name position equals name start and + // false otherwise. + bool first_code_point = name_position == name_start; + // Let valid code point be the result of running is a valid name code + // point given tokenizer's code point and first code point. + auto valid_code_point = + idna::valid_name_code_point(tokenizer.code_point, first_code_point); + ada_log("tokenizer.code_point=", uint32_t(tokenizer.code_point), + " first_code_point=", first_code_point, + " valid_code_point=", valid_code_point); + // If valid code point is false break. + if (!valid_code_point) break; + // Set name position to tokenizer's next index. + name_position = tokenizer.next_index; + } + + // If name position is less than or equal to name start: + if (name_position <= name_start) { + // Run process a tokenizing error given tokenizer, name start, and + // tokenizer's index. + if (auto error = tokenizer.process_tokenizing_error(name_start, + tokenizer.index)) { + ada_log("process_tokenizing_error failed"); + return tl::unexpected(*error); + } + // Continue + continue; + } + + // Run add a token with default length given tokenizer, "name", name + // position, and name start. + tokenizer.add_token_with_default_length(token_type::NAME, name_position, + name_start); + continue; + } + + // If tokenizer's code point is U+0028 ((): + if (tokenizer.code_point == '(') { + // Let depth be 1. + size_t depth = 1; + // Let regexp position be tokenizer's next index. + auto regexp_position = tokenizer.next_index; + // Let regexp start be regexp position. + auto regexp_start = regexp_position; + // Let error be false. + bool error = false; + + // While regexp position is less than tokenizer's input's code point + // length: + while (regexp_position < tokenizer.input.size()) { + // Run seek and get the next code point given tokenizer and regexp + // position. + tokenizer.seek_and_get_next_code_point(regexp_position); + + // TODO: Optimization opportunity: The next 2 if statements can be + // merged. If the result of running is ASCII given tokenizer's code + // point is false: + if (!unicode::is_ascii(tokenizer.code_point)) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + + // If regexp position equals regexp start and tokenizer's code point is + // U+003F (?): + if (regexp_position == regexp_start && tokenizer.code_point == '?') { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true; + error = true; + break; + } + + // If tokenizer's code point is U+005C (\): + if (tokenizer.code_point == '\\') { + // If regexp position equals tokenizer's input's code point length - 1 + if (regexp_position == tokenizer.input.size() - 1) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Run get the next code point given tokenizer. + tokenizer.get_next_code_point(); + // If the result of running is ASCII given tokenizer's code point is + // false: + if (!unicode::is_ascii(tokenizer.code_point)) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index); + process_error.has_value()) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Set regexp position to tokenizer's next index. + regexp_position = tokenizer.next_index; + continue; + } + + // If tokenizer's code point is U+0029 ()): + if (tokenizer.code_point == ')') { + // Decrement depth by 1. + depth--; + // If depth is 0: + if (depth == 0) { + // Set regexp position to tokenizer's next index. + regexp_position = tokenizer.next_index; + // Break. + break; + } + } else if (tokenizer.code_point == '(') { + // Otherwise if tokenizer's code point is U+0028 ((): + // Increment depth by 1. + depth++; + // If regexp position equals tokenizer's input's code point length - + // 1: + if (regexp_position == tokenizer.input.size() - 1) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Let temporary position be tokenizer's next index. + auto temporary_position = tokenizer.next_index; + // Run get the next code point given tokenizer. + tokenizer.get_next_code_point(); + // If tokenizer's code point is not U+003F (?): + if (tokenizer.code_point != '?') { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Set tokenizer's next index to temporary position. + tokenizer.next_index = temporary_position; + } + // Set regexp position to tokenizer's next index. + regexp_position = tokenizer.next_index; + } + + // If error is true continue. + if (error) continue; + // If depth is not zero: + if (depth != 0) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + continue; + } + // Let regexp length be regexp position - regexp start - 1. + auto regexp_length = regexp_position - regexp_start - 1; + // If regexp length is zero: + if (regexp_length == 0) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer's index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + ada_log("process_tokenizing_error failed"); + return tl::unexpected(*process_error); + } + continue; + } + // Run add a token given tokenizer, "regexp", regexp position, regexp + // start, and regexp length. + tokenizer.add_token(token_type::REGEXP, regexp_position, regexp_start, + regexp_length); + continue; + } + // Run add a token with default position and length given tokenizer and + // "char". + tokenizer.add_token_with_defaults(token_type::CHAR); + } + // Run add a token with default length given tokenizer, "end", tokenizer's + // index, and tokenizer's index. + tokenizer.add_token_with_default_length(token_type::END, tokenizer.index, + tokenizer.index); + + ada_log("tokenizer.token_list size is: ", tokenizer.token_list.size()); + // Return tokenizer's token list. + return tokenizer.token_list; +} + +std::string escape_pattern_string(std::string_view input) { + ada_log("escape_pattern_string called with input=", input); + if (input.empty()) [[unlikely]] { + return ""; + } + // Assert: input is an ASCII string. + ADA_ASSERT_TRUE(ada::idna::is_ascii(input)); + // Let result be the empty string. + std::string result{}; + result.reserve(input.size()); + + // TODO: Optimization opportunity: Use a lookup table + constexpr auto should_escape = [](const char c) { + return c == '+' || c == '*' || c == '?' || c == ':' || c == '{' || + c == '}' || c == '(' || c == ')' || c == '\\'; + }; + + // While index is less than input's length: + for (const auto& c : input) { + if (should_escape(c)) { + // then append U+005C (\) to the end of result. + result.append("\\"); + } + + // Append c to the end of result. + result += c; + } + // Return result. + return result; +} + +namespace { +constexpr std::array escape_regexp_table = []() consteval { + std::array out{}; + for (auto& c : {'.', '+', '*', '?', '^', '$', '{', '}', '(', ')', '[', ']', + '|', '/', '\\'}) { + out[c] = 1; + } + return out; +}(); + +constexpr bool should_escape_regexp_char(char c) { + return escape_regexp_table[(uint8_t)c]; +} +} // namespace + +std::string escape_regexp_string(std::string_view input) { + // Assert: input is an ASCII string. + ADA_ASSERT_TRUE(idna::is_ascii(input)); + // Let result be the empty string. + std::string result{}; + result.reserve(input.size()); + for (const auto& c : input) { + // TODO: Optimize this even further + if (should_escape_regexp_char(c)) { + result.append(std::string("\\") + c); + } else { + result.push_back(c); + } + } + return result; +} + +std::string process_base_url_string(std::string_view input, + url_pattern_init::process_type type) { + // If type is not "pattern" return input. + if (type != url_pattern_init::process_type::pattern) { + return std::string(input); + } + // Return the result of escaping a pattern string given input. + return escape_pattern_string(input); +} + +constexpr bool is_absolute_pathname( + std::string_view input, url_pattern_init::process_type type) noexcept { + // If input is the empty string, then return false. + if (input.empty()) [[unlikely]] { + return false; + } + // If input[0] is U+002F (/), then return true. + if (input.starts_with("/")) return true; + // If type is "url", then return false. + if (type == url_pattern_init::process_type::url) return false; + // If input's code point length is less than 2, then return false. + if (input.size() < 2) return false; + // If input[0] is U+005C (\) and input[1] is U+002F (/), then return true. + // If input[0] is U+007B ({) and input[1] is U+002F (/), then return true. + // Return false. + return input[1] == '/' && (input[0] == '\\' || input[0] == '{'); +} + +std::string generate_pattern_string( + std::vector& part_list, + url_pattern_compile_component_options& options) { + // Let result be the empty string. + std::string result{}; + // Let index list be the result of getting the indices for part list. + // For each index of index list: + for (size_t index = 0; index < part_list.size(); index++) { + // Let part be part list[index]. + auto part = part_list[index]; + // Let previous part be part list[index - 1] if index is greater than 0, + // otherwise let it be null. + // TODO: Optimization opportunity. Find a way to avoid making a copy here. + std::optional previous_part = + index == 0 ? std::nullopt : std::optional(part_list[index - 1]); + // Let next part be part list[index + 1] if index is less than index list's + // size - 1, otherwise let it be null. + std::optional next_part = + index < part_list.size() - 1 ? std::optional(part_list[index + 1]) + : std::nullopt; + // If part's type is "fixed-text" then: + if (part.type == url_pattern_part_type::FIXED_TEXT) { + // If part's modifier is "none" then: + if (part.modifier == url_pattern_part_modifier::none) { + // Append the result of running escape a pattern string given part's + // value to the end of result. + result.append(escape_pattern_string(part.value)); + continue; + } + // Append "{" to the end of result. + result += "{"; + // Append the result of running escape a pattern string given part's value + // to the end of result. + result.append(escape_pattern_string(part.value)); + // Append "}" to the end of result. + result += "}"; + // Append the result of running convert a modifier to a string given + // part's modifier to the end of result. + result.append(convert_modifier_to_string(part.modifier)); + continue; + } + // Let custom name be true if part's name[0] is not an ASCII digit; + // otherwise false. + bool custom_name = !unicode::is_ascii_digit(part.name[0]); + // Let needs grouping be true if at least one of the following are true, + // otherwise let it be false: + // - part's suffix is not the empty string. + // - part's prefix is not the empty string and is not options's prefix code + // point. + bool needs_grouping = + !part.suffix.empty() || + (!part.prefix.empty() && part.prefix[0] != options.get_prefix()[0]); + + // If all of the following are true: + // - needs grouping is false; and + // - custom name is true; and + // - part's type is "segment-wildcard"; and + // - part's modifier is "none"; and + // - next part is not null; and + // - next part's prefix is the empty string; and + // - next part's suffix is the empty string + if (!needs_grouping && custom_name && + part.type == url_pattern_part_type::SEGMENT_WILDCARD && + part.modifier == url_pattern_part_modifier::none && + next_part.has_value() && next_part->prefix.empty() && + next_part->suffix.empty()) { + // If next part's type is "fixed-text": + if (next_part->type == url_pattern_part_type::FIXED_TEXT) { + // Set needs grouping to true if the result of running is a valid name + // code point given next part's value's first code point and the boolean + // false is true. + if (idna::valid_name_code_point(next_part->value[0], false)) { + needs_grouping = true; + } + } else { + // Set needs grouping to true if next part's name[0] is an ASCII digit. + needs_grouping = !next_part->name.empty() && + unicode::is_ascii_digit(next_part->name[0]); + } + } + + // If all of the following are true: + // - needs grouping is false; and + // - part's prefix is the empty string; and + // - previous part is not null; and + // - previous part's type is "fixed-text"; and + // - previous part's value's last code point is options's prefix code point. + // then set needs grouping to true. + if (!needs_grouping && part.prefix.empty() && previous_part.has_value() && + previous_part->type == url_pattern_part_type::FIXED_TEXT && + !options.get_prefix().empty() && + previous_part->value.at(previous_part->value.size() - 1) == + options.get_prefix()[0]) { + needs_grouping = true; + } + + // Assert: part's name is not the empty string or null. + ADA_ASSERT_TRUE(!part.name.empty()); + + // If needs grouping is true, then append "{" to the end of result. + if (needs_grouping) { + result.append("{"); + } + + // Append the result of running escape a pattern string given part's prefix + // to the end of result. + result.append(escape_pattern_string(part.prefix)); + + // If custom name is true: + if (custom_name) { + // Append ":" to the end of result. + result.append(":"); + // Append part's name to the end of result. + result.append(part.name); + } + + // If part's type is "regexp" then: + if (part.type == url_pattern_part_type::REGEXP) { + // Append "(" to the end of result. + result.append("("); + // Append part's value to the end of result. + result.append(part.value); + // Append ")" to the end of result. + result.append(")"); + } else if (part.type == url_pattern_part_type::SEGMENT_WILDCARD && + !custom_name) { + // Otherwise if part's type is "segment-wildcard" and custom name is + // false: Append "(" to the end of result. + result.append("("); + // Append the result of running generate a segment wildcard regexp given + // options to the end of result. + result.append(generate_segment_wildcard_regexp(options)); + // Append ")" to the end of result. + result.append(")"); + } else if (part.type == url_pattern_part_type::FULL_WILDCARD) { + // Otherwise if part's type is "full-wildcard": + // If custom name is false and one of the following is true: + // - previous part is null; or + // - previous part's type is "fixed-text"; or + // - previous part's modifier is not "none"; or + // - needs grouping is true; or + // - part's prefix is not the empty string + // - then append "*" to the end of result. + if (!custom_name && + (!previous_part.has_value() || + previous_part->type == url_pattern_part_type::FIXED_TEXT || + previous_part->modifier != url_pattern_part_modifier::none || + needs_grouping || !part.prefix.empty())) { + result.append("*"); + } else { + // Append "(" to the end of result. + // Append full wildcard regexp value to the end of result. + // Append ")" to the end of result. + result.append("(.*)"); + } + } + + // If all of the following are true: + // - part's type is "segment-wildcard"; and + // - custom name is true; and + // - part's suffix is not the empty string; and + // - The result of running is a valid name code point given part's suffix's + // first code point and the boolean false is true then append U+005C (\) to + // the end of result. + if (part.type == url_pattern_part_type::SEGMENT_WILDCARD && custom_name && + !part.suffix.empty() && + idna::valid_name_code_point(part.suffix[0], false)) { + result.append("\\"); + } + + // Append the result of running escape a pattern string given part's suffix + // to the end of result. + result.append(escape_pattern_string(part.suffix)); + // If needs grouping is true, then append "}" to the end of result. + if (needs_grouping) result.append("}"); + // Append the result of running convert a modifier to a string given part's + // modifier to the end of result. + result.append(convert_modifier_to_string(part.modifier)); + } + // Return result. + return result; +} +} // namespace ada::url_pattern_helpers + +#endif // ADA_INCLUDE_URL_PATTERN +/* end file src/url_pattern_helpers.cpp */ +/* begin file src/url_pattern_regex.cpp */ +#if ADA_INCLUDE_URL_PATTERN + + +namespace ada::url_pattern_regex { + +#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER +std::optional std_regex_provider::create_instance( + std::string_view pattern, bool ignore_case) { + // Let flags be an empty string. + // If options's ignore case is true then set flags to "vi". + // Otherwise set flags to "v" + auto flags = ignore_case + ? std::regex::icase | std::regex_constants::ECMAScript + : std::regex_constants::ECMAScript; + try { + return std::regex(pattern.data(), pattern.size(), flags); + } catch (const std::regex_error& e) { + (void)e; + ada_log("std_regex_provider::create_instance failed:", e.what()); + return std::nullopt; + } +} + +std::optional>> +std_regex_provider::regex_search(std::string_view input, + const std::regex& pattern) { + std::string input_str( + input.begin(), + input.end()); // Convert string_view to string for regex_search + std::smatch match_result; + if (!std::regex_search(input_str, match_result, pattern, + std::regex_constants::match_any)) { + return std::nullopt; + } + std::vector> matches; + // If input is empty, let's assume the result will be empty as well. + if (input.empty() || match_result.empty()) { + return matches; + } + matches.reserve(match_result.size()); + for (size_t i = 1; i < match_result.size(); ++i) { + if (auto entry = match_result[i]; entry.matched) { + matches.emplace_back(entry.str()); + } + } + return matches; +} + +bool std_regex_provider::regex_match(std::string_view input, + const std::regex& pattern) { + return std::regex_match(input.begin(), input.end(), pattern); +} + +#endif // ADA_USE_UNSAFE_STD_REGEX_PROVIDER + +} // namespace ada::url_pattern_regex + +#endif // ADA_INCLUDE_URL_PATTERN +/* end file src/url_pattern_regex.cpp */ +#endif // ADA_INCLUDE_URL_PATTERN + /* begin file src/ada_c.cpp */ ada::result& get_instance(void* result) noexcept { diff --git a/test-app/runtime/src/main/cpp/modules/url/ada/ada.h b/test-app/runtime/src/main/cpp/modules/url/ada/ada.h index a42ed6ef..db2803c8 100644 --- a/test-app/runtime/src/main/cpp/modules/url/ada/ada.h +++ b/test-app/runtime/src/main/cpp/modules/url/ada/ada.h @@ -1,4 +1,4 @@ -/* auto-generated on 2024-07-06 17:38:56 -0400. Do not edit! */ +/* auto-generated on 2025-09-23 12:57:35 -0400. Do not edit! */ /* begin file include/ada.h */ /** * @file ada.h @@ -8,7 +8,7 @@ #define ADA_H /* begin file include/ada/ada_idna.h */ -/* auto-generated on 2023-09-19 15:58:51 -0400. Do not edit! */ +/* auto-generated on 2025-03-08 13:17:11 -0500. Do not edit! */ /* begin file include/idna.h */ #ifndef ADA_IDNA_H #define ADA_IDNA_H @@ -45,8 +45,6 @@ namespace ada::idna { // If the input is ascii, then the mapping is just -> lower case. void ascii_map(char* input, size_t length); -// check whether an ascii string needs mapping -bool ascii_has_upper_case(char* input, size_t length); // Map the characters according to IDNA, returning the empty string on error. std::u32string map(std::u32string_view input); @@ -129,9 +127,6 @@ std::string to_ascii(std::string_view ut8_string); // https://url.spec.whatwg.org/#forbidden-domain-code-point bool contains_forbidden_domain_code_point(std::string_view ascii_string); -bool begins_with(std::u32string_view view, std::u32string_view prefix); -bool begins_with(std::string_view view, std::string_view prefix); - bool constexpr is_ascii(std::u32string_view view); bool constexpr is_ascii(std::string_view view); @@ -154,20 +149,32 @@ std::string to_unicode(std::string_view input); #endif // ADA_IDNA_TO_UNICODE_H /* end file include/ada/idna/to_unicode.h */ +/* begin file include/ada/idna/identifier.h */ +#ifndef ADA_IDNA_IDENTIFIER_H +#define ADA_IDNA_IDENTIFIER_H + +#include +#include + +namespace ada::idna { + +// Verify if it is valid name code point given a Unicode code point and a +// boolean first: If first is true return the result of checking if code point +// is contained in the IdentifierStart set of code points. Otherwise return the +// result of checking if code point is contained in the IdentifierPart set of +// code points. Returns false if the input is empty or the code point is not +// valid. There is minimal Unicode error handling: the input should be valid +// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point +bool valid_name_code_point(char32_t code_point, bool first); + +} // namespace ada::idna + +#endif +/* end file include/ada/idna/identifier.h */ #endif /* end file include/idna.h */ /* end file include/ada/ada_idna.h */ -/* begin file include/ada/character_sets-inl.h */ -/** - * @file character_sets-inl.h - * @brief Definitions of the character sets used by unicode functions. - * @author Node.js - * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc - */ -#ifndef ADA_CHARACTER_SETS_INL_H -#define ADA_CHARACTER_SETS_INL_H - /* begin file include/ada/character_sets.h */ /** * @file character_sets.h @@ -186,6 +193,10 @@ std::string to_unicode(std::string_view input); #ifndef ADA_COMMON_DEFS_H #define ADA_COMMON_DEFS_H +// https://en.cppreference.com/w/cpp/feature_test#Library_features +// detect C++20 features +#include + #ifdef _MSC_VER #define ADA_VISUAL_STUDIO 1 /** @@ -230,13 +241,6 @@ std::string to_unicode(std::string_view input); #define ada_unused #define ada_warn_unused -#ifndef ada_likely -#define ada_likely(x) x -#endif -#ifndef ada_unlikely -#define ada_unlikely(x) x -#endif - #define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push)) #define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0)) #define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \ @@ -268,13 +272,6 @@ std::string to_unicode(std::string_view input); #define ada_unused __attribute__((unused)) #define ada_warn_unused __attribute__((warn_unused_result)) -#ifndef ada_likely -#define ada_likely(x) __builtin_expect(!!(x), 1) -#endif -#ifndef ada_unlikely -#define ada_unlikely(x) __builtin_expect(!!(x), 0) -#endif - #define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push") // gcc doesn't seem to disable all warnings with all and extra, add warnings // here as necessary @@ -290,7 +287,8 @@ std::string to_unicode(std::string_view input); ADA_DISABLE_GCC_WARNING("-Wreturn-type") \ ADA_DISABLE_GCC_WARNING("-Wshadow") \ ADA_DISABLE_GCC_WARNING("-Wunused-parameter") \ - ADA_DISABLE_GCC_WARNING("-Wunused-variable") + ADA_DISABLE_GCC_WARNING("-Wunused-variable") \ + ADA_DISABLE_GCC_WARNING("-Wsign-compare") #define ADA_PRAGMA(P) _Pragma(#P) #define ADA_DISABLE_GCC_WARNING(WARNING) \ ADA_PRAGMA(GCC diagnostic ignored WARNING) @@ -354,52 +352,6 @@ namespace ada { } } // namespace ada -#if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ <= 8 -#define ADA_OLD_GCC 1 -#endif // __GNUC__ <= 8 -#endif // defined(__GNUC__) && !defined(__clang__) - -#if ADA_OLD_GCC -#define ada_constexpr -#else -#define ada_constexpr constexpr -#endif - -#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) -#define ADA_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#elif defined(_WIN32) -#define ADA_IS_BIG_ENDIAN 0 -#else -#if defined(__APPLE__) || \ - defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined - // __ORDER_BIG_ENDIAN__ -#include -#elif defined(sun) || \ - defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__) -#include -#else // defined(__APPLE__) || defined(__FreeBSD__) - -#ifdef __has_include -#if __has_include() -#include -#endif //__has_include() -#endif //__has_include - -#endif // defined(__APPLE__) || defined(__FreeBSD__) - -#ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) -#define ADA_IS_BIG_ENDIAN 0 -#endif - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define ADA_IS_BIG_ENDIAN 0 -#else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define ADA_IS_BIG_ENDIAN 1 -#endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - -#endif // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ - // Unless the programmer has already set ADA_DEVELOPMENT_CHECKS, // we want to set it under debug builds. We detect a debug build // under Visual Studio when the _DEBUG macro is set. Under the other @@ -479,6 +431,33 @@ namespace ada { #define ADA_NEON 1 #endif +#if defined(__loongarch_sx) +#define ADA_LSX 1 +#endif + +#ifndef __has_cpp_attribute +#define ada_lifetime_bound +#elif __has_cpp_attribute(msvc::lifetimebound) +#define ada_lifetime_bound [[msvc::lifetimebound]] +#elif __has_cpp_attribute(clang::lifetimebound) +#define ada_lifetime_bound [[clang::lifetimebound]] +#elif __has_cpp_attribute(lifetimebound) +#define ada_lifetime_bound [[lifetimebound]] +#else +#define ada_lifetime_bound +#endif + +#ifdef __cpp_lib_format +#if __cpp_lib_format >= 202110L +#include +#define ADA_HAS_FORMAT 1 +#endif +#endif + +#ifndef ADA_INCLUDE_URL_PATTERN +#define ADA_INCLUDE_URL_PATTERN 1 +#endif // ADA_INCLUDE_URL_PATTERN + #endif // ADA_COMMON_DEFS_H /* end file include/ada/common_defs.h */ #include @@ -491,11 +470,21 @@ namespace ada { * @brief Includes the definitions for unicode character sets. */ namespace ada::character_sets { -ada_really_inline bool bit_at(const uint8_t a[], uint8_t i); +ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i); } // namespace ada::character_sets #endif // ADA_CHARACTER_SETS_H /* end file include/ada/character_sets.h */ +/* begin file include/ada/character_sets-inl.h */ +/** + * @file character_sets-inl.h + * @brief Definitions of the character sets used by unicode functions. + * @author Node.js + * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc + */ +#ifndef ADA_CHARACTER_SETS_INL_H +#define ADA_CHARACTER_SETS_INL_H + /** * These functions are not part of our public API and may @@ -892,7 +881,7 @@ constexpr uint8_t PATH_PERCENT_ENCODE[32] = { // 50 51 52 53 54 55 56 57 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, // 58 59 5A 5B 5C 5D 5E 5F - 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, + 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00, // 60 61 62 63 64 65 66 67 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, // 68 69 6A 6B 6C 6D 6E 6F @@ -958,7 +947,7 @@ constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = { // 50 51 52 53 54 55 56 57 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, // 58 59 5A 5B 5C 5D 5E 5F - 0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x40 | 0x00, + 0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00, // 60 61 62 63 64 65 66 67 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, // 68 69 6A 6B 6C 6D 6E 6F @@ -1000,7 +989,7 @@ constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = { // F8 F9 FA FB FC FD FE FF 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80}; -ada_really_inline bool bit_at(const uint8_t a[], const uint8_t i) { +ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) { return !!(a[i >> 3] & (1 << (i & 7))); } @@ -1016,22 +1005,19 @@ ada_really_inline bool bit_at(const uint8_t a[], const uint8_t i) { #ifndef ADA_CHECKERS_INL_H #define ADA_CHECKERS_INL_H -#include -#include +#include #include namespace ada::checkers { -inline bool has_hex_prefix_unsafe(std::string_view input) { +constexpr bool has_hex_prefix_unsafe(std::string_view input) { // This is actually efficient code, see has_hex_prefix for the assembly. - uint32_t value_one = 1; - bool is_little_endian = (reinterpret_cast(&value_one)[0] == 1); - uint16_t word0x{}; - std::memcpy(&word0x, "0x", 2); // we would use bit_cast in C++20 and the - // function could be constexpr. - uint16_t two_first_bytes{}; - std::memcpy(&two_first_bytes, input.data(), 2); - if (is_little_endian) { + constexpr bool is_little_endian = std::endian::native == std::endian::little; + constexpr uint16_t word0x = 0x7830; + uint16_t two_first_bytes = + static_cast(input[0]) | + static_cast((static_cast(input[1]) << 8)); + if constexpr (is_little_endian) { two_first_bytes |= 0x2000; } else { two_first_bytes |= 0x020; @@ -1039,7 +1025,7 @@ inline bool has_hex_prefix_unsafe(std::string_view input) { return two_first_bytes == word0x; } -inline bool has_hex_prefix(std::string_view input) { +constexpr bool has_hex_prefix(std::string_view input) { return input.size() >= 2 && has_hex_prefix_unsafe(input); } @@ -1051,26 +1037,18 @@ constexpr bool is_alpha(char x) noexcept { return (to_lower(x) >= 'a') && (to_lower(x) <= 'z'); } -inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept { +constexpr bool is_windows_drive_letter(std::string_view input) noexcept { return input.size() >= 2 && (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) && ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' || input[2] == '?' || input[2] == '#')); } -inline constexpr bool is_normalized_windows_drive_letter( +constexpr bool is_normalized_windows_drive_letter( std::string_view input) noexcept { return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':')); } -ada_really_inline bool begins_with(std::string_view view, - std::string_view prefix) { - // in C++20, you have view.begins_with(prefix) - // std::equal is constexpr in C++20 - return view.size() >= prefix.size() && - std::equal(prefix.begin(), prefix.end(), view.begin()); -} - } // namespace ada::checkers #endif // ADA_CHECKERS_INL_H @@ -1084,65 +1062,31 @@ ada_really_inline bool begins_with(std::string_view view, #ifndef ADA_LOG_H #define ADA_LOG_H -#include // To enable logging, set ADA_LOGGING to 1: #ifndef ADA_LOGGING #define ADA_LOGGING 0 #endif -namespace ada { - -/** - * Private function used for logging messages. - * @private - */ -template -ada_really_inline void inner_log([[maybe_unused]] T t) { -#if ADA_LOGGING - std::cout << t << std::endl; -#endif -} - -/** - * Private function used for logging messages. - * @private - */ -template -ada_really_inline void inner_log([[maybe_unused]] T t, - [[maybe_unused]] Args... args) { #if ADA_LOGGING - std::cout << t; - inner_log(args...); -#endif -} +#include +#endif // ADA_LOGGING -/** - * Log a message. - * @private - */ -template -ada_really_inline void log([[maybe_unused]] T t, - [[maybe_unused]] Args... args) { -#if ADA_LOGGING - std::cout << "ADA_LOG: " << t; - inner_log(args...); -#endif -} +namespace ada { /** - * Log a message. + * Log a message. If you want to have no overhead when logging is disabled, use + * the ada_log macro. * @private */ -template -ada_really_inline void log([[maybe_unused]] T t) { +template +constexpr ada_really_inline void log([[maybe_unused]] Args... args) { #if ADA_LOGGING - std::cout << "ADA_LOG: " << t << std::endl; -#endif + ((std::cout << "ADA_LOG: ") << ... << args) << std::endl; +#endif // ADA_LOGGING } } // namespace ada #if ADA_LOGGING - #ifndef ada_log #define ada_log(...) \ do { \ @@ -1182,7 +1126,7 @@ enum class encoding_type { /** * Convert a encoding_type to string. */ -ada_warn_unused std::string to_string(encoding_type type); +ada_warn_unused std::string_view to_string(encoding_type type); } // namespace ada @@ -1196,512 +1140,311 @@ ada_warn_unused std::string to_string(encoding_type type); #ifndef ADA_HELPERS_H #define ADA_HELPERS_H -/* begin file include/ada/state.h */ +/* begin file include/ada/url_base.h */ /** - * @file state.h - * @brief Definitions for the states of the URL state machine. + * @file url_base.h + * @brief Declaration for the basic URL definitions */ -#ifndef ADA_STATE_H -#define ADA_STATE_H +#ifndef ADA_URL_BASE_H +#define ADA_URL_BASE_H + +/* begin file include/ada/scheme.h */ +/** + * @file scheme.h + * @brief Declarations for the URL scheme. + */ +#ifndef ADA_SCHEME_H +#define ADA_SCHEME_H + + +#include + +/** + * @namespace ada::scheme + * @brief Includes the scheme declarations + */ +namespace ada::scheme { + +/** + * Type of the scheme as an enum. + * Using strings to represent a scheme type is not ideal because + * checking for types involves string comparisons. It is faster to use + * a simple integer. + * In C++11, we are allowed to specify the underlying type of the enum. + * We pick an 8-bit integer (which allows up to 256 types). Specifying the + * type of the enum may help integration with other systems if the type + * variable is exposed (since its value will not depend on the compiler). + */ +enum type : uint8_t { + HTTP = 0, + NOT_SPECIAL = 1, + HTTPS = 2, + WS = 3, + FTP = 4, + WSS = 5, + FILE = 6 +}; + +/** + * A special scheme is an ASCII string that is listed in the first column of the + * following table. The default port for a special scheme is listed in the + * second column on the same row. The default port for any other ASCII string is + * null. + * + * @see https://url.spec.whatwg.org/#url-miscellaneous + * @param scheme + * @return If scheme is a special scheme + */ +ada_really_inline constexpr bool is_special(std::string_view scheme); + +/** + * A special scheme is an ASCII string that is listed in the first column of the + * following table. The default port for a special scheme is listed in the + * second column on the same row. The default port for any other ASCII string is + * null. + * + * @see https://url.spec.whatwg.org/#url-miscellaneous + * @param scheme + * @return The special port + */ +constexpr uint16_t get_special_port(std::string_view scheme) noexcept; + +/** + * Returns the port number of a special scheme. + * @see https://url.spec.whatwg.org/#special-scheme + */ +constexpr uint16_t get_special_port(ada::scheme::type type) noexcept; +/** + * Returns the scheme of an input, or NOT_SPECIAL if it's not a special scheme + * defined by the spec. + */ +constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; + +} // namespace ada::scheme + +#endif // ADA_SCHEME_H +/* end file include/ada/scheme.h */ #include +#include namespace ada { /** - * @see https://url.spec.whatwg.org/#url-parsing + * Type of URL host as an enum. */ -enum class state { +enum url_host_type : uint8_t { /** - * @see https://url.spec.whatwg.org/#authority-state + * Represents common URLs such as "https://www.google.com" */ - AUTHORITY, - + DEFAULT = 0, /** - * @see https://url.spec.whatwg.org/#scheme-start-state + * Represents ipv4 addresses such as "http://127.0.0.1" */ - SCHEME_START, - + IPV4 = 1, /** - * @see https://url.spec.whatwg.org/#scheme-state + * Represents ipv6 addresses such as + * "http://[2001:db8:3333:4444:5555:6666:7777:8888]" */ - SCHEME, + IPV6 = 2, +}; - /** - * @see https://url.spec.whatwg.org/#host-state - */ - HOST, +/** + * @brief Base class of URL implementations + * + * @details A url_base contains a few attributes: is_valid, has_opaque_path and + * type. All non-trivial implementation details are in derived classes such as + * ada::url and ada::url_aggregator. + * + * It is an abstract class that cannot be instantiated directly. + */ +struct url_base { + virtual ~url_base() = default; /** - * @see https://url.spec.whatwg.org/#no-scheme-state + * Used for returning the validity from the result of the URL parser. */ - NO_SCHEME, + bool is_valid{true}; /** - * @see https://url.spec.whatwg.org/#fragment-state + * A URL has an opaque path if its path is a string. */ - FRAGMENT, + bool has_opaque_path{false}; /** - * @see https://url.spec.whatwg.org/#relative-state + * URL hosts type */ - RELATIVE_SCHEME, + url_host_type host_type = url_host_type::DEFAULT; /** - * @see https://url.spec.whatwg.org/#relative-slash-state + * @private */ - RELATIVE_SLASH, + ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; /** - * @see https://url.spec.whatwg.org/#file-state + * A URL is special if its scheme is a special scheme. A URL is not special if + * its scheme is not a special scheme. */ - FILE, + [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept; /** - * @see https://url.spec.whatwg.org/#file-host-state + * The origin getter steps are to return the serialization of this's URL's + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin */ - FILE_HOST, + [[nodiscard]] virtual std::string get_origin() const noexcept = 0; /** - * @see https://url.spec.whatwg.org/#file-slash-state + * Returns true if this URL has a valid domain as per RFC 1034 and + * corresponding specifications. Among other things, it requires + * that the domain string has fewer than 255 octets. */ - FILE_SLASH, + [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; /** - * @see https://url.spec.whatwg.org/#path-or-authority-state + * @private + * + * Return the 'special port' if the URL is special and not 'file'. + * Returns 0 otherwise. */ - PATH_OR_AUTHORITY, + [[nodiscard]] inline uint16_t get_special_port() const noexcept; /** - * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state + * @private + * + * Get the default port if the url's scheme has one, returns 0 otherwise. */ - SPECIAL_AUTHORITY_IGNORE_SLASHES, + [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; /** - * @see https://url.spec.whatwg.org/#special-authority-slashes-state + * @private + * + * Parse a port (16-bit decimal digit) from the provided input. + * We assume that the input does not contain spaces or tabs + * within the ASCII digits. + * It returns how many bytes were consumed when a number is successfully + * parsed. + * @return On failure, it returns zero. + * @see https://url.spec.whatwg.org/#host-parsing */ - SPECIAL_AUTHORITY_SLASHES, + virtual size_t parse_port(std::string_view view, + bool check_trailing_content) noexcept = 0; - /** - * @see https://url.spec.whatwg.org/#special-relative-or-authority-state - */ - SPECIAL_RELATIVE_OR_AUTHORITY, + virtual ada_really_inline size_t parse_port(std::string_view view) noexcept { + return this->parse_port(view, false); + } /** - * @see https://url.spec.whatwg.org/#query-state + * Returns a JSON string representation of this URL. */ - QUERY, + [[nodiscard]] virtual std::string to_string() const = 0; - /** - * @see https://url.spec.whatwg.org/#path-state - */ - PATH, + /** @private */ + virtual inline void clear_pathname() = 0; - /** - * @see https://url.spec.whatwg.org/#path-start-state - */ - PATH_START, + /** @private */ + virtual inline void clear_search() = 0; - /** - * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state - */ - OPAQUE_PATH, + /** @private */ + [[nodiscard]] virtual inline bool has_hash() const noexcept = 0; - /** - * @see https://url.spec.whatwg.org/#port-state - */ - PORT, -}; + /** @private */ + [[nodiscard]] virtual inline bool has_search() const noexcept = 0; -/** - * Stringify a URL state machine state. - */ -ada_warn_unused std::string to_string(ada::state s); +}; // url_base } // namespace ada -#endif // ADA_STATE_H -/* end file include/ada/state.h */ -/* begin file include/ada/url_base.h */ -/** - * @file url_base.h - * @brief Declaration for the basic URL definitions - */ -#ifndef ADA_URL_BASE_H -#define ADA_URL_BASE_H - -/* begin file include/ada/url_components.h */ -/** - * @file url_components.h - * @brief Declaration for the URL Components - */ -#ifndef ADA_URL_COMPONENTS_H -#define ADA_URL_COMPONENTS_H +#endif +/* end file include/ada/url_base.h */ -#include +#include #include +#include -namespace ada { +#if ADA_DEVELOPMENT_CHECKS +#include +#endif // ADA_DEVELOPMENT_CHECKS /** - * @brief URL Component representations using offsets. - * - * @details We design the url_components struct so that it is as small - * and simple as possible. This version uses 32 bytes. + * These functions are not part of our public API and may + * change at any time. * - * This struct is used to extract components from a single 'href'. + * @private + * @namespace ada::helpers + * @brief Includes the definitions for helper functions */ -struct url_components { - constexpr static uint32_t omitted = uint32_t(-1); - - url_components() = default; - url_components(const url_components& u) = default; - url_components(url_components&& u) noexcept = default; - url_components& operator=(url_components&& u) noexcept = default; - url_components& operator=(const url_components& u) = default; - ~url_components() = default; - - /* - * By using 32-bit integers, we implicitly assume that the URL string - * cannot exceed 4 GB. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - uint32_t protocol_end{0}; - /** - * Username end is not `omitted` by default to make username and password - * getters less costly to implement. - */ - uint32_t username_end{0}; - uint32_t host_start{0}; - uint32_t host_end{0}; - uint32_t port{omitted}; - uint32_t pathname_start{0}; - uint32_t search_start{omitted}; - uint32_t hash_start{omitted}; - - /** - * Check the following conditions: - * protocol_end < username_end < ... < hash_start, - * expect when a value is omitted. It also computes - * a lower bound on the possible string length that may match these - * offsets. - * @return true if the offset values are - * consistent with a possible URL string - */ - [[nodiscard]] bool check_offset_consistency() const noexcept; - - /** - * Converts a url_components to JSON stringified version. - */ - [[nodiscard]] std::string to_string() const; - -}; // struct url_components +namespace ada::helpers { -} // namespace ada -#endif -/* end file include/ada/url_components.h */ -/* begin file include/ada/scheme.h */ /** - * @file scheme.h - * @brief Declarations for the URL scheme. + * @private */ -#ifndef ADA_SCHEME_H -#define ADA_SCHEME_H - -#include -#include -#include +template +void encode_json(std::string_view view, out_iter out); /** - * @namespace ada::scheme - * @brief Includes the scheme declarations + * @private + * This function is used to prune a fragment from a url, and returning the + * removed string if input has fragment. + * + * @details prune_hash seeks the first '#' and returns everything after it + * as a string_view, and modifies (in place) the input so that it points at + * everything before the '#'. If no '#' is found, the input is left unchanged + * and std::nullopt is returned. + * + * @attention The function is non-allocating and it does not throw. + * @returns Note that the returned string_view might be empty! */ -namespace ada::scheme { +ada_really_inline std::optional prune_hash( + std::string_view& input) noexcept; /** - * Type of the scheme as an enum. - * Using strings to represent a scheme type is not ideal because - * checking for types involves string comparisons. It is faster to use - * a simple integer. - * In C++11, we are allowed to specify the underlying type of the enum. - * We pick an 8-bit integer (which allows up to 256 types). Specifying the - * type of the enum may help integration with other systems if the type - * variable is exposed (since its value will not depend on the compiler). + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -enum type : uint8_t { - HTTP = 0, - NOT_SPECIAL = 1, - HTTPS = 2, - WS = 3, - FTP = 4, - WSS = 5, - FILE = 6 -}; +ada_really_inline bool shorten_path(std::string& path, + ada::scheme::type type) noexcept; /** - * A special scheme is an ASCII string that is listed in the first column of the - * following table. The default port for a special scheme is listed in the - * second column on the same row. The default port for any other ASCII string is - * null. - * - * @see https://url.spec.whatwg.org/#url-miscellaneous - * @param scheme - * @return If scheme is a special scheme + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -ada_really_inline constexpr bool is_special(std::string_view scheme); +ada_really_inline bool shorten_path(std::string_view& path, + ada::scheme::type type) noexcept; /** - * A special scheme is an ASCII string that is listed in the first column of the - * following table. The default port for a special scheme is listed in the - * second column on the same row. The default port for any other ASCII string is - * null. + * @private * - * @see https://url.spec.whatwg.org/#url-miscellaneous - * @param scheme - * @return The special port + * Parse the path from the provided input and append to the existing + * (possibly empty) path. The input cannot contain tabs and spaces: it + * is the user's responsibility to check. + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ */ -constexpr uint16_t get_special_port(std::string_view scheme) noexcept; +ada_really_inline void parse_prepared_path(std::string_view input, + ada::scheme::type type, + std::string& path); /** - * Returns the port number of a special scheme. - * @see https://url.spec.whatwg.org/#special-scheme + * @private + * Remove and mutate all ASCII tab or newline characters from an input. */ -constexpr uint16_t get_special_port(ada::scheme::type type) noexcept; +ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; + /** - * Returns the scheme of an input, or NOT_SPECIAL if it's not a special scheme - * defined by the spec. + * @private + * Return the substring from input going from index pos to the end. + * This function cannot throw. */ -constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; - -} // namespace ada::scheme - -#endif // ADA_SCHEME_H -/* end file include/ada/scheme.h */ - -#include - -namespace ada { - -/** - * Type of URL host as an enum. - */ -enum url_host_type : uint8_t { - /** - * Represents common URLs such as "https://www.google.com" - */ - DEFAULT = 0, - /** - * Represents ipv4 addresses such as "http://127.0.0.1" - */ - IPV4 = 1, - /** - * Represents ipv6 addresses such as - * "http://[2001:db8:3333:4444:5555:6666:7777:8888]" - */ - IPV6 = 2, -}; - -/** - * @brief Base class of URL implementations - * - * @details A url_base contains a few attributes: is_valid, has_opaque_path and - * type. All non-trivial implementation details are in derived classes such as - * ada::url and ada::url_aggregator. - * - * It is an abstract class that cannot be instantiated directly. - */ -struct url_base { - virtual ~url_base() = default; - - /** - * Used for returning the validity from the result of the URL parser. - */ - bool is_valid{true}; - - /** - * A URL has an opaque path if its path is a string. - */ - bool has_opaque_path{false}; - - /** - * URL hosts type - */ - url_host_type host_type = url_host_type::DEFAULT; - - /** - * @private - */ - ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; - - /** - * A URL is special if its scheme is a special scheme. A URL is not special if - * its scheme is not a special scheme. - */ - [[nodiscard]] ada_really_inline bool is_special() const noexcept; - - /** - * The origin getter steps are to return the serialization of this's URL's - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] virtual std::string get_origin() const noexcept = 0; - - /** - * Returns true if this URL has a valid domain as per RFC 1034 and - * corresponding specifications. Among other things, it requires - * that the domain string has fewer than 255 octets. - */ - [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; - - /** - * @private - * - * Return the 'special port' if the URL is special and not 'file'. - * Returns 0 otherwise. - */ - [[nodiscard]] inline uint16_t get_special_port() const noexcept; - - /** - * @private - * - * Get the default port if the url's scheme has one, returns 0 otherwise. - */ - [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; - - /** - * @private - * - * Parse a port (16-bit decimal digit) from the provided input. - * We assume that the input does not contain spaces or tabs - * within the ASCII digits. - * It returns how many bytes were consumed when a number is successfully - * parsed. - * @return On failure, it returns zero. - * @see https://url.spec.whatwg.org/#host-parsing - */ - virtual size_t parse_port(std::string_view view, - bool check_trailing_content) noexcept = 0; - - virtual ada_really_inline size_t parse_port(std::string_view view) noexcept { - return this->parse_port(view, false); - } - - /** - * Returns a JSON string representation of this URL. - */ - [[nodiscard]] virtual std::string to_string() const = 0; - - /** @private */ - virtual inline void clear_pathname() = 0; - - /** @private */ - virtual inline void clear_search() = 0; - - /** @private */ - [[nodiscard]] virtual inline bool has_hash() const noexcept = 0; - - /** @private */ - [[nodiscard]] virtual inline bool has_search() const noexcept = 0; - -}; // url_base - -} // namespace ada - -#endif -/* end file include/ada/url_base.h */ - -#include -#include - -/** - * These functions are not part of our public API and may - * change at any time. - * - * @private - * @namespace ada::helpers - * @brief Includes the definitions for helper functions - */ -namespace ada::helpers { - -/** - * @private - */ -template -void encode_json(std::string_view view, out_iter out); - -/** - * @private - * This function is used to prune a fragment from a url, and returning the - * removed string if input has fragment. - * - * @details prune_hash seeks the first '#' and returns everything after it - * as a string_view, and modifies (in place) the input so that it points at - * everything before the '#'. If no '#' is found, the input is left unchanged - * and std::nullopt is returned. - * - * @attention The function is non-allocating and it does not throw. - * @returns Note that the returned string_view might be empty! - */ -ada_really_inline std::optional prune_hash( - std::string_view& input) noexcept; - -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string& path, - ada::scheme::type type) noexcept; - -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string_view& path, - ada::scheme::type type) noexcept; - -/** - * @private - * - * Parse the path from the provided input and append to the existing - * (possibly empty) path. The input cannot contain tabs and spaces: it - * is the user's responsibility to check. - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ -ada_really_inline void parse_prepared_path(std::string_view input, - ada::scheme::type type, - std::string& path); - -/** - * @private - * Remove and mutate all ASCII tab or newline characters from an input. - */ -ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; - -/** - * @private - * Return the substring from input going from index pos to the end. - * This function cannot throw. - */ -ada_really_inline std::string_view substring(std::string_view input, - size_t pos) noexcept; +ada_really_inline constexpr std::string_view substring(std::string_view input, + size_t pos) noexcept; /** * @private @@ -1714,9 +1457,9 @@ bool overlaps(std::string_view input1, const std::string& input2) noexcept; * Return the substring from input going from index pos1 to the pos2 (non * included). The length of the substring is pos2 - pos1. */ -ada_really_inline std::string_view substring(const std::string& input, - size_t pos1, - size_t pos2) noexcept { +ada_really_inline constexpr std::string_view substring(std::string_view input, + size_t pos1, + size_t pos2) noexcept { #if ADA_DEVELOPMENT_CHECKS if (pos2 < pos1) { std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")" @@ -1724,7 +1467,7 @@ ada_really_inline std::string_view substring(const std::string& input, abort(); } #endif - return std::string_view(input.data() + pos1, pos2 - pos1); + return input.substr(pos1, pos2 - pos1); } /** @@ -1740,14 +1483,14 @@ ada_really_inline void resize(std::string_view& input, size_t pos) noexcept; * and whether a colon was found outside brackets. Used by the host parser. */ ada_really_inline std::pair get_host_delimiter_location( - const bool is_special, std::string_view& view) noexcept; + bool is_special, std::string_view& view) noexcept; /** * @private * Removes leading and trailing C0 control and whitespace characters from * string. */ -ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept; +void trim_c0_whitespace(std::string_view& input) noexcept; /** * @private @@ -1851,8 +1594,8 @@ inline int fast_digit_count(uint32_t x) noexcept { #ifndef ADA_PARSER_H #define ADA_PARSER_H -#include #include +#include /* begin file include/ada/expected.h */ /** @@ -2008,25 +1751,25 @@ class unexpected { static_assert(!std::is_same::value, "E must not be void"); unexpected() = delete; - constexpr explicit unexpected(const E& e) : m_val(e) {} + constexpr explicit unexpected(const E &e) : m_val(e) {} - constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {} + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} template ::value>::type* = nullptr> - constexpr explicit unexpected(Args&&... args) + E, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) : m_val(std::forward(args)...) {} template < class U, class... Args, typename std::enable_if&, Args&&...>::value>::type* = nullptr> - constexpr explicit unexpected(std::initializer_list l, Args&&... args) + E, std::initializer_list &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) : m_val(l, std::forward(args)...) {} - constexpr const E& value() const& { return m_val; } - TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); } - constexpr const E&& value() const&& { return std::move(m_val); } + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } private: E m_val; @@ -2038,32 +1781,32 @@ unexpected(E) -> unexpected; #endif template -constexpr bool operator==(const unexpected& lhs, const unexpected& rhs) { +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { return lhs.value() == rhs.value(); } template -constexpr bool operator!=(const unexpected& lhs, const unexpected& rhs) { +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() != rhs.value(); } template -constexpr bool operator<(const unexpected& lhs, const unexpected& rhs) { +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { return lhs.value() < rhs.value(); } template -constexpr bool operator<=(const unexpected& lhs, const unexpected& rhs) { +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() <= rhs.value(); } template -constexpr bool operator>(const unexpected& lhs, const unexpected& rhs) { +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { return lhs.value() > rhs.value(); } template -constexpr bool operator>=(const unexpected& lhs, const unexpected& rhs) { +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() >= rhs.value(); } template -unexpected::type> make_unexpected(E&& e) { +unexpected::type> make_unexpected(E &&e) { return unexpected::type>(std::forward(e)); } @@ -2074,7 +1817,7 @@ static constexpr unexpect_t unexpect{}; namespace detail { template -[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) { +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED throw std::forward(e); #else @@ -2124,7 +1867,7 @@ template struct is_pointer_to_non_const_member_func : std::true_type {}; template -struct is_pointer_to_non_const_member_func +struct is_pointer_to_non_const_member_func : std::true_type {}; template struct is_pointer_to_non_const_member_func @@ -2133,16 +1876,16 @@ template struct is_pointer_to_non_const_member_func : std::true_type {}; template -struct is_pointer_to_non_const_member_func +struct is_pointer_to_non_const_member_func : std::true_type {}; template -struct is_pointer_to_non_const_member_func +struct is_pointer_to_non_const_member_func : std::true_type {}; template struct is_const_or_const_ref : std::false_type {}; template -struct is_const_or_const_ref : std::true_type {}; +struct is_const_or_const_ref : std::true_type {}; template struct is_const_or_const_ref : std::true_type {}; #endif @@ -2156,7 +1899,7 @@ template < is_const_or_const_ref::value)>, #endif typename = enable_if_t>::value>, int = 0> -constexpr auto invoke(Fn&& f, Args&&... args) noexcept( +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( noexcept(std::mem_fn(f)(std::forward(args)...))) -> decltype(std::mem_fn(f)(std::forward(args)...)) { return std::mem_fn(f)(std::forward(args)...); @@ -2164,7 +1907,7 @@ constexpr auto invoke(Fn&& f, Args&&... args) noexcept( template >::value>> -constexpr auto invoke(Fn&& f, Args&&... args) noexcept( +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) { return std::forward(f)(std::forward(args)...); @@ -2204,7 +1947,7 @@ namespace swap_adl_tests { struct tag {}; template -tag swap(T&, T&); +tag swap(T &, T &); template tag swap(T (&a)[N], T (&b)[N]); @@ -2213,14 +1956,14 @@ tag swap(T (&a)[N], T (&b)[N]); template std::false_type can_swap(...) noexcept(false); template (), std::declval()))> -std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); + class = decltype(swap(std::declval(), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); template std::false_type uses_std(...); template -std::is_same(), std::declval())), tag> +std::is_same(), std::declval())), tag> uses_std(int); template @@ -2251,8 +1994,8 @@ struct is_swappable : std::integral_constant< bool, decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype( - detail::swap_adl_tests::uses_std(0))::value || + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || is_swappable::value)> {}; template @@ -2260,12 +2003,10 @@ struct is_nothrow_swappable : std::integral_constant< bool, is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value&& - detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value&& - detail::swap_adl_tests::is_adl_swap_noexcept::value))> { -}; + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; #endif #endif @@ -2279,7 +2020,7 @@ using is_expected = is_expected_impl>; template using expected_enable_forward_value = detail::enable_if_t< - std::is_constructible::value && + std::is_constructible::value && !std::is_same, in_place_t>::value && !std::is_same, detail::decay_t>::value && !std::is_same, detail::decay_t>::value>; @@ -2288,14 +2029,14 @@ template using expected_enable_from_other = detail::enable_if_t< std::is_constructible::value && std::is_constructible::value && - !std::is_constructible&>::value && - !std::is_constructible&&>::value && - !std::is_constructible&>::value && - !std::is_constructible&&>::value && - !std::is_convertible&, T>::value && - !std::is_convertible&&, T>::value && - !std::is_convertible&, T>::value && - !std::is_convertible&&, T>::value>; + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; template using is_void_or = conditional_t::value, std::true_type, U>; @@ -2333,29 +2074,29 @@ struct expected_storage_base { constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args&&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args&&...>::value>* = nullptr> + T, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -2381,29 +2122,29 @@ struct expected_storage_base { constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args&&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args&&...>::value>* = nullptr> + T, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() = default; @@ -2423,29 +2164,29 @@ struct expected_storage_base { : m_no_init(), m_has_val(false) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args&&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args&&...>::value>* = nullptr> + T, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -2469,29 +2210,29 @@ struct expected_storage_base { constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args&&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args&&...>::value>* = nullptr> + T, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -2522,17 +2263,17 @@ struct expected_storage_base { constexpr expected_storage_base(in_place_t) : m_has_val(true) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() = default; @@ -2553,17 +2294,17 @@ struct expected_storage_base { constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -2586,19 +2327,19 @@ struct expected_operations_base : expected_storage_base { using expected_storage_base::expected_storage_base; template - void construct(Args&&... args) noexcept { + void construct(Args &&...args) noexcept { new (std::addressof(this->m_val)) T(std::forward(args)...); this->m_has_val = true; } template - void construct_with(Rhs&& rhs) noexcept { + void construct_with(Rhs &&rhs) noexcept { new (std::addressof(this->m_val)) T(std::forward(rhs).get()); this->m_has_val = true; } template - void construct_error(Args&&... args) noexcept { + void construct_error(Args &&...args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); this->m_has_val = false; @@ -2613,9 +2354,9 @@ struct expected_operations_base : expected_storage_base { // This overload handles the case where we can just copy-construct `T` // directly into place without throwing. template ::value>* = - nullptr> - void assign(const expected_operations_base& rhs) noexcept { + detail::enable_if_t::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { if (!this->m_has_val && rhs.m_has_val) { geterr().~unexpected(); construct(rhs.get()); @@ -2628,9 +2369,9 @@ struct expected_operations_base : expected_storage_base { // `T`, then no-throw move it into place if the copy was successful. template ::value && - std::is_nothrow_move_constructible::value>* = - nullptr> - void assign(const expected_operations_base& rhs) noexcept { + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { if (!this->m_has_val && rhs.m_has_val) { T tmp = rhs.get(); geterr().~unexpected(); @@ -2646,10 +2387,10 @@ struct expected_operations_base : expected_storage_base { // then we move the old unexpected value back into place before rethrowing the // exception. template ::value && - !std::is_nothrow_move_constructible::value>* = nullptr> - void assign(const expected_operations_base& rhs) { + detail::enable_if_t::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { if (!this->m_has_val && rhs.m_has_val) { auto tmp = std::move(geterr()); geterr().~unexpected(); @@ -2671,9 +2412,9 @@ struct expected_operations_base : expected_storage_base { // These overloads do the same as above, but for rvalues template ::value>* = - nullptr> - void assign(expected_operations_base&& rhs) noexcept { + detail::enable_if_t::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { if (!this->m_has_val && rhs.m_has_val) { geterr().~unexpected(); construct(std::move(rhs).get()); @@ -2683,9 +2424,9 @@ struct expected_operations_base : expected_storage_base { } template ::value>* = nullptr> - void assign(expected_operations_base&& rhs) { + detail::enable_if_t::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { if (!this->m_has_val && rhs.m_has_val) { auto tmp = std::move(geterr()); geterr().~unexpected(); @@ -2707,7 +2448,7 @@ struct expected_operations_base : expected_storage_base { #else // If exceptions are disabled then we can just copy-construct - void assign(const expected_operations_base& rhs) noexcept { + void assign(const expected_operations_base &rhs) noexcept { if (!this->m_has_val && rhs.m_has_val) { geterr().~unexpected(); construct(rhs.get()); @@ -2716,7 +2457,7 @@ struct expected_operations_base : expected_storage_base { } } - void assign(expected_operations_base&& rhs) noexcept { + void assign(expected_operations_base &&rhs) noexcept { if (!this->m_has_val && rhs.m_has_val) { geterr().~unexpected(); construct(std::move(rhs).get()); @@ -2729,7 +2470,7 @@ struct expected_operations_base : expected_storage_base { // The common part of move/copy assigning template - void assign_common(Rhs&& rhs) { + void assign_common(Rhs &&rhs) { if (this->m_has_val) { if (rhs.m_has_val) { get() = std::forward(rhs).get(); @@ -2746,22 +2487,22 @@ struct expected_operations_base : expected_storage_base { bool has_value() const { return this->m_has_val; } - TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; } - constexpr const T& get() const& { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); } + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } #ifndef TL_EXPECTED_NO_CONSTRR - constexpr const T&& get() const&& { return std::move(this->m_val); } + constexpr const T &&get() const && { return std::move(this->m_val); } #endif - TL_EXPECTED_11_CONSTEXPR unexpected& geterr() & { + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { return this->m_unexpect; } - constexpr const unexpected& geterr() const& { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected&& geterr() && { + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { return std::move(this->m_unexpect); } #ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected&& geterr() const&& { + constexpr const unexpected &&geterr() const && { return std::move(this->m_unexpect); } #endif @@ -2783,19 +2524,19 @@ struct expected_operations_base : expected_storage_base { // This function doesn't use its argument, but needs it so that code in // levels above this can work independently of whether T is void template - void construct_with(Rhs&&) noexcept { + void construct_with(Rhs &&) noexcept { this->m_has_val = true; } template - void construct_error(Args&&... args) noexcept { + void construct_error(Args &&...args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); this->m_has_val = false; } template - void assign(Rhs&& rhs) noexcept { + void assign(Rhs &&rhs) noexcept { if (!this->m_has_val) { if (rhs.m_has_val) { geterr().~unexpected(); @@ -2812,15 +2553,15 @@ struct expected_operations_base : expected_storage_base { bool has_value() const { return this->m_has_val; } - TL_EXPECTED_11_CONSTEXPR unexpected& geterr() & { + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { return this->m_unexpect; } - constexpr const unexpected& geterr() const& { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected&& geterr() && { + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { return std::move(this->m_unexpect); } #ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected&& geterr() const&& { + constexpr const unexpected &&geterr() const && { return std::move(this->m_unexpect); } #endif @@ -2833,8 +2574,9 @@ struct expected_operations_base : expected_storage_base { // This class manages conditionally having a trivial copy constructor // This specialization is for when T and E are trivially copy constructible template :: - value&& TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> + bool = is_void_or::value && + TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> struct expected_copy_base : expected_operations_base { using expected_operations_base::expected_operations_base; }; @@ -2845,7 +2587,7 @@ struct expected_copy_base : expected_operations_base { using expected_operations_base::expected_operations_base; expected_copy_base() = default; - expected_copy_base(const expected_copy_base& rhs) + expected_copy_base(const expected_copy_base &rhs) : expected_operations_base(no_init) { if (rhs.has_value()) { this->construct_with(rhs); @@ -2854,9 +2596,9 @@ struct expected_copy_base : expected_operations_base { } } - expected_copy_base(expected_copy_base&& rhs) = default; - expected_copy_base& operator=(const expected_copy_base& rhs) = default; - expected_copy_base& operator=(expected_copy_base&& rhs) = default; + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; }; // This class manages conditionally having a trivial move constructor @@ -2866,7 +2608,8 @@ struct expected_copy_base : expected_operations_base { // move constructible #ifndef TL_EXPECTED_GCC49 template >::value&& + bool = + is_void_or>::value && std::is_trivially_move_constructible::value> struct expected_move_base : expected_copy_base { using expected_copy_base::expected_copy_base; @@ -2880,9 +2623,9 @@ struct expected_move_base : expected_copy_base { using expected_copy_base::expected_copy_base; expected_move_base() = default; - expected_move_base(const expected_move_base& rhs) = default; + expected_move_base(const expected_move_base &rhs) = default; - expected_move_base(expected_move_base&& rhs) noexcept( + expected_move_base(expected_move_base &&rhs) noexcept( std::is_nothrow_move_constructible::value) : expected_copy_base(no_init) { if (rhs.has_value()) { @@ -2891,19 +2634,21 @@ struct expected_move_base : expected_copy_base { this->construct_error(std::move(rhs.geterr())); } } - expected_move_base& operator=(const expected_move_base& rhs) = default; - expected_move_base& operator=(expected_move_base&& rhs) = default; + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; }; // This class manages conditionally having a trivial copy assignment operator -template >::value&& - TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value&& - TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value&& - TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +template < + class T, class E, + bool = + is_void_or< + T, conjunction>::value && + TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value && + TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value && + TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> struct expected_copy_assign_base : expected_move_base { using expected_move_base::expected_move_base; }; @@ -2913,14 +2658,14 @@ struct expected_copy_assign_base : expected_move_base { using expected_move_base::expected_move_base; expected_copy_assign_base() = default; - expected_copy_assign_base(const expected_copy_assign_base& rhs) = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; - expected_copy_assign_base(expected_copy_assign_base&& rhs) = default; - expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) { + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { this->assign(rhs); return *this; } - expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) = + expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) = default; }; @@ -2930,14 +2675,15 @@ struct expected_copy_assign_base : expected_move_base { // to make do with a non-trivial move assignment operator even if T is trivially // move assignable #ifndef TL_EXPECTED_GCC49 -template , - std::is_trivially_move_constructible, - std::is_trivially_move_assignable>>:: - value&& std::is_trivially_destructible::value&& - std::is_trivially_move_constructible::value&& - std::is_trivially_move_assignable::value> +template < + class T, class E, + bool = is_void_or< + T, conjunction, + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>::value && + std::is_trivially_destructible::value && + std::is_trivially_move_constructible::value && + std::is_trivially_move_assignable::value> struct expected_move_assign_base : expected_copy_assign_base { using expected_copy_assign_base::expected_copy_assign_base; }; @@ -2952,17 +2698,17 @@ struct expected_move_assign_base using expected_copy_assign_base::expected_copy_assign_base; expected_move_assign_base() = default; - expected_move_assign_base(const expected_move_assign_base& rhs) = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; - expected_move_assign_base(expected_move_assign_base&& rhs) = default; + expected_move_assign_base(expected_move_assign_base &&rhs) = default; - expected_move_assign_base& operator=(const expected_move_assign_base& rhs) = + expected_move_assign_base &operator=(const expected_move_assign_base &rhs) = default; - expected_move_assign_base& - operator=(expected_move_assign_base&& rhs) noexcept( - std::is_nothrow_move_constructible::value&& - std::is_nothrow_move_assignable::value) { + expected_move_assign_base &operator=( + expected_move_assign_base + &&rhs) noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value) { this->assign(std::move(rhs)); return *this; } @@ -2977,44 +2723,44 @@ template ::value)> struct expected_delete_ctor_base { expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = default; }; template struct expected_delete_ctor_base { expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = default; }; template struct expected_delete_ctor_base { expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = default; }; template struct expected_delete_ctor_base { expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = default; }; @@ -3032,45 +2778,49 @@ template ::value)> struct expected_delete_assign_base { expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = default; - expected_delete_assign_base& operator=( - expected_delete_assign_base&&) noexcept = default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = default; }; template struct expected_delete_assign_base { expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = default; - expected_delete_assign_base& operator=( - expected_delete_assign_base&&) noexcept = delete; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = delete; }; template struct expected_delete_assign_base { expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = delete; - expected_delete_assign_base& operator=( - expected_delete_assign_base&&) noexcept = default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = default; }; template struct expected_delete_assign_base { expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = delete; - expected_delete_assign_base& operator=( - expected_delete_assign_base&&) noexcept = delete; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = delete; }; // This is needed to be able to construct the expected_default_ctor_base which @@ -3088,13 +2838,13 @@ template struct expected_default_ctor_base { constexpr expected_default_ctor_base() noexcept = delete; constexpr expected_default_ctor_base( - expected_default_ctor_base const&) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = - default; - expected_default_ctor_base& operator=( - expected_default_ctor_base const&) noexcept = default; - expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = default; + expected_default_ctor_base &operator=( + expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base &operator=( + expected_default_ctor_base &&) noexcept = default; constexpr explicit expected_default_ctor_base(default_constructor_tag) {} }; @@ -3121,14 +2871,14 @@ class bad_expected_access : public std::exception { public: explicit bad_expected_access(E e) : m_val(std::move(e)) {} - virtual const char* what() const noexcept override { + virtual const char *what() const noexcept override { return "Bad expected access"; } - const E& error() const& { return m_val; } - E& error() & { return m_val; } - const E&& error() const&& { return std::move(m_val); } - E&& error() && { return std::move(m_val); } + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } private: E m_val; @@ -3156,26 +2906,26 @@ class expected : private detail::expected_move_assign_base, "T must not be unexpected"); static_assert(!std::is_reference::value, "E must not be a reference"); - T* valptr() { return std::addressof(this->m_val); } - const T* valptr() const { return std::addressof(this->m_val); } - unexpected* errptr() { return std::addressof(this->m_unexpect); } - const unexpected* errptr() const { + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { return std::addressof(this->m_unexpect); } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR U& val() { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR unexpected& err() { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } template ::value>* = nullptr> - constexpr const U& val() const { + detail::enable_if_t::value> * = nullptr> + constexpr const U &val() const { return this->m_val; } - constexpr const unexpected& err() const { return this->m_unexpect; } + constexpr const unexpected &err() const { return this->m_unexpect; } using impl_base = detail::expected_move_assign_base; using ctor_base = detail::expected_default_ctor_base; @@ -3188,46 +2938,46 @@ class expected : private detail::expected_move_assign_base, #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template - TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & { + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { return and_then_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && { + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { return and_then_impl(std::move(*this), std::forward(f)); } template - constexpr auto and_then(F&& f) const& { + constexpr auto and_then(F &&f) const & { return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr auto and_then(F&& f) const&& { + constexpr auto and_then(F &&f) const && { return and_then_impl(std::move(*this), std::forward(f)); } #endif #else template - TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } template - constexpr auto and_then(F&& f) const& -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr auto and_then(F&& f) const&& -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } #endif @@ -3236,46 +2986,46 @@ class expected : private detail::expected_move_assign_base, #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template - TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & { + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && { + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } template - constexpr auto map(F&& f) const& { + constexpr auto map(F &&f) const & { return expected_map_impl(*this, std::forward(f)); } template - constexpr auto map(F&& f) const&& { + constexpr auto map(F &&f) const && { return expected_map_impl(std::move(*this), std::forward(f)); } #else template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - map(F&& f) & { + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - map(F&& f) && { + std::declval())) + map(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F&& f) const& { + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { return expected_map_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F&& f) const&& { + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { return expected_map_impl(std::move(*this), std::forward(f)); } #endif @@ -3284,46 +3034,46 @@ class expected : private detail::expected_move_assign_base, #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template - TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & { + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && { + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } template - constexpr auto transform(F&& f) const& { + constexpr auto transform(F &&f) const & { return expected_map_impl(*this, std::forward(f)); } template - constexpr auto transform(F&& f) const&& { + constexpr auto transform(F &&f) const && { return expected_map_impl(std::move(*this), std::forward(f)); } #else template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F&& f) & { + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F&& f) && { + std::declval())) + transform(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F&& f) const& { + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { return expected_map_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F&& f) const&& { + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { return expected_map_impl(std::move(*this), std::forward(f)); } #endif @@ -3332,46 +3082,46 @@ class expected : private detail::expected_move_assign_base, #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template - TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & { + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { return map_error_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && { + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { return map_error_impl(std::move(*this), std::forward(f)); } template - constexpr auto map_error(F&& f) const& { + constexpr auto map_error(F &&f) const & { return map_error_impl(*this, std::forward(f)); } template - constexpr auto map_error(F&& f) const&& { + constexpr auto map_error(F &&f) const && { return map_error_impl(std::move(*this), std::forward(f)); } #else template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F&& f) & { + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { return map_error_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F&& f) && { + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { return map_error_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F&& f) const& { + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { return map_error_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F&& f) const&& { + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { return map_error_impl(std::move(*this), std::forward(f)); } #endif @@ -3379,147 +3129,149 @@ class expected : private detail::expected_move_assign_base, #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template - TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & { + TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { return map_error_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && { + TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { return map_error_impl(std::move(*this), std::forward(f)); } template - constexpr auto transform_error(F&& f) const& { + constexpr auto transform_error(F &&f) const & { return map_error_impl(*this, std::forward(f)); } template - constexpr auto transform_error(F&& f) const&& { + constexpr auto transform_error(F &&f) const && { return map_error_impl(std::move(*this), std::forward(f)); } #else template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F&& f) & { + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { return map_error_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F&& f) && { + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { return map_error_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F&& f) const& { + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { return map_error_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F&& f) const&& { + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { return map_error_impl(std::move(*this), std::forward(f)); } #endif #endif template - expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & { + expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { return or_else_impl(*this, std::forward(f)); } template - expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && { + expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { return or_else_impl(std::move(*this), std::forward(f)); } template - expected constexpr or_else(F&& f) const& { + expected constexpr or_else(F &&f) const & { return or_else_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - expected constexpr or_else(F&& f) const&& { + expected constexpr or_else(F &&f) const && { return or_else_impl(std::move(*this), std::forward(f)); } #endif constexpr expected() = default; - constexpr expected(const expected& rhs) = default; - constexpr expected(expected&& rhs) = default; - expected& operator=(const expected& rhs) = default; - expected& operator=(expected&& rhs) = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr expected(in_place_t, Args&&... args) + constexpr expected(in_place_t, Args &&...args) : impl_base(in_place, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} template &, Args&&...>::value>* = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args&&... args) + T, std::initializer_list &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) : impl_base(in_place, il, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} - template < - class G = E, - detail::enable_if_t::value>* = nullptr, - detail::enable_if_t::value>* = nullptr> - explicit constexpr expected(const unexpected& e) + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) : impl_base(unexpect, e.value()), ctor_base(detail::default_constructor_tag{}) {} template < class G = E, - detail::enable_if_t::value>* = nullptr, - detail::enable_if_t::value>* = nullptr> - constexpr expected(unexpected const& e) + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) : impl_base(unexpect, e.value()), ctor_base(detail::default_constructor_tag{}) {} template < class G = E, - detail::enable_if_t::value>* = nullptr, - detail::enable_if_t::value>* = nullptr> - explicit constexpr expected(unexpected&& e) noexcept( - std::is_nothrow_constructible::value) + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) : impl_base(unexpect, std::move(e.value())), ctor_base(detail::default_constructor_tag{}) {} template < class G = E, - detail::enable_if_t::value>* = nullptr, - detail::enable_if_t::value>* = nullptr> - constexpr expected(unexpected&& e) noexcept( - std::is_nothrow_constructible::value) + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) : impl_base(unexpect, std::move(e.value())), ctor_base(detail::default_constructor_tag{}) {} template ::value>* = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected(unexpect_t, Args&&... args) + constexpr explicit expected(unexpect_t, Args &&...args) : impl_base(unexpect, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} template &, Args&&...>::value>* = nullptr> + E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected(unexpect_t, std::initializer_list il, - Args&&... args) + Args &&...args) : impl_base(unexpect, il, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} template ::value && - std::is_convertible::value)>* = + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other* = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(const expected& rhs) + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(*rhs); @@ -3528,13 +3280,13 @@ class expected : private detail::expected_move_assign_base, } } - template < - class U, class G, - detail::enable_if_t<(std::is_convertible::value && - std::is_convertible::value)>* = nullptr, - detail::expected_enable_from_other* = - nullptr> - TL_EXPECTED_11_CONSTEXPR expected(const expected& rhs) + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(*rhs); @@ -3545,10 +3297,10 @@ class expected : private detail::expected_move_assign_base, template < class U, class G, - detail::enable_if_t::value && - std::is_convertible::value)>* = nullptr, - detail::expected_enable_from_other* = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(expected&& rhs) + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(std::move(*rhs)); @@ -3559,10 +3311,10 @@ class expected : private detail::expected_move_assign_base, template < class U, class G, - detail::enable_if_t<(std::is_convertible::value && - std::is_convertible::value)>* = nullptr, - detail::expected_enable_from_other* = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(expected&& rhs) + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(std::move(*rhs)); @@ -3571,31 +3323,33 @@ class expected : private detail::expected_move_assign_base, } } - template ::value>* = nullptr, - detail::expected_enable_forward_value* = nullptr> - explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v) + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) : expected(in_place, std::forward(v)) {} - template ::value>* = nullptr, - detail::expected_enable_forward_value* = nullptr> - TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v) + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) : expected(in_place, std::forward(v)) {} template < class U = T, class G = T, - detail::enable_if_t::value>* = + detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value>* = nullptr, + detail::enable_if_t::value> * = nullptr, detail::enable_if_t< (!std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)>* = nullptr> - expected& operator=(U&& v) { + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { if (has_value()) { val() = std::forward(v); } else { @@ -3609,17 +3363,17 @@ class expected : private detail::expected_move_assign_base, template < class U = T, class G = T, - detail::enable_if_t::value>* = + detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value>* = nullptr, + detail::enable_if_t::value> * = nullptr, detail::enable_if_t< (!std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)>* = nullptr> - expected& operator=(U&& v) { + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { if (has_value()) { val() = std::forward(v); } else { @@ -3645,8 +3399,8 @@ class expected : private detail::expected_move_assign_base, template ::value && - std::is_assignable::value>* = nullptr> - expected& operator=(const unexpected& rhs) { + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { if (!has_value()) { err() = rhs; } else { @@ -3660,8 +3414,8 @@ class expected : private detail::expected_move_assign_base, template ::value && - std::is_move_assignable::value>* = nullptr> - expected& operator=(unexpected&& rhs) noexcept { + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { if (!has_value()) { err() = std::move(rhs); } else { @@ -3674,8 +3428,8 @@ class expected : private detail::expected_move_assign_base, } template ::value>* = nullptr> - void emplace(Args&&... args) { + T, Args &&...>::value> * = nullptr> + void emplace(Args &&...args) { if (has_value()) { val().~T(); } else { @@ -3686,8 +3440,8 @@ class expected : private detail::expected_move_assign_base, } template ::value>* = nullptr> - void emplace(Args&&... args) { + T, Args &&...>::value> * = nullptr> + void emplace(Args &&...args) { if (has_value()) { val().~T(); ::new (valptr()) T(std::forward(args)...); @@ -3712,8 +3466,8 @@ class expected : private detail::expected_move_assign_base, template &, Args&&...>::value>* = nullptr> - void emplace(std::initializer_list il, Args&&... args) { + T, std::initializer_list &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { if (has_value()) { T t(il, std::forward(args)...); val() = std::move(t); @@ -3726,8 +3480,8 @@ class expected : private detail::expected_move_assign_base, template &, Args&&...>::value>* = nullptr> - void emplace(std::initializer_list il, Args&&... args) { + T, std::initializer_list &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { if (has_value()) { T t(il, std::forward(args)...); val() = std::move(t); @@ -3758,30 +3512,30 @@ class expected : private detail::expected_move_assign_base, using e_is_nothrow_move_constructible = std::true_type; using move_constructing_e_can_throw = std::false_type; - void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept { + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { // swapping void is a no-op } - void swap_where_both_have_value(expected& rhs, t_is_not_void) { + void swap_where_both_have_value(expected &rhs, t_is_not_void) { using std::swap; swap(val(), rhs.val()); } - void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept( + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( std::is_nothrow_move_constructible::value) { ::new (errptr()) unexpected_type(std::move(rhs.err())); rhs.err().~unexpected_type(); std::swap(this->m_has_val, rhs.m_has_val); } - void swap_where_only_one_has_value(expected& rhs, t_is_not_void) { + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { swap_where_only_one_has_value_and_t_is_not_void( rhs, typename std::is_nothrow_move_constructible::type{}, typename std::is_nothrow_move_constructible::type{}); } void swap_where_only_one_has_value_and_t_is_not_void( - expected& rhs, t_is_nothrow_move_constructible, + expected &rhs, t_is_nothrow_move_constructible, e_is_nothrow_move_constructible) noexcept { auto temp = std::move(val()); val().~T(); @@ -3792,7 +3546,7 @@ class expected : private detail::expected_move_assign_base, } void swap_where_only_one_has_value_and_t_is_not_void( - expected& rhs, t_is_nothrow_move_constructible, + expected &rhs, t_is_nothrow_move_constructible, move_constructing_e_can_throw) { auto temp = std::move(val()); val().~T(); @@ -3815,7 +3569,7 @@ class expected : private detail::expected_move_assign_base, } void swap_where_only_one_has_value_and_t_is_not_void( - expected& rhs, move_constructing_t_can_throw, + expected &rhs, move_constructing_t_can_throw, e_is_nothrow_move_constructible) { auto temp = std::move(rhs.err()); rhs.err().~unexpected_type(); @@ -3843,11 +3597,10 @@ class expected : private detail::expected_move_assign_base, detail::is_swappable::value && (std::is_nothrow_move_constructible::value || std::is_nothrow_move_constructible::value)> - swap(expected& rhs) noexcept( - std::is_nothrow_move_constructible::value&& - detail::is_nothrow_swappable::value&& - std::is_nothrow_move_constructible::value&& - detail::is_nothrow_swappable::value) { + swap(expected &rhs) noexcept(std::is_nothrow_move_constructible::value && + detail::is_nothrow_swappable::value && + std::is_nothrow_move_constructible::value && + detail::is_nothrow_swappable::value) { if (has_value() && rhs.has_value()) { swap_where_both_have_value(rhs, typename std::is_void::type{}); } else if (!has_value() && rhs.has_value()) { @@ -3860,36 +3613,36 @@ class expected : private detail::expected_move_assign_base, } } - constexpr const T* operator->() const { + constexpr const T *operator->() const { TL_ASSERT(has_value()); return valptr(); } - TL_EXPECTED_11_CONSTEXPR T* operator->() { + TL_EXPECTED_11_CONSTEXPR T *operator->() { TL_ASSERT(has_value()); return valptr(); } template ::value>* = nullptr> - constexpr const U& operator*() const& { + detail::enable_if_t::value> * = nullptr> + constexpr const U &operator*() const & { TL_ASSERT(has_value()); return val(); } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR U& operator*() & { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { TL_ASSERT(has_value()); return val(); } template ::value>* = nullptr> - constexpr const U&& operator*() const&& { + detail::enable_if_t::value> * = nullptr> + constexpr const U &&operator*() const && { TL_ASSERT(has_value()); return std::move(val()); } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR U&& operator*() && { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { TL_ASSERT(has_value()); return std::move(val()); } @@ -3898,62 +3651,62 @@ class expected : private detail::expected_move_assign_base, constexpr explicit operator bool() const noexcept { return this->m_has_val; } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR const U& value() const& { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { if (!has_value()) detail::throw_exception(bad_expected_access(err().value())); return val(); } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR U& value() & { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { if (!has_value()) detail::throw_exception(bad_expected_access(err().value())); return val(); } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR const U&& value() const&& { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { if (!has_value()) detail::throw_exception(bad_expected_access(std::move(err()).value())); return std::move(val()); } template ::value>* = nullptr> - TL_EXPECTED_11_CONSTEXPR U&& value() && { + detail::enable_if_t::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { if (!has_value()) detail::throw_exception(bad_expected_access(std::move(err()).value())); return std::move(val()); } - constexpr const E& error() const& { + constexpr const E &error() const & { TL_ASSERT(!has_value()); return err().value(); } - TL_EXPECTED_11_CONSTEXPR E& error() & { + TL_EXPECTED_11_CONSTEXPR E &error() & { TL_ASSERT(!has_value()); return err().value(); } - constexpr const E&& error() const&& { + constexpr const E &&error() const && { TL_ASSERT(!has_value()); return std::move(err().value()); } - TL_EXPECTED_11_CONSTEXPR E&& error() && { + TL_EXPECTED_11_CONSTEXPR E &&error() && { TL_ASSERT(!has_value()); return std::move(err().value()); } template - constexpr T value_or(U&& v) const& { + constexpr T value_or(U &&v) const & { static_assert(std::is_copy_constructible::value && - std::is_convertible::value, + std::is_convertible::value, "T must be copy-constructible and convertible to from U&&"); return bool(*this) ? **this : static_cast(std::forward(v)); } template - TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && { + TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { static_assert(std::is_move_constructible::value && - std::is_convertible::value, + std::is_convertible::value, "T must be move-constructible and convertible to from U&&"); return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } @@ -3969,10 +3722,10 @@ using ret_t = expected>; #ifdef TL_EXPECTED_CXX14 template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval()))> -constexpr auto and_then_impl(Exp&& exp, F&& f) { +constexpr auto and_then_impl(Exp &&exp, F &&f) { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() @@ -3981,9 +3734,9 @@ constexpr auto and_then_impl(Exp&& exp, F&& f) { } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval()))> -constexpr auto and_then_impl(Exp&& exp, F&& f) { +constexpr auto and_then_impl(Exp &&exp, F &&f) { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() ? detail::invoke(std::forward(f)) @@ -3995,8 +3748,8 @@ struct TC; template (), *std::declval())), - detail::enable_if_t>::value>* = nullptr> -auto and_then_impl(Exp&& exp, F&& f) -> Ret { + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() @@ -4006,8 +3759,8 @@ auto and_then_impl(Exp&& exp, F&& f) -> Ret { template ())), - detail::enable_if_t>::value>* = nullptr> -constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret { + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() ? detail::invoke(std::forward(f)) @@ -4017,11 +3770,11 @@ constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret { #ifdef TL_EXPECTED_CXX14 template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), - detail::enable_if_t::value>* = nullptr> -constexpr auto expected_map_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { using result = ret_t>; return exp.has_value() ? result(detail::invoke(std::forward(f), *std::forward(exp))) @@ -4029,11 +3782,11 @@ constexpr auto expected_map_impl(Exp&& exp, F&& f) { } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), - detail::enable_if_t::value>* = nullptr> -auto expected_map_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { using result = expected>; if (exp.has_value()) { detail::invoke(std::forward(f), *std::forward(exp)); @@ -4044,20 +3797,20 @@ auto expected_map_impl(Exp&& exp, F&& f) { } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value>* = nullptr> -constexpr auto expected_map_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { using result = ret_t>; return exp.has_value() ? result(detail::invoke(std::forward(f))) : result(unexpect, std::forward(exp).error()); } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value>* = nullptr> -auto expected_map_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { using result = expected>; if (exp.has_value()) { detail::invoke(std::forward(f)); @@ -4068,12 +3821,12 @@ auto expected_map_impl(Exp&& exp, F&& f) { } #else template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), - detail::enable_if_t::value>* = nullptr> + detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp&& exp, F&& f) +constexpr auto expected_map_impl(Exp &&exp, F &&f) -> ret_t> { using result = ret_t>; @@ -4083,12 +3836,12 @@ constexpr auto expected_map_impl(Exp&& exp, F&& f) } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), - detail::enable_if_t::value>* = nullptr> + detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp&& exp, F&& f) -> expected> { +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { if (exp.has_value()) { detail::invoke(std::forward(f), *std::forward(exp)); return {}; @@ -4098,11 +3851,11 @@ auto expected_map_impl(Exp&& exp, F&& f) -> expected> { } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value>* = nullptr> + detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp&& exp, F&& f) +constexpr auto expected_map_impl(Exp &&exp, F &&f) -> ret_t> { using result = ret_t>; @@ -4111,11 +3864,11 @@ constexpr auto expected_map_impl(Exp&& exp, F&& f) } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value>* = nullptr> + detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp&& exp, F&& f) -> expected> { +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { if (exp.has_value()) { detail::invoke(std::forward(f)); return {}; @@ -4128,11 +3881,11 @@ auto expected_map_impl(Exp&& exp, F&& f) -> expected> { #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -constexpr auto map_error_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { using result = expected, detail::decay_t>; return exp.has_value() ? result(*std::forward(exp)) @@ -4140,11 +3893,11 @@ constexpr auto map_error_impl(Exp&& exp, F&& f) { std::forward(exp).error())); } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -auto map_error_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { using result = expected, monostate>; if (exp.has_value()) { return result(*std::forward(exp)); @@ -4154,11 +3907,11 @@ auto map_error_impl(Exp&& exp, F&& f) { return result(unexpect, monostate{}); } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -constexpr auto map_error_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { using result = expected, detail::decay_t>; return exp.has_value() ? result() @@ -4166,11 +3919,11 @@ constexpr auto map_error_impl(Exp&& exp, F&& f) { std::forward(exp).error())); } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -auto map_error_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { using result = expected, monostate>; if (exp.has_value()) { return result(); @@ -4181,11 +3934,11 @@ auto map_error_impl(Exp&& exp, F&& f) { } #else template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -constexpr auto map_error_impl(Exp&& exp, F&& f) + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) -> expected, detail::decay_t> { using result = expected, detail::decay_t>; @@ -4196,11 +3949,11 @@ constexpr auto map_error_impl(Exp&& exp, F&& f) } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -auto map_error_impl(Exp&& exp, F&& f) -> expected, monostate> { + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { using result = expected, monostate>; if (exp.has_value()) { return result(*std::forward(exp)); @@ -4211,11 +3964,11 @@ auto map_error_impl(Exp&& exp, F&& f) -> expected, monostate> { } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -constexpr auto map_error_impl(Exp&& exp, F&& f) + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) -> expected, detail::decay_t> { using result = expected, detail::decay_t>; @@ -4226,11 +3979,11 @@ constexpr auto map_error_impl(Exp&& exp, F&& f) } template >::value>* = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -auto map_error_impl(Exp&& exp, F&& f) -> expected, monostate> { + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { using result = expected, monostate>; if (exp.has_value()) { return result(); @@ -4245,8 +3998,8 @@ auto map_error_impl(Exp&& exp, F&& f) -> expected, monostate> { template (), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -constexpr auto or_else_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() ? std::forward(exp) : detail::invoke(std::forward(f), @@ -4256,8 +4009,8 @@ constexpr auto or_else_impl(Exp&& exp, F&& f) { template (), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -detail::decay_t or_else_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { return exp.has_value() ? std::forward(exp) : (detail::invoke(std::forward(f), std::forward(exp).error()), @@ -4267,8 +4020,8 @@ detail::decay_t or_else_impl(Exp&& exp, F&& f) { template (), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -auto or_else_impl(Exp&& exp, F&& f) -> Ret { + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() ? std::forward(exp) : detail::invoke(std::forward(f), @@ -4278,8 +4031,8 @@ auto or_else_impl(Exp&& exp, F&& f) -> Ret { template (), std::declval().error())), - detail::enable_if_t::value>* = nullptr> -detail::decay_t or_else_impl(Exp&& exp, F&& f) { + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { return exp.has_value() ? std::forward(exp) : (detail::invoke(std::forward(f), std::forward(exp).error()), @@ -4289,65 +4042,65 @@ detail::decay_t or_else_impl(Exp&& exp, F&& f) { } // namespace detail template -constexpr bool operator==(const expected& lhs, - const expected& rhs) { +constexpr bool operator==(const expected &lhs, + const expected &rhs) { return (lhs.has_value() != rhs.has_value()) ? false : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); } template -constexpr bool operator!=(const expected& lhs, - const expected& rhs) { +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { return (lhs.has_value() != rhs.has_value()) ? true : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); } template -constexpr bool operator==(const expected& lhs, - const expected& rhs) { +constexpr bool operator==(const expected &lhs, + const expected &rhs) { return (lhs.has_value() != rhs.has_value()) ? false : (!lhs.has_value() ? lhs.error() == rhs.error() : true); } template -constexpr bool operator!=(const expected& lhs, - const expected& rhs) { +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { return (lhs.has_value() != rhs.has_value()) ? true : (!lhs.has_value() ? lhs.error() == rhs.error() : false); } template -constexpr bool operator==(const expected& x, const U& v) { +constexpr bool operator==(const expected &x, const U &v) { return x.has_value() ? *x == v : false; } template -constexpr bool operator==(const U& v, const expected& x) { +constexpr bool operator==(const U &v, const expected &x) { return x.has_value() ? *x == v : false; } template -constexpr bool operator!=(const expected& x, const U& v) { +constexpr bool operator!=(const expected &x, const U &v) { return x.has_value() ? *x != v : true; } template -constexpr bool operator!=(const U& v, const expected& x) { +constexpr bool operator!=(const U &v, const expected &x) { return x.has_value() ? *x != v : true; } template -constexpr bool operator==(const expected& x, const unexpected& e) { +constexpr bool operator==(const expected &x, const unexpected &e) { return x.has_value() ? false : x.error() == e.value(); } template -constexpr bool operator==(const unexpected& e, const expected& x) { +constexpr bool operator==(const unexpected &e, const expected &x) { return x.has_value() ? false : x.error() == e.value(); } template -constexpr bool operator!=(const expected& x, const unexpected& e) { +constexpr bool operator!=(const expected &x, const unexpected &e) { return x.has_value() ? true : x.error() != e.value(); } template -constexpr bool operator!=(const unexpected& e, const expected& x) { +constexpr bool operator!=(const unexpected &e, const expected &x) { return x.has_value() ? true : x.error() != e.value(); } @@ -4356,9 +4109,9 @@ template ::value) && detail::is_swappable::value && std::is_move_constructible::value && - detail::is_swappable::value>* = nullptr> -void swap(expected& lhs, - expected& rhs) noexcept(noexcept(lhs.swap(rhs))) { + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } } // namespace tl @@ -4366,16 +4119,236 @@ void swap(expected& lhs, #endif /* end file include/ada/expected.h */ +/* begin file include/ada/url_pattern_regex.h */ /** - * @private + * @file url_search_params.h + * @brief Declaration for the URL Search Params */ -namespace ada { -struct url_aggregator; -struct url; -} // namespace ada +#ifndef ADA_URL_PATTERN_REGEX_H +#define ADA_URL_PATTERN_REGEX_H -/** - * @namespace ada::parser +#include +#include + +#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER +#include +#endif // ADA_USE_UNSAFE_STD_REGEX_PROVIDER + +#if ADA_INCLUDE_URL_PATTERN +namespace ada::url_pattern_regex { + +template +concept regex_concept = requires(T t, std::string_view pattern, + bool ignore_case, std::string_view input) { + // Ensure the class has a type alias 'regex_type' + typename T::regex_type; + + // Function to create a regex instance + { + T::create_instance(pattern, ignore_case) + } -> std::same_as>; + + // Function to perform regex search + { + T::regex_search(input, std::declval()) + } -> std::same_as>>>; + + // Function to match regex pattern + { + T::regex_match(input, std::declval()) + } -> std::same_as; + + // Copy constructor + { T(std::declval()) } -> std::same_as; + + // Move constructor + { T(std::declval()) } -> std::same_as; +}; + +#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER +class std_regex_provider final { + public: + std_regex_provider() = default; + using regex_type = std::regex; + static std::optional create_instance(std::string_view pattern, + bool ignore_case); + static std::optional>> regex_search( + std::string_view input, const regex_type& pattern); + static bool regex_match(std::string_view input, const regex_type& pattern); +}; +#endif // ADA_USE_UNSAFE_STD_REGEX_PROVIDER + +} // namespace ada::url_pattern_regex +#endif // ADA_INCLUDE_URL_PATTERN +#endif // ADA_URL_PATTERN_REGEX_H +/* end file include/ada/url_pattern_regex.h */ +/* begin file include/ada/url_pattern_init.h */ +/** + * @file url_pattern_init.h + * @brief Declaration for the url_pattern_init implementation. + */ +#ifndef ADA_URL_PATTERN_INIT_H +#define ADA_URL_PATTERN_INIT_H + +/* begin file include/ada/errors.h */ +/** + * @file errors.h + * @brief Definitions for the errors. + */ +#ifndef ADA_ERRORS_H +#define ADA_ERRORS_H + +#include +namespace ada { +enum class errors : uint8_t { type_error }; +} // namespace ada +#endif // ADA_ERRORS_H +/* end file include/ada/errors.h */ + +#include +#include +#include +#include + +#if ADA_TESTING +#include +#endif // ADA_TESTING + +#if ADA_INCLUDE_URL_PATTERN +namespace ada { + +// Important: C++20 allows us to use concept rather than `using` or `typedef +// and allows functions with second argument, which is optional (using either +// std::nullopt or a parameter with default value) +template +concept url_pattern_encoding_callback = requires(F f, std::string_view sv) { + { f(sv) } -> std::same_as>; +}; + +// A structure providing matching patterns for individual components +// of a URL. When a URLPattern is created, or when a URLPattern is +// used to match or test against a URL, the input can be given as +// either a string or a URLPatternInit struct. If a string is given, +// it will be parsed to create a URLPatternInit. The URLPatternInit +// API is defined as part of the URLPattern specification. +// All provided strings must be valid UTF-8. +struct url_pattern_init { + enum class process_type : uint8_t { + url, + pattern, + }; + + friend std::ostream& operator<<(std::ostream& os, process_type type) { + switch (type) { + case process_type::url: + return os << "url"; + case process_type::pattern: + return os << "pattern"; + default: + return os << "unknown"; + } + } + + // All strings must be valid UTF-8. + // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit + static tl::expected process( + const url_pattern_init& init, process_type type, + std::optional protocol = std::nullopt, + std::optional username = std::nullopt, + std::optional password = std::nullopt, + std::optional hostname = std::nullopt, + std::optional port = std::nullopt, + std::optional pathname = std::nullopt, + std::optional search = std::nullopt, + std::optional hash = std::nullopt); + + // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init + static tl::expected process_protocol( + std::string_view value, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-username-for-init + static tl::expected process_username( + std::string_view value, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-password-for-init + static tl::expected process_password( + std::string_view value, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init + static tl::expected process_hostname( + std::string_view value, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-port-for-init + static tl::expected process_port( + std::string_view port, std::string_view protocol, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init + static tl::expected process_pathname( + std::string_view value, std::string_view protocol, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-search-for-init + static tl::expected process_search( + std::string_view value, process_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init + static tl::expected process_hash(std::string_view value, + process_type type); + +#if ADA_TESTING + friend void PrintTo(const url_pattern_init& init, std::ostream* os) { + *os << "protocol: '" << init.protocol.value_or("undefined") << "', "; + *os << "username: '" << init.username.value_or("undefined") << "', "; + *os << "password: '" << init.password.value_or("undefined") << "', "; + *os << "hostname: '" << init.hostname.value_or("undefined") << "', "; + *os << "port: '" << init.port.value_or("undefined") << "', "; + *os << "pathname: '" << init.pathname.value_or("undefined") << "', "; + *os << "search: '" << init.search.value_or("undefined") << "', "; + *os << "hash: '" << init.hash.value_or("undefined") << "', "; + *os << "base_url: '" << init.base_url.value_or("undefined") << "', "; + } +#endif // ADA_TESTING + + bool operator==(const url_pattern_init&) const; + // If present, must be valid UTF-8. + std::optional protocol{}; + // If present, must be valid UTF-8. + std::optional username{}; + // If present, must be valid UTF-8. + std::optional password{}; + // If present, must be valid UTF-8. + std::optional hostname{}; + // If present, must be valid UTF-8. + std::optional port{}; + // If present, must be valid UTF-8. + std::optional pathname{}; + // If present, must be valid UTF-8. + std::optional search{}; + // If present, must be valid UTF-8. + std::optional hash{}; + // If present, must be valid UTF-8. + std::optional base_url{}; +}; +} // namespace ada +#endif // ADA_INCLUDE_URL_PATTERN +#endif // ADA_URL_PATTERN_INIT_H +/* end file include/ada/url_pattern_init.h */ + +/** + * @private + */ +namespace ada { +struct url_aggregator; +struct url; +#if ADA_INCLUDE_URL_PATTERN +template +class url_pattern; +struct url_pattern_options; +#endif // ADA_INCLUDE_URL_PATTERN +enum class errors : uint8_t; +} // namespace ada + +/** + * @namespace ada::parser * @brief Includes the definitions for supported parsers */ namespace ada::parser { @@ -4385,7 +4358,7 @@ namespace ada::parser { * parameter that can be used to resolve relative URLs. If the base_url is * provided, the user_input is resolved against the base_url. */ -template +template result_type parse_url(std::string_view user_input, const result_type* base_url = nullptr); @@ -4394,7 +4367,7 @@ extern template url_aggregator parse_url( extern template url parse_url(std::string_view user_input, const url* base_url); -template +template result_type parse_url_impl(std::string_view user_input, const result_type* base_url = nullptr); @@ -4402,507 +4375,496 @@ extern template url_aggregator parse_url_impl( std::string_view user_input, const url_aggregator* base_url); extern template url parse_url_impl(std::string_view user_input, const url* base_url); + +#if ADA_INCLUDE_URL_PATTERN +template +tl::expected, errors> parse_url_pattern_impl( + std::variant&& input, + const std::string_view* base_url, const url_pattern_options* options); +#endif // ADA_INCLUDE_URL_PATTERN + } // namespace ada::parser #endif // ADA_PARSER_H /* end file include/ada/parser.h */ -/* begin file include/ada/scheme-inl.h */ +/* begin file include/ada/parser-inl.h */ /** - * @file scheme-inl.h - * @brief Definitions for the URL scheme. + * @file parser-inl.h */ -#ifndef ADA_SCHEME_INL_H -#define ADA_SCHEME_INL_H - -namespace ada::scheme { +#ifndef ADA_PARSER_INL_H +#define ADA_PARSER_INL_H +/* begin file include/ada/url_pattern.h */ /** - * @namespace ada::scheme::details - * @brief Includes the definitions for scheme specific entities + * @file url_pattern.h + * @brief Declaration for the URLPattern implementation. */ -namespace details { -// for use with is_special and get_special_port -// Spaces, if present, are removed from URL. -constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", - "ftp", "wss", "file", " "}; -// for use with get_special_port -constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; -} // namespace details - -/**** - * @private - * In is_special, get_scheme_type, and get_special_port, we - * use a standard hashing technique to find the index of the scheme in - * the is_special_list. The hashing technique is based on the size of - * the scheme and the first character of the scheme. It ensures that we - * do at most one string comparison per call. If the protocol is - * predictible (e.g., it is always "http"), we can get a better average - * performance by using a simpler approach where we loop and compare - * scheme with all possible protocols starting with the most likely - * protocol. Doing multiple comparisons may have a poor worst case - * performance, however. In this instance, we choose a potentially - * slightly lower best-case performance for a better worst-case - * performance. We can revisit this choice at any time. - * - * Reference: - * Schmidt, Douglas C. "Gperf: A perfect hash function generator." - * More C++ gems 17 (2000). - * - * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function - * - * Reference: https://github.com/ada-url/ada/issues/617 - ****/ +#ifndef ADA_URL_PATTERN_H +#define ADA_URL_PATTERN_H -ada_really_inline constexpr bool is_special(std::string_view scheme) { - if (scheme.empty()) { - return false; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); -} -constexpr uint16_t get_special_port(std::string_view scheme) noexcept { - if (scheme.empty()) { - return 0; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return details::special_ports[hash_value]; - } else { - return 0; - } -} -constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { - return details::special_ports[int(type)]; -} -constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { - if (scheme.empty()) { - return ada::scheme::NOT_SPECIAL; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return ada::scheme::type(hash_value); - } else { - return ada::scheme::NOT_SPECIAL; - } -} +/* begin file include/ada/implementation.h */ +/** + * @file implementation.h + * @brief Definitions for user facing functions for parsing URL and it's + * components. + */ +#ifndef ADA_IMPLEMENTATION_H +#define ADA_IMPLEMENTATION_H -} // namespace ada::scheme +#include +#include +#include -#endif // ADA_SCHEME_INL_H -/* end file include/ada/scheme-inl.h */ -/* begin file include/ada/serializers.h */ +/* begin file include/ada/url.h */ /** - * @file serializers.h - * @brief Definitions for the URL serializers. + * @file url.h + * @brief Declaration for the URL */ -#ifndef ADA_SERIALIZERS_H -#define ADA_SERIALIZERS_H +#ifndef ADA_URL_H +#define ADA_URL_H -#include +#include #include +#include #include +#include +/* begin file include/ada/checkers.h */ /** - * @namespace ada::serializers - * @brief Includes the definitions for URL serializers + * @file checkers.h + * @brief Declarations for URL specific checkers used within Ada. */ -namespace ada::serializers { +#ifndef ADA_CHECKERS_H +#define ADA_CHECKERS_H + + +#include +#include /** - * Finds and returns the longest sequence of 0 values in a ipv6 input. + * These functions are not part of our public API and may + * change at any time. + * @private + * @namespace ada::checkers + * @brief Includes the definitions for validation functions */ -void find_longest_sequence_of_ipv6_pieces( - const std::array& address, size_t& compress, - size_t& compress_length) noexcept; +namespace ada::checkers { /** - * Serializes an ipv6 address. - * @details An IPv6 address is a 128-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv6-serializer + * @private + * Assuming that x is an ASCII letter, this function returns the lower case + * equivalent. + * @details More likely to be inlined by the compiler and constexpr. */ -std::string ipv6(const std::array& address) noexcept; +constexpr char to_lower(char x) noexcept; /** - * Serializes an ipv4 address. - * @details An IPv4 address is a 32-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv4-serializer + * @private + * Returns true if the character is an ASCII letter. Equivalent to std::isalpha + * but more likely to be inlined by the compiler. + * + * @attention std::isalpha is not constexpr generally. */ -std::string ipv4(uint64_t address) noexcept; - -} // namespace ada::serializers +constexpr bool is_alpha(char x) noexcept; -#endif // ADA_SERIALIZERS_H -/* end file include/ada/serializers.h */ -/* begin file include/ada/unicode.h */ /** - * @file unicode.h - * @brief Definitions for all unicode specific functions. + * @private + * Check whether a string starts with 0x or 0X. The function is only + * safe if input.size() >=2. + * + * @see has_hex_prefix */ -#ifndef ADA_UNICODE_H -#define ADA_UNICODE_H - -#include -#include +constexpr bool has_hex_prefix_unsafe(std::string_view input); +/** + * @private + * Check whether a string starts with 0x or 0X. + */ +constexpr bool has_hex_prefix(std::string_view input); /** - * Unicode operations. These functions are not part of our public API and may - * change at any time. - * * @private - * @namespace ada::unicode - * @brief Includes the definitions for unicode operations + * Check whether x is an ASCII digit. More likely to be inlined than + * std::isdigit. */ -namespace ada::unicode { +constexpr bool is_digit(char x) noexcept; /** * @private - * We receive a UTF-8 string representing a domain name. - * If the string is percent encoded, we apply percent decoding. - * - * Given a domain, we need to identify its labels. - * They are separated by label-separators: - * - * U+002E (.) FULL STOP - * U+FF0E FULLWIDTH FULL STOP - * U+3002 IDEOGRAPHIC FULL STOP - * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP + * @details A string starts with a Windows drive letter if all of the following + * are true: * - * They are all mapped to U+002E. - * - * We process each label into a string that should not exceed 63 octets. - * If the string is already punycode (starts with "xn--"), then we must - * scan it to look for unallowed code points. - * Otherwise, if the string is not pure ASCII, we need to transcode it - * to punycode by following RFC 3454 which requires us to - * - Map characters (see section 3), - * - Normalize (see section 4), - * - Reject forbidden characters, - * - Check for right-to-left characters and if so, check all requirements (see - * section 6), - * - Optionally reject based on unassigned code points (section 7). - * - * The Unicode standard provides a table of code points with a mapping, a list - * of forbidden code points and so forth. This table is subject to change and - * will vary based on the implementation. For Unicode 15, the table is at - * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt - * If you use ICU, they parse this table and map it to code using a Python - * script. - * - * The resulting strings should not exceed 255 octets according to RFC 1035 - * section 2.3.4. ICU checks for label size and domain size, but these errors - * are ignored. - * - * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * - its length is greater than or equal to 2 + * - its first two code points are a Windows drive letter + * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F + * (?), or U+0023 (#). * + * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter */ -bool to_ascii(std::optional& out, std::string_view plain, - size_t first_percent); +inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; /** * @private - * Checks if the input has tab or newline characters. - * - * @attention The has_tabs_or_newline function is a bottleneck and it is simple - * enough that compilers like GCC can 'autovectorize it'. + * @details A normalized Windows drive letter is a Windows drive letter of which + * the second code point is U+003A (:). */ -ada_really_inline bool has_tabs_or_newline( - std::string_view user_input) noexcept; +inline constexpr bool is_normalized_windows_drive_letter( + std::string_view input) noexcept; /** * @private - * Checks if the input is a forbidden host code point. - * @see https://url.spec.whatwg.org/#forbidden-host-code-point + * Returns true if an input is an ipv4 address. It is assumed that the string + * does not contain uppercase ASCII characters (the input should have been + * lowered cased before calling this function) and is not empty. */ -ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept; +ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept; /** * @private - * Checks if the input contains a forbidden domain code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * Returns a bitset. If the first bit is set, then at least one character needs + * percent encoding. If the second bit is set, a \\ is found. If the third bit + * is set then we have a dot. If the fourth bit is set, then we have a percent + * character. */ -ada_really_inline constexpr bool contains_forbidden_domain_code_point( - const char* input, size_t length) noexcept; +ada_really_inline constexpr uint8_t path_signature( + std::string_view input) noexcept; /** * @private - * Checks if the input contains a forbidden domain code point in which case - * the first bit is set to 1. If the input contains an upper case ASCII letter, - * then the second bit is set to 1. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * Returns true if the length of the domain name and its labels are according to + * the specifications. The length of the domain must be 255 octets (253 + * characters not including the last 2 which are the empty label reserved at the + * end). When the empty label is included (a dot at the end), the domain name + * can have 254 characters. The length of a label must be at least 1 and at most + * 63 characters. + * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 + * @see https://www.unicode.org/reports/tr46/#ToASCII */ -ada_really_inline constexpr uint8_t -contains_forbidden_domain_code_point_or_upper(const char* input, - size_t length) noexcept; +ada_really_inline constexpr bool verify_dns_length( + std::string_view input) noexcept; -/** - * @private - * Checks if the input is a forbidden domain code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point - */ -ada_really_inline constexpr bool is_forbidden_domain_code_point( - char c) noexcept; +} // namespace ada::checkers +#endif // ADA_CHECKERS_H +/* end file include/ada/checkers.h */ +/* begin file include/ada/url_components.h */ /** - * @private - * Checks if the input is alphanumeric, '+', '-' or '.' + * @file url_components.h + * @brief Declaration for the URL Components */ -ada_really_inline constexpr bool is_alnum_plus(char c) noexcept; +#ifndef ADA_URL_COMPONENTS_H +#define ADA_URL_COMPONENTS_H -/** - * @private - * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex - * digit. An ASCII upper hex digit is an ASCII digit or a code point in the - * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an - * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. - */ -ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept; +namespace ada { /** - * @private - * Checks if the input is a C0 control or space character. + * @brief URL Component representations using offsets. * - * @details A C0 control or space is a C0 control or U+0020 SPACE. - * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION - * SEPARATOR ONE, inclusive. - */ -ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept; - -/** - * @private - * Checks if the input is a ASCII tab or newline character. + * @details We design the url_components struct so that it is as small + * and simple as possible. This version uses 32 bytes. * - * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. - */ -ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept; - -/** - * @private - * @details A double-dot path segment must be ".." or an ASCII case-insensitive - * match for ".%2e", "%2e.", or "%2e%2e". - */ -ada_really_inline ada_constexpr bool is_double_dot_path_segment( - std::string_view input) noexcept; - -/** - * @private - * @details A single-dot path segment must be "." or an ASCII case-insensitive - * match for "%2e". + * This struct is used to extract components from a single 'href'. */ -ada_really_inline constexpr bool is_single_dot_path_segment( - std::string_view input) noexcept; +struct url_components { + constexpr static uint32_t omitted = uint32_t(-1); -/** - * @private - * @details ipv4 character might contain 0-9 or a-f character ranges. - */ -ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept; + url_components() = default; + url_components(const url_components &u) = default; + url_components(url_components &&u) noexcept = default; + url_components &operator=(url_components &&u) noexcept = default; + url_components &operator=(const url_components &u) = default; + ~url_components() = default; -/** - * @private - * @details Convert hex to binary. Caller is responsible to ensure that - * the parameter is an hexadecimal digit (0-9, A-F, a-f). - */ -ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept; + /* + * By using 32-bit integers, we implicitly assume that the URL string + * cannot exceed 4 GB. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + uint32_t protocol_end{0}; + /** + * Username end is not `omitted` by default to make username and password + * getters less costly to implement. + */ + uint32_t username_end{0}; + uint32_t host_start{0}; + uint32_t host_end{0}; + uint32_t port{omitted}; + uint32_t pathname_start{0}; + uint32_t search_start{omitted}; + uint32_t hash_start{omitted}; -/** - * @private - * first_percent should be = input.find('%') - * - * @todo It would be faster as noexcept maybe, but it could be unsafe since. - * @author Node.js - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 - * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom - */ -std::string percent_decode(std::string_view input, size_t first_percent); + /** + * Check the following conditions: + * protocol_end < username_end < ... < hash_start, + * expect when a value is omitted. It also computes + * a lower bound on the possible string length that may match these + * offsets. + * @return true if the offset values are + * consistent with a possible URL string + */ + [[nodiscard]] constexpr bool check_offset_consistency() const noexcept; -/** - * @private - * Returns a percent-encoding string whether percent encoding was needed or not. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 - */ -std::string percent_encode(std::string_view input, - const uint8_t character_set[]); -/** - * @private - * Returns a percent-encoded string version of input, while starting the percent - * encoding at the provided index. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 - */ -std::string percent_encode(std::string_view input, - const uint8_t character_set[], size_t index); -/** - * @private - * Returns true if percent encoding was needed, in which case, we store - * the percent-encoded content in 'out'. If the boolean 'append' is set to - * true, the content is appended to 'out'. - * If percent encoding is not needed, out is left unchanged. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 - */ -template -bool percent_encode(std::string_view input, const uint8_t character_set[], - std::string& out); -/** - * @private - * Returns the index at which percent encoding should start, or (equivalently), - * the length of the prefix that does not require percent encoding. - */ -ada_really_inline size_t percent_encode_index(std::string_view input, - const uint8_t character_set[]); -/** - * @private - * Lowers the string in-place, assuming that the content is ASCII. - * Return true if the content was ASCII. - */ -constexpr bool to_lower_ascii(char* input, size_t length) noexcept; -} // namespace ada::unicode + /** + * Converts a url_components to JSON stringified version. + */ + [[nodiscard]] std::string to_string() const; -#endif // ADA_UNICODE_H -/* end file include/ada/unicode.h */ -/* begin file include/ada/url_base-inl.h */ -/** - * @file url_base-inl.h - * @brief Inline functions for url base - */ -#ifndef ADA_URL_BASE_INL_H -#define ADA_URL_BASE_INL_H +}; // struct url_components +} // namespace ada +#endif +/* end file include/ada/url_components.h */ -/* begin file include/ada/url_aggregator.h */ -/** - * @file url_aggregator.h - * @brief Declaration for the basic URL definitions - */ -#ifndef ADA_URL_AGGREGATOR_H -#define ADA_URL_AGGREGATOR_H +namespace ada { -#include -#include +struct url_aggregator; -namespace ada { +// namespace parser { +// template +// result_type parse_url(std::string_view user_input, +// const result_type* base_url = nullptr); +// template +// result_type parse_url_impl(std::string_view user_input, +// const result_type* base_url = nullptr); +// } /** - * @brief Lightweight URL struct. + * @brief Generic URL struct reliant on std::string instantiation. * - * @details The url_aggregator class aims to minimize temporary memory - * allocation while representing a parsed URL. Internally, it contains a single - * normalized URL (the href), and it makes available the components, mostly - * using std::string_view. + * @details To disambiguate from a valid URL string it can also be referred to + * as a URL record. A URL is a struct that represents a universal identifier. + * Unlike the url_aggregator, the ada::url represents the different components + * of a parsed URL as independent std::string instances. This makes the + * structure heavier and more reliant on memory allocations. When getting + * components from the parsed URL, a new std::string is typically constructed. + * + * @see https://url.spec.whatwg.org/#url-representation */ -struct url_aggregator : url_base { - url_aggregator() = default; - url_aggregator(const url_aggregator& u) = default; - url_aggregator(url_aggregator&& u) noexcept = default; - url_aggregator& operator=(url_aggregator&& u) noexcept = default; - url_aggregator& operator=(const url_aggregator& u) = default; - ~url_aggregator() override = default; +struct url : url_base { + url() = default; + url(const url &u) = default; + url(url &&u) noexcept = default; + url &operator=(url &&u) noexcept = default; + url &operator=(const url &u) = default; + ~url() override = default; - bool set_href(std::string_view input); - bool set_host(std::string_view input); - bool set_hostname(std::string_view input); - bool set_protocol(std::string_view input); - bool set_username(std::string_view input); - bool set_password(std::string_view input); - bool set_port(std::string_view input); - bool set_pathname(std::string_view input); - void set_search(std::string_view input); - void set_hash(std::string_view input); + /** + * @private + * A URL's username is an ASCII string identifying a username. It is initially + * the empty string. + */ + std::string username{}; - [[nodiscard]] bool has_valid_domain() const noexcept override; /** - * The origin getter steps are to return the serialization of this's URL's - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin + * @private + * A URL's password is an ASCII string identifying a password. It is initially + * the empty string. */ - [[nodiscard]] std::string get_origin() const noexcept override; + std::string password{}; + /** - * Return the normalized string. - * This function does not allocate memory. - * It is highly efficient. - * @return a constant reference to the underlying normalized URL. - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer + * @private + * A URL's host is null or a host. It is initially null. */ - [[nodiscard]] inline std::string_view get_href() const noexcept; + std::optional host{}; + /** - * The username getter steps are to return this's URL's username. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-username + * @private + * A URL's port is either null or a 16-bit unsigned integer that identifies a + * networking port. It is initially null. */ - [[nodiscard]] std::string_view get_username() const noexcept; + std::optional port{}; + /** - * The password getter steps are to return this's URL's password. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-password + * @private + * A URL's path is either an ASCII string or a list of zero or more ASCII + * strings, usually identifying a location. */ - [[nodiscard]] std::string_view get_password() const noexcept; + std::string path{}; + /** - * Return this's URL's port, serialized. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-port + * @private + * A URL's query is either null or an ASCII string. It is initially null. */ - [[nodiscard]] std::string_view get_port() const noexcept; + std::optional query{}; + /** - * Return U+0023 (#), followed by this's URL's fragment. - * This function does not allocate memory. - * @return a lightweight std::string_view.. - * @see https://url.spec.whatwg.org/#dom-url-hash + * @private + * A URL's fragment is either null or an ASCII string that can be used for + * further processing on the resource the URL's other components identify. It + * is initially null. + */ + std::optional hash{}; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] inline bool has_empty_hostname() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] inline bool has_port() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] inline bool has_hostname() const noexcept; + [[nodiscard]] bool has_valid_domain() const noexcept override; + + /** + * Returns a JSON string representation of this URL. + */ + [[nodiscard]] std::string to_string() const override; + + /** + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer + */ + [[nodiscard]] ada_really_inline std::string get_href() const noexcept; + + /** + * The origin getter steps are to return the serialization of this's URL's + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + + /** + * The protocol getter steps are to return this's URL's scheme, followed by + * U+003A (:). + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#dom-url-protocol */ - [[nodiscard]] std::string_view get_hash() const noexcept; + [[nodiscard]] std::string get_protocol() const noexcept; + /** * Return url's host, serialized, followed by U+003A (:) and url's port, * serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-host */ - [[nodiscard]] std::string_view get_host() const noexcept; + [[nodiscard]] std::string get_host() const noexcept; + /** * Return this's URL's host, serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-hostname */ - [[nodiscard]] std::string_view get_hostname() const noexcept; + [[nodiscard]] std::string get_hostname() const noexcept; + /** * The pathname getter steps are to return the result of URL path serializing * this's URL. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-pathname */ - [[nodiscard]] std::string_view get_pathname() const noexcept; + [[nodiscard]] constexpr std::string_view get_pathname() const noexcept; + /** * Compute the pathname length in bytes without instantiating a view or a * string. * @return size of the pathname in bytes * @see https://url.spec.whatwg.org/#dom-url-pathname */ - [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept; + [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept; + /** * Return U+003F (?), followed by this's URL's query. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-search */ - [[nodiscard]] std::string_view get_search() const noexcept; + [[nodiscard]] std::string get_search() const noexcept; + /** - * The protocol getter steps are to return this's URL's scheme, followed by - * U+003A (:). - * This function does not allocate memory. - * @return a lightweight std::string_view. + * The username getter steps are to return this's URL's username. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + [[nodiscard]] const std::string &get_username() const noexcept; + + /** + * @return Returns true on successful operation. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + bool set_username(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + bool set_password(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + bool set_port(std::string_view input); + + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + void set_hash(std::string_view input); + + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + void set_search(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + bool set_pathname(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + bool set_host(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + bool set_hostname(std::string_view input); + + /** + * @return Returns true on success. * @see https://url.spec.whatwg.org/#dom-url-protocol */ - [[nodiscard]] std::string_view get_protocol() const noexcept; + bool set_protocol(std::string_view input); + + /** + * @see https://url.spec.whatwg.org/#dom-url-href + */ + bool set_href(std::string_view input); + + /** + * The password getter steps are to return this's URL's password. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] const std::string &get_password() const noexcept; + + /** + * Return this's URL's port, serialized. + * @return a newly constructed string representing the port. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string get_port() const noexcept; + + /** + * Return U+0023 (#), followed by this's URL's fragment. + * @return a newly constructed string representing the hash. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string get_hash() const noexcept; /** * A URL includes credentials if its username or password is not the empty @@ -4926,92 +4888,54 @@ struct url_aggregator : url_base { * * Inspired after servo/url * - * @return a constant reference to the underlying component attribute. + * @return a newly constructed component. * * @see * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 */ - [[nodiscard]] ada_really_inline const ada::url_components& get_components() + [[nodiscard]] ada_really_inline ada::url_components get_components() const noexcept; - /** - * Returns a string representation of this URL. - */ - [[nodiscard]] std::string to_string() const override; - /** - * Returns a string diagram of this URL. - */ - [[nodiscard]] std::string to_diagram() const; + /** @return true if the URL has a hash component */ + [[nodiscard]] constexpr bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] constexpr bool has_search() const noexcept override; - /** - * Verifies that the parsed URL could be valid. Useful for debugging purposes. - * @return true if the URL is valid, otherwise return true of the offsets are - * possible. - */ - [[nodiscard]] bool validate() const noexcept; + private: + friend ada::url ada::parser::parse_url(std::string_view, + const ada::url *); + friend ada::url_aggregator ada::parser::parse_url( + std::string_view, const ada::url_aggregator *); + friend void ada::helpers::strip_trailing_spaces_from_opaque_path( + ada::url &url) noexcept; - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - /** @return true if the URL has a non-empty username */ - [[nodiscard]] inline bool has_non_empty_username() const noexcept; - /** @return true if the URL has a non-empty password */ - [[nodiscard]] inline bool has_non_empty_password() const noexcept; - /** @return true if the URL has a (non default) port */ - [[nodiscard]] inline bool has_port() const noexcept; - /** @return true if the URL has a password */ - [[nodiscard]] inline bool has_password() const noexcept; - /** @return true if the URL has a hash component */ - [[nodiscard]] inline bool has_hash() const noexcept override; - /** @return true if the URL has a search component */ - [[nodiscard]] inline bool has_search() const noexcept override; - - inline void clear_port(); - inline void clear_hash(); - inline void clear_search() override; - - private: - friend ada::url_aggregator ada::parser::parse_url( - std::string_view, const ada::url_aggregator*); - friend void ada::helpers::strip_trailing_spaces_from_opaque_path< - ada::url_aggregator>(ada::url_aggregator& url) noexcept; - friend ada::url_aggregator ada::parser::parse_url_impl< - ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*); + friend ada::url ada::parser::parse_url_impl(std::string_view, + const ada::url *); friend ada::url_aggregator ada::parser::parse_url_impl< - ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*); - - std::string buffer{}; - url_components components{}; - - /** - * Returns true if neither the search, nor the hash nor the pathname - * have been set. - * @return true if the buffer is ready to receive the path. - */ - [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; + ada::url_aggregator, true>(std::string_view, const ada::url_aggregator *); - inline void add_authority_slashes_if_needed() noexcept; + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t query_percent_encode_set[]); + inline void update_base_search(std::optional &&input); + inline void update_base_pathname(std::string_view input); + inline void update_base_username(std::string_view input); + inline void update_base_password(std::string_view input); + inline void update_base_port(std::optional input); /** - * To optimize performance, you may indicate how much memory to allocate - * within this instance. + * Sets the host or hostname according to override condition. + * Return true on success. + * @see https://url.spec.whatwg.org/#hostname-state */ - inline void reserve(uint32_t capacity); - - ada_really_inline size_t parse_port( - std::string_view view, bool check_trailing_content) noexcept override; - - ada_really_inline size_t parse_port(std::string_view view) noexcept override { - return this->parse_port(view, false); - } + template + bool set_host_or_hostname(std::string_view input); /** - * Return true on success. The 'in_place' parameter indicates whether the - * the string_view input is pointing in the buffer. When in_place is false, - * we must nearly always update the buffer. + * Return true on success. * @see https://url.spec.whatwg.org/#concept-ipv4-parser */ - [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place); + [[nodiscard]] bool parse_ipv4(std::string_view input); /** * Return true on success. @@ -5025,7 +4949,16 @@ struct url_aggregator : url_base { */ [[nodiscard]] bool parse_opaque_host(std::string_view input); - ada_really_inline void parse_path(std::string_view input); + /** + * A URL's scheme is an ASCII string that identifies the type of URL and can + * be used to dispatch a URL for further processing after parsing. It is + * initially the empty string. We only set non_special_scheme when the scheme + * is non-special, otherwise we avoid constructing string. + * + * Special schemes are stored in ada::scheme::details::is_special_list so we + * typically do not need to store them in each url instance. + */ + std::string non_special_scheme{}; /** * A URL cannot have a username/password/port if its host is null or the empty @@ -5033,729 +4966,1747 @@ struct url_aggregator : url_base { */ [[nodiscard]] inline bool cannot_have_credentials_or_port() const; - template - bool set_host_or_hostname(std::string_view input); + ada_really_inline size_t parse_port( + std::string_view view, bool check_trailing_content) noexcept override; - ada_really_inline bool parse_host(std::string_view input); + ada_really_inline size_t parse_port(std::string_view view) noexcept override { + return this->parse_port(view, false); + } + + /** + * Parse the host from the provided input. We assume that + * the input does not contain spaces or tabs. Control + * characters and spaces are not trimmed (they should have + * been removed if needed). + * Return true on success. + * @see https://url.spec.whatwg.org/#host-parsing + */ + [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); - inline void update_base_authority(std::string_view base_buffer, - const ada::url_components& base); - inline void update_unencoded_base_hash(std::string_view input); - inline void update_base_hostname(std::string_view input); - inline void update_base_search(std::string_view input); - inline void update_base_search(std::string_view input, - const uint8_t* query_percent_encode_set); - inline void update_base_pathname(std::string_view input); - inline void update_base_username(std::string_view input); - inline void append_base_username(std::string_view input); - inline void update_base_password(std::string_view input); - inline void append_base_password(std::string_view input); - inline void update_base_port(uint32_t input); - inline void append_base_pathname(std::string_view input); - [[nodiscard]] inline uint32_t retrieve_base_port() const; - inline void clear_hostname(); - inline void clear_password(); - inline void clear_pathname() override; - [[nodiscard]] inline bool has_dash_dot() const noexcept; - void delete_dash_dot(); - inline void consume_prepared_path(std::string_view input); template - [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( - std::string_view input); - ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, - std::string_view input); - [[nodiscard]] inline bool has_authority() const noexcept; - inline void set_protocol_as_file(); - inline void set_scheme(std::string_view new_scheme) noexcept; + [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input); + + constexpr void clear_pathname() override; + constexpr void clear_search() override; + constexpr void set_protocol_as_file(); + /** - * Fast function to set the scheme from a view with a colon in the - * buffer, does not change type. + * Parse the path from the provided input. + * Return true on success. Control characters not + * trimmed from the ends (they should have + * been removed if needed). + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ */ - inline void set_scheme_from_view_with_colon( - std::string_view new_scheme_with_colon) noexcept; - inline void copy_scheme(const url_aggregator& u) noexcept; + ada_really_inline void parse_path(std::string_view input); -}; // url_aggregator + /** + * Set the scheme for this URL. The provided scheme should be a valid + * scheme string, be lower-cased, not contain spaces or tabs. It should + * have no spurious trailing or leading content. + */ + inline void set_scheme(std::string &&new_scheme) noexcept; -inline std::ostream& operator<<(std::ostream& out, const ada::url& u); -} // namespace ada + /** + * Take the scheme from another URL. The scheme string is moved from the + * provided url. + */ + constexpr void copy_scheme(ada::url &&u) noexcept; -#endif -/* end file include/ada/url_aggregator.h */ -/* begin file include/ada/checkers.h */ -/** - * @file checkers.h - * @brief Declarations for URL specific checkers used within Ada. - */ -#ifndef ADA_CHECKERS_H -#define ADA_CHECKERS_H + /** + * Take the scheme from another URL. The scheme string is copied from the + * provided url. + */ + constexpr void copy_scheme(const ada::url &u); -#include -#include +}; // struct url -/** - * These functions are not part of our public API and may - * change at any time. - * @private - * @namespace ada::checkers - * @brief Includes the definitions for validation functions - */ -namespace ada::checkers { +inline std::ostream &operator<<(std::ostream &out, const ada::url &u); +} // namespace ada -/** - * @private - * Assuming that x is an ASCII letter, this function returns the lower case - * equivalent. - * @details More likely to be inlined by the compiler and constexpr. - */ -constexpr char to_lower(char x) noexcept; +#endif // ADA_URL_H +/* end file include/ada/url.h */ -/** - * @private - * Returns true if the character is an ASCII letter. Equivalent to std::isalpha - * but more likely to be inlined by the compiler. - * - * @attention std::isalpha is not constexpr generally. - */ -constexpr bool is_alpha(char x) noexcept; +namespace ada { + +template +using result = tl::expected; /** - * @private - * Check whether a string starts with 0x or 0X. The function is only - * safe if input.size() >=2. + * The URL parser takes a scalar value string input, with an optional null or + * base URL base (default null). The parser assumes the input is a valid ASCII + * or UTF-8 string. * - * @see has_hex_prefix - */ -inline bool has_hex_prefix_unsafe(std::string_view input); -/** - * @private - * Check whether a string starts with 0x or 0X. + * @param input the string input to analyze (must be valid ASCII or UTF-8) + * @param base_url the optional URL input to use as a base url. + * @return a parsed URL. */ -inline bool has_hex_prefix(std::string_view input); +template +ada_warn_unused ada::result parse( + std::string_view input, const result_type* base_url = nullptr); + +extern template ada::result parse(std::string_view input, + const url* base_url); +extern template ada::result parse( + std::string_view input, const url_aggregator* base_url); /** - * @private - * Check whether x is an ASCII digit. More likely to be inlined than - * std::isdigit. + * Verifies whether the URL strings can be parsed. The function assumes + * that the inputs are valid ASCII or UTF-8 strings. + * @see https://url.spec.whatwg.org/#dom-url-canparse + * @return If URL can be parsed or not. */ -constexpr bool is_digit(char x) noexcept; +bool can_parse(std::string_view input, + const std::string_view* base_input = nullptr); +#if ADA_INCLUDE_URL_PATTERN /** - * @private - * @details A string starts with a Windows drive letter if all of the following - * are true: + * Implementation of the URL pattern parsing algorithm. + * @see https://urlpattern.spec.whatwg.org * - * - its length is greater than or equal to 2 - * - its first two code points are a Windows drive letter - * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F - * (?), or U+0023 (#). - * - * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter + * @param input valid UTF-8 string or URLPatternInit struct + * @param base_url an optional valid UTF-8 string + * @param options an optional url_pattern_options struct + * @return url_pattern instance */ -inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; +template +ada_warn_unused tl::expected, errors> +parse_url_pattern(std::variant&& input, + const std::string_view* base_url = nullptr, + const url_pattern_options* options = nullptr); +#endif // ADA_INCLUDE_URL_PATTERN /** - * @private - * @details A normalized Windows drive letter is a Windows drive letter of which - * the second code point is U+003A (:). + * Computes a href string from a file path. The function assumes + * that the input is a valid ASCII or UTF-8 string. + * @return a href string (starts with file:://) */ -inline constexpr bool is_normalized_windows_drive_letter( - std::string_view input) noexcept; +std::string href_from_file(std::string_view path); +} // namespace ada -/** - * @private - * @warning Will be removed when Ada requires C++20. - */ -ada_really_inline bool begins_with(std::string_view view, - std::string_view prefix); +#endif // ADA_IMPLEMENTATION_H +/* end file include/ada/implementation.h */ -/** - * @private - * Returns true if an input is an ipv4 address. It is assumed that the string - * does not contain uppercase ASCII characters (the input should have been - * lowered cased before calling this function) and is not empty. - */ -ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept; +#include +#include +#include +#include +#include +#include -/** - * @private - * Returns a bitset. If the first bit is set, then at least one character needs - * percent encoding. If the second bit is set, a \\ is found. If the third bit - * is set then we have a dot. If the fourth bit is set, then we have a percent - * character. - */ -ada_really_inline constexpr uint8_t path_signature( - std::string_view input) noexcept; +#if ADA_TESTING +#include +#endif // ADA_TESTING -/** - * @private - * Returns true if the length of the domain name and its labels are according to - * the specifications. The length of the domain must be 255 octets (253 - * characters not including the last 2 which are the empty label reserved at the - * end). When the empty label is included (a dot at the end), the domain name - * can have 254 characters. The length of a label must be at least 1 and at most - * 63 characters. - * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 - * @see https://www.unicode.org/reports/tr46/#ToASCII - */ -ada_really_inline constexpr bool verify_dns_length( - std::string_view input) noexcept; +#if ADA_INCLUDE_URL_PATTERN +namespace ada { -} // namespace ada::checkers +enum class url_pattern_part_type : uint8_t { + // The part represents a simple fixed text string. + FIXED_TEXT, + // The part represents a matching group with a custom regular expression. + REGEXP, + // The part represents a matching group that matches code points up to the + // next separator code point. This is typically used for a named group like + // ":foo" that does not have a custom regular expression. + SEGMENT_WILDCARD, + // The part represents a matching group that greedily matches all code points. + // This is typically used for the "*" wildcard matching group. + FULL_WILDCARD, +}; -#endif // ADA_CHECKERS_H -/* end file include/ada/checkers.h */ -/* begin file include/ada/url.h */ -/** - * @file url.h - * @brief Declaration for the URL - */ -#ifndef ADA_URL_H -#define ADA_URL_H +enum class url_pattern_part_modifier : uint8_t { + // The part does not have a modifier. + none, + // The part has an optional modifier indicated by the U+003F (?) code point. + optional, + // The part has a "zero or more" modifier indicated by the U+002A (*) code + // point. + zero_or_more, + // The part has a "one or more" modifier indicated by the U+002B (+) code + // point. + one_or_more, +}; -#include -#include -#include -#include -#include -#include +// @see https://urlpattern.spec.whatwg.org/#part +class url_pattern_part { + public: + url_pattern_part(url_pattern_part_type _type, std::string&& _value, + url_pattern_part_modifier _modifier) + : type(_type), value(std::move(_value)), modifier(_modifier) {} + + url_pattern_part(url_pattern_part_type _type, std::string&& _value, + url_pattern_part_modifier _modifier, std::string&& _name, + std::string&& _prefix, std::string&& _suffix) + : type(_type), + value(std::move(_value)), + modifier(_modifier), + name(std::move(_name)), + prefix(std::move(_prefix)), + suffix(std::move(_suffix)) {} + // A part has an associated type, a string, which must be set upon creation. + url_pattern_part_type type; + // A part has an associated value, a string, which must be set upon creation. + std::string value; + // A part has an associated modifier a string, which must be set upon + // creation. + url_pattern_part_modifier modifier; + // A part has an associated name, a string, initially the empty string. + std::string name{}; + // A part has an associated prefix, a string, initially the empty string. + std::string prefix{}; + // A part has an associated suffix, a string, initially the empty string. + std::string suffix{}; + + inline bool is_regexp() const noexcept; +}; -namespace ada { +// @see https://urlpattern.spec.whatwg.org/#options-header +struct url_pattern_compile_component_options { + url_pattern_compile_component_options() = default; + explicit url_pattern_compile_component_options( + std::optional new_delimiter = std::nullopt, + std::optional new_prefix = std::nullopt) + : delimiter(new_delimiter), prefix(new_prefix) {} -/** - * @brief Generic URL struct reliant on std::string instantiation. - * - * @details To disambiguate from a valid URL string it can also be referred to - * as a URL record. A URL is a struct that represents a universal identifier. - * Unlike the url_aggregator, the ada::url represents the different components - * of a parsed URL as independent std::string instances. This makes the - * structure heavier and more reliant on memory allocations. When getting - * components from the parsed URL, a new std::string is typically constructed. - * - * @see https://url.spec.whatwg.org/#url-representation - */ -struct url : url_base { - url() = default; - url(const url& u) = default; - url(url&& u) noexcept = default; - url& operator=(url&& u) noexcept = default; - url& operator=(const url& u) = default; - ~url() override = default; + inline std::string_view get_delimiter() const ada_warn_unused; + inline std::string_view get_prefix() const ada_warn_unused; + + // @see https://urlpattern.spec.whatwg.org/#options-ignore-case + bool ignore_case = false; + + static url_pattern_compile_component_options DEFAULT; + static url_pattern_compile_component_options HOSTNAME; + static url_pattern_compile_component_options PATHNAME; + + private: + // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point + std::optional delimiter{}; + // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point + std::optional prefix{}; +}; + +// The default options is an options struct with delimiter code point set to +// the empty string and prefix code point set to the empty string. +inline url_pattern_compile_component_options + url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt); + +// The hostname options is an options struct with delimiter code point set +// "." and prefix code point set to the empty string. +inline url_pattern_compile_component_options + url_pattern_compile_component_options::HOSTNAME('.', std::nullopt); + +// The pathname options is an options struct with delimiter code point set +// "/" and prefix code point set to "/". +inline url_pattern_compile_component_options + url_pattern_compile_component_options::PATHNAME('/', '/'); + +// A struct providing the URLPattern matching results for a single +// URL component. The URLPatternComponentResult is only ever used +// as a member attribute of a URLPatternResult struct. The +// URLPatternComponentResult API is defined as part of the URLPattern +// specification. +struct url_pattern_component_result { + std::string input; + std::unordered_map> groups; + + bool operator==(const url_pattern_component_result&) const; + +#if ADA_TESTING + friend void PrintTo(const url_pattern_component_result& result, + std::ostream* os) { + *os << "input: '" << result.input << "', group: "; + for (const auto& group : result.groups) { + *os << "(" << group.first << ", " << group.second.value_or("undefined") + << ") "; + } + } +#endif // ADA_TESTING +}; + +template +class url_pattern_component { + public: + url_pattern_component() = default; + + // This function explicitly takes a std::string because it is moved. + // To avoid unnecessary copy, move each value while calling the constructor. + url_pattern_component(std::string&& new_pattern, + typename regex_provider::regex_type&& new_regexp, + std::vector&& new_group_name_list, + bool new_has_regexp_groups) + : regexp(std::move(new_regexp)), + pattern(std::move(new_pattern)), + group_name_list(std::move(new_group_name_list)), + has_regexp_groups(new_has_regexp_groups) {} + + // @see https://urlpattern.spec.whatwg.org/#compile-a-component + template + static tl::expected compile( + std::string_view input, F& encoding_callback, + url_pattern_compile_component_options& options); + + // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result + url_pattern_component_result create_component_match_result( + std::string&& input, + std::vector>&& exec_result); + +#if ADA_TESTING + friend void PrintTo(const url_pattern_component& component, + std::ostream* os) { + *os << "pattern: '" << component.pattern + << "', has_regexp_groups: " << component.has_regexp_groups + << "group_name_list: "; + for (const auto& name : component.group_name_list) { + *os << name << ", "; + } + } +#endif // ADA_TESTING + + typename regex_provider::regex_type regexp{}; + std::string pattern{}; + std::vector group_name_list{}; + bool has_regexp_groups = false; +}; + +// A URLPattern input can be either a string or a URLPatternInit object. +// If it is a string, it must be a valid UTF-8 string. +using url_pattern_input = std::variant; + +// A struct providing the URLPattern matching results for all +// components of a URL. The URLPatternResult API is defined as +// part of the URLPattern specification. +struct url_pattern_result { + std::vector inputs; + url_pattern_component_result protocol; + url_pattern_component_result username; + url_pattern_component_result password; + url_pattern_component_result hostname; + url_pattern_component_result port; + url_pattern_component_result pathname; + url_pattern_component_result search; + url_pattern_component_result hash; +}; + +struct url_pattern_options { + bool ignore_case = false; + +#if ADA_TESTING + friend void PrintTo(const url_pattern_options& options, std::ostream* os) { + *os << "ignore_case: '" << options.ignore_case; + } +#endif // ADA_TESTING +}; + +// URLPattern is a Web Platform standard API for matching URLs against a +// pattern syntax (think of it as a regular expression for URLs). It is +// defined in https://wicg.github.io/urlpattern. +// More information about the URL Pattern syntax can be found at +// https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API +// +// We require all strings to be valid UTF-8: it is the user's responsibility +// to ensure that the provided strings are valid UTF-8. +template +class url_pattern { + public: + url_pattern() = default; /** - * @private - * A URL's username is an ASCII string identifying a username. It is initially - * the empty string. + * If non-null, base_url must pointer at a valid UTF-8 string. + * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec */ - std::string username{}; + result> exec( + const url_pattern_input& input, + const std::string_view* base_url = nullptr); /** - * @private - * A URL's password is an ASCII string identifying a password. It is initially - * the empty string. + * If non-null, base_url must pointer at a valid UTF-8 string. + * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test */ - std::string password{}; + result test(const url_pattern_input& input, + const std::string_view* base_url = nullptr); /** - * @private - * A URL's host is null or a host. It is initially null. + * @see https://urlpattern.spec.whatwg.org/#url-pattern-match + * This function expects a valid UTF-8 string if input is a string. */ - std::optional host{}; + result> match( + const url_pattern_input& input, + const std::string_view* base_url_string = nullptr); + + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol + [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username + [[nodiscard]] std::string_view get_username() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password + [[nodiscard]] std::string_view get_password() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname + [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port + [[nodiscard]] std::string_view get_port() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname + [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search + [[nodiscard]] std::string_view get_search() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash + [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound; + + // If ignoreCase is true, the JavaScript regular expression created for each + // pattern must use the `vi` flag. Otherwise, they must use the `v` flag. + [[nodiscard]] bool ignore_case() const; + + // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups + [[nodiscard]] bool has_regexp_groups() const; + +#if ADA_TESTING + friend void PrintTo(const url_pattern& c, std::ostream* os) { + *os << "protocol_component: '" << c.get_protocol() << ", "; + *os << "username_component: '" << c.get_username() << ", "; + *os << "password_component: '" << c.get_password() << ", "; + *os << "hostname_component: '" << c.get_hostname() << ", "; + *os << "port_component: '" << c.get_port() << ", "; + *os << "pathname_component: '" << c.get_pathname() << ", "; + *os << "search_component: '" << c.get_search() << ", "; + *os << "hash_component: '" << c.get_hash(); + } +#endif // ADA_TESTING + + template + friend tl::expected, errors> parser::parse_url_pattern_impl( + std::variant&& input, + const std::string_view* base_url, const url_pattern_options* options); /** * @private - * A URL's port is either null or a 16-bit unsigned integer that identifies a - * networking port. It is initially null. + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - std::optional port{}; - + url_pattern_component protocol_component{}; /** * @private - * A URL's path is either an ASCII string or a list of zero or more ASCII - * strings, usually identifying a location. + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - std::string path{}; - + url_pattern_component username_component{}; /** * @private - * A URL's query is either null or an ASCII string. It is initially null. + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - std::optional query{}; - + url_pattern_component password_component{}; /** * @private - * A URL's fragment is either null or an ASCII string that can be used for - * further processing on the resource the URL's other components identify. It - * is initially null. + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - std::optional hash{}; - - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if the URL has a (non default) port */ - [[nodiscard]] inline bool has_port() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - [[nodiscard]] bool has_valid_domain() const noexcept override; - + url_pattern_component hostname_component{}; /** - * Returns a JSON string representation of this URL. + * @private + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - [[nodiscard]] std::string to_string() const override; - + url_pattern_component port_component{}; /** - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer + * @private + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - [[nodiscard]] ada_really_inline std::string get_href() const noexcept; - + url_pattern_component pathname_component{}; /** - * The origin getter steps are to return the serialization of this's URL's - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin + * @private + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - [[nodiscard]] std::string get_origin() const noexcept override; - + url_pattern_component search_component{}; /** - * The protocol getter steps are to return this's URL's scheme, followed by - * U+003A (:). - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-protocol + * @private + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - [[nodiscard]] std::string get_protocol() const noexcept; - + url_pattern_component hash_component{}; /** - * Return url's host, serialized, followed by U+003A (:) and url's port, - * serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-host + * @private + * We can not make this private due to a LLVM bug. + * Ref: https://github.com/ada-url/ada/pull/859 */ - [[nodiscard]] std::string get_host() const noexcept; + bool ignore_case_ = false; +}; +} // namespace ada +#endif // ADA_INCLUDE_URL_PATTERN +#endif +/* end file include/ada/url_pattern.h */ +/* begin file include/ada/url_pattern_helpers.h */ +/** + * @file url_pattern_helpers.h + * @brief Declaration for the URLPattern helpers. + */ +#ifndef ADA_URL_PATTERN_HELPERS_H +#define ADA_URL_PATTERN_HELPERS_H - /** - * Return this's URL's host, serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - [[nodiscard]] std::string get_hostname() const noexcept; - /** - * The pathname getter steps are to return the result of URL path serializing - * this's URL. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-pathname - */ - [[nodiscard]] std::string_view get_pathname() const noexcept; +#include +#include +#include - /** - * Compute the pathname length in bytes without instantiating a view or a - * string. - * @return size of the pathname in bytes - * @see https://url.spec.whatwg.org/#dom-url-pathname - */ - [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept; +#if ADA_INCLUDE_URL_PATTERN +namespace ada { +enum class errors : uint8_t; +} + +namespace ada::url_pattern_helpers { + +// @see https://urlpattern.spec.whatwg.org/#token +enum class token_type : uint8_t { + INVALID_CHAR, // 0 + OPEN, // 1 + CLOSE, // 2 + REGEXP, // 3 + NAME, // 4 + CHAR, // 5 + ESCAPED_CHAR, // 6 + OTHER_MODIFIER, // 7 + ASTERISK, // 8 + END, // 9 +}; - /** - * Return U+003F (?), followed by this's URL's query. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - [[nodiscard]] std::string get_search() const noexcept; +#ifdef ADA_TESTING +std::string to_string(token_type type); +#endif // ADA_TESTING - /** - * The username getter steps are to return this's URL's username. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - [[nodiscard]] const std::string& get_username() const noexcept; +// @see https://urlpattern.spec.whatwg.org/#tokenize-policy +enum class token_policy { + strict, + lenient, +}; - /** - * @return Returns true on successful operation. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - bool set_username(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#tokens +class token { + public: + token(token_type _type, size_t _index, std::string&& _value) + : type(_type), index(_index), value(std::move(_value)) {} - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - bool set_password(std::string_view input); + // A token has an associated type, a string, initially "invalid-char". + token_type type = token_type::INVALID_CHAR; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - bool set_port(std::string_view input); + // A token has an associated index, a number, initially 0. It is the position + // of the first code point in the pattern string represented by the token. + size_t index = 0; - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - void set_hash(std::string_view input); + // A token has an associated value, a string, initially the empty string. It + // contains the code points from the pattern string represented by the token. + std::string value{}; +}; - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - void set_search(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#pattern-parser +template +class url_pattern_parser { + public: + url_pattern_parser(F& encoding_callback_, + std::string_view segment_wildcard_regexp_) + : encoding_callback(encoding_callback_), + segment_wildcard_regexp(segment_wildcard_regexp_) {} + + bool can_continue() const { return index < tokens.size(); } + + // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token + token* try_consume_token(token_type type); + // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token + token* try_consume_modifier_token(); + // @see + // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token + token* try_consume_regexp_or_wildcard_token(const token* name_token); + // @see https://urlpattern.spec.whatwg.org/#consume-text + std::string consume_text(); + // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token + bool consume_required_token(token_type type); + // @see + // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value + std::optional maybe_add_part_from_the_pending_fixed_value() + ada_warn_unused; + // @see https://urlpattern.spec.whatwg.org/#add-a-part + std::optional add_part(std::string_view prefix, token* name_token, + token* regexp_or_wildcard_token, + std::string_view suyffix, + token* modifier_token) ada_warn_unused; + + std::vector tokens{}; + F& encoding_callback; + std::string segment_wildcard_regexp; + std::vector parts{}; + std::string pending_fixed_value{}; + size_t index = 0; + size_t next_numeric_name = 0; +}; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - bool set_pathname(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#tokenizer +class Tokenizer { + public: + explicit Tokenizer(std::string_view new_input, token_policy new_policy) + : input(new_input), policy(new_policy) {} - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - bool set_host(std::string_view input); + // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point + constexpr void get_next_code_point(); - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - bool set_hostname(std::string_view input); + // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point + constexpr void seek_and_get_next_code_point(size_t index); - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - bool set_protocol(std::string_view input); + // @see https://urlpattern.spec.whatwg.org/#add-a-token - /** - * @see https://url.spec.whatwg.org/#dom-url-href - */ - bool set_href(std::string_view input); + void add_token(token_type type, size_t next_position, size_t value_position, + size_t value_length); - /** - * The password getter steps are to return this's URL's password. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - [[nodiscard]] const std::string& get_password() const noexcept; + // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length + void add_token_with_default_length(token_type type, size_t next_position, + size_t value_position); - /** - * Return this's URL's port, serialized. - * @return a newly constructed string representing the port. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - [[nodiscard]] std::string get_port() const noexcept; + // @see + // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length + void add_token_with_defaults(token_type type); - /** - * Return U+0023 (#), followed by this's URL's fragment. - * @return a newly constructed string representing the hash. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - [[nodiscard]] std::string get_hash() const noexcept; + // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error + std::optional process_tokenizing_error( + size_t next_position, size_t value_position) ada_warn_unused; - /** - * A URL includes credentials if its username or password is not the empty - * string. - */ - [[nodiscard]] ada_really_inline bool has_credentials() const noexcept; - - /** - * Useful for implementing efficient serialization for the URL. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - * - * Inspired after servo/url - * - * @return a newly constructed component. - * - * @see - * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 - */ - [[nodiscard]] ada_really_inline ada::url_components get_components() - const noexcept; - /** @return true if the URL has a hash component */ - [[nodiscard]] inline bool has_hash() const noexcept override; - /** @return true if the URL has a search component */ - [[nodiscard]] inline bool has_search() const noexcept override; + friend tl::expected, errors> tokenize( + std::string_view input, token_policy policy); private: - friend ada::url ada::parser::parse_url(std::string_view, - const ada::url*); - friend ada::url_aggregator ada::parser::parse_url( - std::string_view, const ada::url_aggregator*); - friend void ada::helpers::strip_trailing_spaces_from_opaque_path( - ada::url& url) noexcept; + // has an associated input, a pattern string, initially the empty string. + std::string input; + // has an associated policy, a tokenize policy, initially "strict". + token_policy policy; + // has an associated token list, a token list, initially an empty list. + std::vector token_list{}; + // has an associated index, a number, initially 0. + size_t index = 0; + // has an associated next index, a number, initially 0. + size_t next_index = 0; + // has an associated code point, a Unicode code point, initially null. + char32_t code_point{}; +}; - friend ada::url ada::parser::parse_url_impl(std::string_view, - const ada::url*); - friend ada::url_aggregator ada::parser::parse_url_impl< - ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*); +// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser +template +struct constructor_string_parser { + explicit constructor_string_parser(std::string_view new_input, + std::vector&& new_token_list) + : input(new_input), token_list(std::move(new_token_list)) {} + // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string + static tl::expected parse(std::string_view input); + + // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state + enum class State { + INIT, + PROTOCOL, + AUTHORITY, + USERNAME, + PASSWORD, + HOSTNAME, + PORT, + PATHNAME, + SEARCH, + HASH, + DONE, + }; - inline void update_unencoded_base_hash(std::string_view input); - inline void update_base_hostname(std::string_view input); - inline void update_base_search(std::string_view input); - inline void update_base_search(std::string_view input, - const uint8_t query_percent_encode_set[]); - inline void update_base_search(std::optional input); - inline void update_base_pathname(std::string_view input); - inline void update_base_username(std::string_view input); - inline void update_base_password(std::string_view input); - inline void update_base_port(std::optional input); + // @see + // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag + std::optional compute_protocol_matches_special_scheme_flag(); - /** - * Sets the host or hostname according to override condition. - * Return true on success. - * @see https://url.spec.whatwg.org/#hostname-state - */ - template - bool set_host_or_hostname(std::string_view input); + private: + // @see https://urlpattern.spec.whatwg.org/#rewind + constexpr void rewind(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix + constexpr bool is_hash_prefix(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix + constexpr bool is_search_prefix(); + + // @see https://urlpattern.spec.whatwg.org/#change-state + void change_state(State state, size_t skip); + + // @see https://urlpattern.spec.whatwg.org/#is-a-group-open + constexpr bool is_group_open() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-group-close + constexpr bool is_group_close() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix + constexpr bool is_protocol_suffix() const; + + // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes + constexpr bool next_is_authority_slashes() const; + + // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator + constexpr bool is_an_identity_terminator() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start + constexpr bool is_pathname_start() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix + constexpr bool is_password_prefix() const; + + // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open + constexpr bool is_an_ipv6_open() const; + + // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close + constexpr bool is_an_ipv6_close() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix + constexpr bool is_port_prefix() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char + constexpr bool is_non_special_pattern_char(size_t index, + uint32_t value) const; + + // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token + constexpr const token* get_safe_token(size_t index) const; + + // @see https://urlpattern.spec.whatwg.org/#make-a-component-string + std::string make_component_string(); + // has an associated input, a string, which must be set upon creation. + std::string input; + // has an associated token list, a token list, which must be set upon + // creation. + std::vector token_list; + // has an associated result, a URLPatternInit, initially set to a new + // URLPatternInit. + url_pattern_init result{}; + // has an associated component start, a number, initially set to 0. + size_t component_start = 0; + // has an associated token index, a number, initially set to 0. + size_t token_index = 0; + // has an associated token increment, a number, initially set to 1. + size_t token_increment = 1; + // has an associated group depth, a number, initially set to 0. + size_t group_depth = 0; + // has an associated hostname IPv6 bracket depth, a number, initially set to + // 0. + size_t hostname_ipv6_bracket_depth = 0; + // has an associated protocol matches a special scheme flag, a boolean, + // initially set to false. + bool protocol_matches_a_special_scheme_flag = false; + // has an associated state, a string, initially set to "init". + State state = State::INIT; +}; - /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv4-parser - */ - [[nodiscard]] bool parse_ipv4(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol +tl::expected canonicalize_protocol(std::string_view input); - /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv6-parser - */ - [[nodiscard]] bool parse_ipv6(std::string_view input); +// @see https://wicg.github.io/urlpattern/#canonicalize-a-username +tl::expected canonicalize_username(std::string_view input); - /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-opaque-host-parser - */ - [[nodiscard]] bool parse_opaque_host(std::string_view input); +// @see https://wicg.github.io/urlpattern/#canonicalize-a-password +tl::expected canonicalize_password(std::string_view input); - /** - * A URL's scheme is an ASCII string that identifies the type of URL and can - * be used to dispatch a URL for further processing after parsing. It is - * initially the empty string. We only set non_special_scheme when the scheme - * is non-special, otherwise we avoid constructing string. - * - * Special schemes are stored in ada::scheme::details::is_special_list so we - * typically do not need to store them in each url instance. - */ - std::string non_special_scheme{}; +// @see https://wicg.github.io/urlpattern/#canonicalize-a-password +tl::expected canonicalize_hostname(std::string_view input); - /** - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". - */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; +// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname +tl::expected canonicalize_ipv6_hostname( + std::string_view input); - ada_really_inline size_t parse_port( - std::string_view view, bool check_trailing_content) noexcept override; +// @see https://wicg.github.io/urlpattern/#canonicalize-a-port +tl::expected canonicalize_port(std::string_view input); - ada_really_inline size_t parse_port(std::string_view view) noexcept override { - return this->parse_port(view, false); - } +// @see https://wicg.github.io/urlpattern/#canonicalize-a-port +tl::expected canonicalize_port_with_protocol( + std::string_view input, std::string_view protocol); - /** - * Take the scheme from another URL. The scheme string is copied from the - * provided url. - */ - inline void copy_scheme(const ada::url& u); +// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname +tl::expected canonicalize_pathname(std::string_view input); - /** - * Parse the host from the provided input. We assume that - * the input does not contain spaces or tabs. Control - * characters and spaces are not trimmed (they should have - * been removed if needed). - * Return true on success. - * @see https://url.spec.whatwg.org/#host-parsing - */ - [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); +// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname +tl::expected canonicalize_opaque_pathname( + std::string_view input); - template - [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input); +// @see https://wicg.github.io/urlpattern/#canonicalize-a-search +tl::expected canonicalize_search(std::string_view input); - inline void clear_pathname() override; - inline void clear_search() override; - inline void set_protocol_as_file(); +// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash +tl::expected canonicalize_hash(std::string_view input); - /** - * Parse the path from the provided input. - * Return true on success. Control characters not - * trimmed from the ends (they should have - * been removed if needed). - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ - ada_really_inline void parse_path(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#tokenize +tl::expected, errors> tokenize(std::string_view input, + token_policy policy); - /** - * Set the scheme for this URL. The provided scheme should be a valid - * scheme string, be lower-cased, not contain spaces or tabs. It should - * have no spurious trailing or leading content. - */ - inline void set_scheme(std::string&& new_scheme) noexcept; +// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string +std::string process_base_url_string(std::string_view input, + url_pattern_init::process_type type); - /** - * Take the scheme from another URL. The scheme string is moved from the - * provided url. - */ - inline void copy_scheme(ada::url&& u) noexcept; +// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string +std::string escape_pattern_string(std::string_view input); -}; // struct url +// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string +std::string escape_regexp_string(std::string_view input); -inline std::ostream& operator<<(std::ostream& out, const ada::url& u); -} // namespace ada +// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname +constexpr bool is_absolute_pathname( + std::string_view input, url_pattern_init::process_type type) noexcept; -#endif // ADA_URL_H -/* end file include/ada/url.h */ +// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string +template +tl::expected, errors> parse_pattern_string( + std::string_view input, url_pattern_compile_component_options& options, + F& encoding_callback); -#include -#include -#if ADA_REGULAR_VISUAL_STUDIO -#include -#endif // ADA_REGULAR_VISUAL_STUDIO +// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string +std::string generate_pattern_string( + std::vector& part_list, + url_pattern_compile_component_options& options); -namespace ada { +// @see +// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list +std::tuple> +generate_regular_expression_and_name_list( + const std::vector& part_list, + url_pattern_compile_component_options options); -[[nodiscard]] ada_really_inline bool url_base::is_special() const noexcept { - return type != ada::scheme::NOT_SPECIAL; -} +// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address +bool is_ipv6_address(std::string_view input) noexcept; -[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept { - return ada::scheme::get_special_port(type); -} +// @see +// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme +template +bool protocol_component_matches_special_scheme( + ada::url_pattern_component& input); -[[nodiscard]] ada_really_inline uint16_t -url_base::scheme_default_port() const noexcept { - return scheme::get_special_port(type); -} +// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string +std::string convert_modifier_to_string(url_pattern_part_modifier modifier); -} // namespace ada +// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp +std::string generate_segment_wildcard_regexp( + url_pattern_compile_component_options options); -#endif // ADA_URL_BASE_INL_H -/* end file include/ada/url_base-inl.h */ -/* begin file include/ada/url-inl.h */ -/** - * @file url-inl.h - * @brief Definitions for the URL - */ -#ifndef ADA_URL_INL_H -#define ADA_URL_INL_H +} // namespace ada::url_pattern_helpers +#endif // ADA_INCLUDE_URL_PATTERN +#endif +/* end file include/ada/url_pattern_helpers.h */ -#include #include -#if ADA_REGULAR_VISUAL_STUDIO -#include -#endif // ADA_REGULAR_VISUAL_STUDIO - -namespace ada { -[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept { - return !username.empty() || !password.empty(); -} -[[nodiscard]] ada_really_inline bool url::has_port() const noexcept { - return port.has_value(); -} -[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const { - return !host.has_value() || host.value().empty() || - type == ada::scheme::type::FILE; -} -[[nodiscard]] inline bool url::has_empty_hostname() const noexcept { - if (!host.has_value()) { - return false; - } - return host.value().empty(); -} -[[nodiscard]] inline bool url::has_hostname() const noexcept { - return host.has_value(); -} -inline std::ostream& operator<<(std::ostream& out, const ada::url& u) { - return out << u.to_string(); -} - -[[nodiscard]] size_t url::get_pathname_length() const noexcept { - return path.size(); -} - -[[nodiscard]] ada_really_inline ada::url_components url::get_components() - const noexcept { - url_components out{}; - - // protocol ends with ':'. for example: "https:" - out.protocol_end = uint32_t(get_protocol().size()); - - // Trailing index is always the next character of the current one. - size_t running_index = out.protocol_end; - - if (host.has_value()) { - // 2 characters for "//" and 1 character for starting index - out.host_start = out.protocol_end + 2; - - if (has_credentials()) { - out.username_end = uint32_t(out.host_start + username.size()); - - out.host_start += uint32_t(username.size()); - - if (!password.empty()) { - out.host_start += uint32_t(password.size() + 1); - } - - out.host_end = uint32_t(out.host_start + host.value().size()); - } else { - out.username_end = out.host_start; +#include +#include - // Host does not start with "@" if it does not include credentials. - out.host_end = uint32_t(out.host_start + host.value().size()) - 1; +namespace ada::parser { +#if ADA_INCLUDE_URL_PATTERN +template +tl::expected, errors> parse_url_pattern_impl( + std::variant&& input, + const std::string_view* base_url, const url_pattern_options* options) { + // Let init be null. + url_pattern_init init; + + // If input is a scalar value string then: + if (std::holds_alternative(input)) { + // Set init to the result of running parse a constructor string given input. + auto parse_result = + url_pattern_helpers::constructor_string_parser::parse( + std::get(input)); + if (!parse_result) { + ada_log("constructor_string_parser::parse failed"); + return tl::unexpected(parse_result.error()); + } + init = std::move(*parse_result); + // If baseURL is null and init["protocol"] does not exist, then throw a + // TypeError. + if (!base_url && !init.protocol) { + ada_log("base url is null and protocol is not set"); + return tl::unexpected(errors::type_error); } - running_index = out.host_end + 1; + // If baseURL is not null, set init["baseURL"] to baseURL. + if (base_url) { + init.base_url = std::string(*base_url); + } } else { - // Update host start and end date to the same index, since it does not - // exist. - out.host_start = out.protocol_end; - out.host_end = out.host_start; - - if (!has_opaque_path && checkers::begins_with(path, "//")) { - // If url's host is null, url does not have an opaque path, url's path's - // size is greater than 1, and url's path[0] is the empty string, then - // append U+002F (/) followed by U+002E (.) to output. - running_index = out.protocol_end + 2; - } else { - running_index = out.protocol_end; + // Assert: input is a URLPatternInit. + ADA_ASSERT_TRUE(std::holds_alternative(input)); + // If baseURL is not null, then throw a TypeError. + if (base_url) { + ada_log("base url is not null"); + return tl::unexpected(errors::type_error); + } + // Optimization: Avoid copy by moving the input value. + // Set init to input. + init = std::move(std::get(input)); + } + + // Let processedInit be the result of process a URLPatternInit given init, + // "pattern", null, null, null, null, null, null, null, and null. + auto processed_init = + url_pattern_init::process(init, url_pattern_init::process_type::pattern); + if (!processed_init) { + ada_log("url_pattern_init::process failed for init and 'pattern'"); + return tl::unexpected(processed_init.error()); + } + + // For each componentName of "protocol", "username", "password", "hostname", + // "port", "pathname", "search", "hash" If processedInit[componentName] does + // not exist, then set processedInit[componentName] to "*". + ADA_ASSERT_TRUE(processed_init.has_value()); + if (!processed_init->protocol) processed_init->protocol = "*"; + if (!processed_init->username) processed_init->username = "*"; + if (!processed_init->password) processed_init->password = "*"; + if (!processed_init->hostname) processed_init->hostname = "*"; + if (!processed_init->port) processed_init->port = "*"; + if (!processed_init->pathname) processed_init->pathname = "*"; + if (!processed_init->search) processed_init->search = "*"; + if (!processed_init->hash) processed_init->hash = "*"; + + ada_log("-- processed_init->protocol: ", processed_init->protocol.value()); + ada_log("-- processed_init->username: ", processed_init->username.value()); + ada_log("-- processed_init->password: ", processed_init->password.value()); + ada_log("-- processed_init->hostname: ", processed_init->hostname.value()); + ada_log("-- processed_init->port: ", processed_init->port.value()); + ada_log("-- processed_init->pathname: ", processed_init->pathname.value()); + ada_log("-- processed_init->search: ", processed_init->search.value()); + ada_log("-- processed_init->hash: ", processed_init->hash.value()); + + // If processedInit["protocol"] is a special scheme and processedInit["port"] + // is a string which represents its corresponding default port in radix-10 + // using ASCII digits then set processedInit["port"] to the empty string. + // TODO: Optimization opportunity. + if (scheme::is_special(*processed_init->protocol)) { + std::string_view port = processed_init->port.value(); + if (std::to_string(scheme::get_special_port(*processed_init->protocol)) == + port) { + processed_init->port->clear(); } } - if (port.has_value()) { - out.port = *port; - running_index += helpers::fast_digit_count(*port) + 1; // Port omits ':' - } - - out.pathname_start = uint32_t(running_index); - - running_index += path.size(); + // Let urlPattern be a new URL pattern. + url_pattern url_pattern_{}; + + // Set urlPattern's protocol component to the result of compiling a component + // given processedInit["protocol"], canonicalize a protocol, and default + // options. + auto protocol_component = url_pattern_component::compile( + processed_init->protocol.value(), + url_pattern_helpers::canonicalize_protocol, + url_pattern_compile_component_options::DEFAULT); + if (!protocol_component) { + ada_log("url_pattern_component::compile failed for protocol ", + processed_init->protocol.value()); + return tl::unexpected(protocol_component.error()); + } + url_pattern_.protocol_component = std::move(*protocol_component); + + // Set urlPattern's username component to the result of compiling a component + // given processedInit["username"], canonicalize a username, and default + // options. + auto username_component = url_pattern_component::compile( + processed_init->username.value(), + url_pattern_helpers::canonicalize_username, + url_pattern_compile_component_options::DEFAULT); + if (!username_component) { + ada_log("url_pattern_component::compile failed for username ", + processed_init->username.value()); + return tl::unexpected(username_component.error()); + } + url_pattern_.username_component = std::move(*username_component); + + // Set urlPattern's password component to the result of compiling a component + // given processedInit["password"], canonicalize a password, and default + // options. + auto password_component = url_pattern_component::compile( + processed_init->password.value(), + url_pattern_helpers::canonicalize_password, + url_pattern_compile_component_options::DEFAULT); + if (!password_component) { + ada_log("url_pattern_component::compile failed for password ", + processed_init->password.value()); + return tl::unexpected(password_component.error()); + } + url_pattern_.password_component = std::move(*password_component); + + // TODO: Optimization opportunity. The following if statement can be + // simplified. + // If the result running hostname pattern is an IPv6 address given + // processedInit["hostname"] is true, then set urlPattern's hostname component + // to the result of compiling a component given processedInit["hostname"], + // canonicalize an IPv6 hostname, and hostname options. + if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) { + ada_log("processed_init->hostname is ipv6 address"); + // then set urlPattern's hostname component to the result of compiling a + // component given processedInit["hostname"], canonicalize an IPv6 hostname, + // and hostname options. + auto hostname_component = url_pattern_component::compile( + processed_init->hostname.value(), + url_pattern_helpers::canonicalize_ipv6_hostname, + url_pattern_compile_component_options::DEFAULT); + if (!hostname_component) { + ada_log("url_pattern_component::compile failed for ipv6 hostname ", + processed_init->hostname.value()); + return tl::unexpected(hostname_component.error()); + } + url_pattern_.hostname_component = std::move(*hostname_component); + } else { + // Otherwise, set urlPattern's hostname component to the result of compiling + // a component given processedInit["hostname"], canonicalize a hostname, and + // hostname options. + auto hostname_component = url_pattern_component::compile( + processed_init->hostname.value(), + url_pattern_helpers::canonicalize_hostname, + url_pattern_compile_component_options::HOSTNAME); + if (!hostname_component) { + ada_log("url_pattern_component::compile failed for hostname ", + processed_init->hostname.value()); + return tl::unexpected(hostname_component.error()); + } + url_pattern_.hostname_component = std::move(*hostname_component); + } + + // Set urlPattern's port component to the result of compiling a component + // given processedInit["port"], canonicalize a port, and default options. + auto port_component = url_pattern_component::compile( + processed_init->port.value(), url_pattern_helpers::canonicalize_port, + url_pattern_compile_component_options::DEFAULT); + if (!port_component) { + ada_log("url_pattern_component::compile failed for port ", + processed_init->port.value()); + return tl::unexpected(port_component.error()); + } + url_pattern_.port_component = std::move(*port_component); + + // Let compileOptions be a copy of the default options with the ignore case + // property set to options["ignoreCase"]. + auto compile_options = url_pattern_compile_component_options::DEFAULT; + if (options) { + compile_options.ignore_case = options->ignore_case; + } + + // TODO: Optimization opportunity: Simplify this if statement. + // If the result of running protocol component matches a special scheme given + // urlPattern's protocol component is true, then: + if (url_pattern_helpers::protocol_component_matches_special_scheme< + regex_provider>(url_pattern_.protocol_component)) { + // Let pathCompileOptions be copy of the pathname options with the ignore + // case property set to options["ignoreCase"]. + auto path_compile_options = url_pattern_compile_component_options::PATHNAME; + if (options) { + path_compile_options.ignore_case = options->ignore_case; + } - if (query.has_value()) { - out.search_start = uint32_t(running_index); - running_index += get_search().size(); - if (get_search().empty()) { - running_index++; + // Set urlPattern's pathname component to the result of compiling a + // component given processedInit["pathname"], canonicalize a pathname, and + // pathCompileOptions. + auto pathname_component = url_pattern_component::compile( + processed_init->pathname.value(), + url_pattern_helpers::canonicalize_pathname, path_compile_options); + if (!pathname_component) { + ada_log("url_pattern_component::compile failed for pathname ", + processed_init->pathname.value()); + return tl::unexpected(pathname_component.error()); + } + url_pattern_.pathname_component = std::move(*pathname_component); + } else { + // Otherwise set urlPattern's pathname component to the result of compiling + // a component given processedInit["pathname"], canonicalize an opaque + // pathname, and compileOptions. + auto pathname_component = url_pattern_component::compile( + processed_init->pathname.value(), + url_pattern_helpers::canonicalize_opaque_pathname, compile_options); + if (!pathname_component) { + ada_log("url_pattern_component::compile failed for opaque pathname ", + processed_init->pathname.value()); + return tl::unexpected(pathname_component.error()); } + url_pattern_.pathname_component = std::move(*pathname_component); } - if (hash.has_value()) { - out.hash_start = uint32_t(running_index); + // Set urlPattern's search component to the result of compiling a component + // given processedInit["search"], canonicalize a search, and compileOptions. + auto search_component = url_pattern_component::compile( + processed_init->search.value(), url_pattern_helpers::canonicalize_search, + compile_options); + if (!search_component) { + ada_log("url_pattern_component::compile failed for search ", + processed_init->search.value()); + return tl::unexpected(search_component.error()); } + url_pattern_.search_component = std::move(*search_component); - return out; + // Set urlPattern's hash component to the result of compiling a component + // given processedInit["hash"], canonicalize a hash, and compileOptions. + auto hash_component = url_pattern_component::compile( + processed_init->hash.value(), url_pattern_helpers::canonicalize_hash, + compile_options); + if (!hash_component) { + ada_log("url_pattern_component::compile failed for hash ", + processed_init->hash.value()); + return tl::unexpected(hash_component.error()); + } + url_pattern_.hash_component = std::move(*hash_component); + + // Return urlPattern. + return url_pattern_; } +#endif // ADA_INCLUDE_URL_PATTERN -inline void url::update_base_hostname(std::string_view input) { host = input; } +} // namespace ada::parser + +#endif // ADA_PARSER_INL_H +/* end file include/ada/parser-inl.h */ +/* begin file include/ada/scheme-inl.h */ +/** + * @file scheme-inl.h + * @brief Definitions for the URL scheme. + */ +#ifndef ADA_SCHEME_INL_H +#define ADA_SCHEME_INL_H + + +namespace ada::scheme { + +/** + * @namespace ada::scheme::details + * @brief Includes the definitions for scheme specific entities + */ +namespace details { +// for use with is_special and get_special_port +// Spaces, if present, are removed from URL. +constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", + "ftp", "wss", "file", " "}; +// for use with get_special_port +constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; +} // namespace details + +/**** + * @private + * In is_special, get_scheme_type, and get_special_port, we + * use a standard hashing technique to find the index of the scheme in + * the is_special_list. The hashing technique is based on the size of + * the scheme and the first character of the scheme. It ensures that we + * do at most one string comparison per call. If the protocol is + * predictible (e.g., it is always "http"), we can get a better average + * performance by using a simpler approach where we loop and compare + * scheme with all possible protocols starting with the most likely + * protocol. Doing multiple comparisons may have a poor worst case + * performance, however. In this instance, we choose a potentially + * slightly lower best-case performance for a better worst-case + * performance. We can revisit this choice at any time. + * + * Reference: + * Schmidt, Douglas C. "Gperf: A perfect hash function generator." + * More C++ gems 17 (2000). + * + * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function + * + * Reference: https://github.com/ada-url/ada/issues/617 + ****/ + +ada_really_inline constexpr bool is_special(std::string_view scheme) { + if (scheme.empty()) { + return false; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); +} +constexpr uint16_t get_special_port(std::string_view scheme) noexcept { + if (scheme.empty()) { + return 0; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return details::special_ports[hash_value]; + } else { + return 0; + } +} +constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { + return details::special_ports[int(type)]; +} +constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { + if (scheme.empty()) { + return ada::scheme::NOT_SPECIAL; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return ada::scheme::type(hash_value); + } else { + return ada::scheme::NOT_SPECIAL; + } +} + +} // namespace ada::scheme + +#endif // ADA_SCHEME_INL_H +/* end file include/ada/scheme-inl.h */ +/* begin file include/ada/serializers.h */ +/** + * @file serializers.h + * @brief Definitions for the URL serializers. + */ +#ifndef ADA_SERIALIZERS_H +#define ADA_SERIALIZERS_H + + +#include +#include + +/** + * @namespace ada::serializers + * @brief Includes the definitions for URL serializers + */ +namespace ada::serializers { + +/** + * Finds and returns the longest sequence of 0 values in a ipv6 input. + */ +void find_longest_sequence_of_ipv6_pieces( + const std::array& address, size_t& compress, + size_t& compress_length) noexcept; + +/** + * Serializes an ipv6 address. + * @details An IPv6 address is a 128-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv6-serializer + */ +std::string ipv6(const std::array& address) noexcept; + +/** + * Serializes an ipv4 address. + * @details An IPv4 address is a 32-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv4-serializer + */ +std::string ipv4(uint64_t address) noexcept; + +} // namespace ada::serializers + +#endif // ADA_SERIALIZERS_H +/* end file include/ada/serializers.h */ +/* begin file include/ada/state.h */ +/** + * @file state.h + * @brief Definitions for the states of the URL state machine. + */ +#ifndef ADA_STATE_H +#define ADA_STATE_H + + +#include + +namespace ada { + +/** + * @see https://url.spec.whatwg.org/#url-parsing + */ +enum class state { + /** + * @see https://url.spec.whatwg.org/#authority-state + */ + AUTHORITY, + + /** + * @see https://url.spec.whatwg.org/#scheme-start-state + */ + SCHEME_START, + + /** + * @see https://url.spec.whatwg.org/#scheme-state + */ + SCHEME, + + /** + * @see https://url.spec.whatwg.org/#host-state + */ + HOST, + + /** + * @see https://url.spec.whatwg.org/#no-scheme-state + */ + NO_SCHEME, + + /** + * @see https://url.spec.whatwg.org/#fragment-state + */ + FRAGMENT, + + /** + * @see https://url.spec.whatwg.org/#relative-state + */ + RELATIVE_SCHEME, + + /** + * @see https://url.spec.whatwg.org/#relative-slash-state + */ + RELATIVE_SLASH, + + /** + * @see https://url.spec.whatwg.org/#file-state + */ + FILE, + + /** + * @see https://url.spec.whatwg.org/#file-host-state + */ + FILE_HOST, + + /** + * @see https://url.spec.whatwg.org/#file-slash-state + */ + FILE_SLASH, + + /** + * @see https://url.spec.whatwg.org/#path-or-authority-state + */ + PATH_OR_AUTHORITY, + + /** + * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state + */ + SPECIAL_AUTHORITY_IGNORE_SLASHES, + + /** + * @see https://url.spec.whatwg.org/#special-authority-slashes-state + */ + SPECIAL_AUTHORITY_SLASHES, + + /** + * @see https://url.spec.whatwg.org/#special-relative-or-authority-state + */ + SPECIAL_RELATIVE_OR_AUTHORITY, + + /** + * @see https://url.spec.whatwg.org/#query-state + */ + QUERY, + + /** + * @see https://url.spec.whatwg.org/#path-state + */ + PATH, + + /** + * @see https://url.spec.whatwg.org/#path-start-state + */ + PATH_START, + + /** + * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state + */ + OPAQUE_PATH, + + /** + * @see https://url.spec.whatwg.org/#port-state + */ + PORT, +}; + +/** + * Stringify a URL state machine state. + */ +ada_warn_unused std::string to_string(ada::state s); + +} // namespace ada + +#endif // ADA_STATE_H +/* end file include/ada/state.h */ +/* begin file include/ada/unicode.h */ +/** + * @file unicode.h + * @brief Definitions for all unicode specific functions. + */ +#ifndef ADA_UNICODE_H +#define ADA_UNICODE_H + + +#include +#include +#include + +/** + * Unicode operations. These functions are not part of our public API and may + * change at any time. + * + * @private + * @namespace ada::unicode + * @brief Includes the definitions for unicode operations + */ +namespace ada::unicode { + +/** + * @private + * We receive a UTF-8 string representing a domain name. + * If the string is percent encoded, we apply percent decoding. + * + * Given a domain, we need to identify its labels. + * They are separated by label-separators: + * + * U+002E (.) FULL STOP + * U+FF0E FULLWIDTH FULL STOP + * U+3002 IDEOGRAPHIC FULL STOP + * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP + * + * They are all mapped to U+002E. + * + * We process each label into a string that should not exceed 63 octets. + * If the string is already punycode (starts with "xn--"), then we must + * scan it to look for unallowed code points. + * Otherwise, if the string is not pure ASCII, we need to transcode it + * to punycode by following RFC 3454 which requires us to + * - Map characters (see section 3), + * - Normalize (see section 4), + * - Reject forbidden characters, + * - Check for right-to-left characters and if so, check all requirements (see + * section 6), + * - Optionally reject based on unassigned code points (section 7). + * + * The Unicode standard provides a table of code points with a mapping, a list + * of forbidden code points and so forth. This table is subject to change and + * will vary based on the implementation. For Unicode 15, the table is at + * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt + * If you use ICU, they parse this table and map it to code using a Python + * script. + * + * The resulting strings should not exceed 255 octets according to RFC 1035 + * section 2.3.4. ICU checks for label size and domain size, but these errors + * are ignored. + * + * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * + */ +bool to_ascii(std::optional& out, std::string_view plain, + size_t first_percent); + +/** + * @private + * Checks if the input has tab or newline characters. + * + * @attention The has_tabs_or_newline function is a bottleneck and it is simple + * enough that compilers like GCC can 'autovectorize it'. + */ +ada_really_inline bool has_tabs_or_newline( + std::string_view user_input) noexcept; + +/** + * @private + * Checks if the input is a forbidden host code point. + * @see https://url.spec.whatwg.org/#forbidden-host-code-point + */ +ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept; + +/** + * @private + * Checks if the input contains a forbidden domain code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool contains_forbidden_domain_code_point( + const char* input, size_t length) noexcept; + +/** + * @private + * Checks if the input contains a forbidden domain code point in which case + * the first bit is set to 1. If the input contains an upper case ASCII letter, + * then the second bit is set to 1. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr uint8_t +contains_forbidden_domain_code_point_or_upper(const char* input, + size_t length) noexcept; + +/** + * @private + * Checks if the input is a forbidden domain code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool is_forbidden_domain_code_point( + char c) noexcept; + +/** + * @private + * Checks if the input is alphanumeric, '+', '-' or '.' + */ +ada_really_inline constexpr bool is_alnum_plus(char c) noexcept; + +/** + * @private + * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex + * digit. An ASCII upper hex digit is an ASCII digit or a code point in the + * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an + * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + */ +ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept; + +/** + * @private + * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9), + * inclusive. + */ +ada_really_inline constexpr bool is_ascii_digit(char c) noexcept; + +/** + * @private + * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII + * character. + */ +ada_really_inline constexpr bool is_ascii(char32_t c) noexcept; + +/** + * @private + * Checks if the input is a C0 control or space character. + * + * @details A C0 control or space is a C0 control or U+0020 SPACE. + * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION + * SEPARATOR ONE, inclusive. + */ +ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept; + +/** + * @private + * Checks if the input is a ASCII tab or newline character. + * + * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. + */ +ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept; + +/** + * @private + * @details A double-dot path segment must be ".." or an ASCII case-insensitive + * match for ".%2e", "%2e.", or "%2e%2e". + */ +ada_really_inline constexpr bool is_double_dot_path_segment( + std::string_view input) noexcept; + +/** + * @private + * @details A single-dot path segment must be "." or an ASCII case-insensitive + * match for "%2e". + */ +ada_really_inline constexpr bool is_single_dot_path_segment( + std::string_view input) noexcept; + +/** + * @private + * @details ipv4 character might contain 0-9 or a-f character ranges. + */ +ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept; + +/** + * @private + * @details Convert hex to binary. Caller is responsible to ensure that + * the parameter is an hexadecimal digit (0-9, A-F, a-f). + */ +ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept; + +/** + * @private + * first_percent should be = input.find('%') + * + * @todo It would be faster as noexcept maybe, but it could be unsafe since. + * @author Node.js + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 + * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + */ +std::string percent_decode(std::string_view input, size_t first_percent); + +/** + * @private + * Returns a percent-encoding string whether percent encoding was needed or not. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(std::string_view input, + const uint8_t character_set[]); +/** + * @private + * Returns a percent-encoded string version of input, while starting the percent + * encoding at the provided index. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(std::string_view input, + const uint8_t character_set[], size_t index); +/** + * @private + * Returns true if percent encoding was needed, in which case, we store + * the percent-encoded content in 'out'. If the boolean 'append' is set to + * true, the content is appended to 'out'. + * If percent encoding is not needed, out is left unchanged. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +template +bool percent_encode(std::string_view input, const uint8_t character_set[], + std::string& out); +/** + * @private + * Returns the index at which percent encoding should start, or (equivalently), + * the length of the prefix that does not require percent encoding. + */ +ada_really_inline size_t percent_encode_index(std::string_view input, + const uint8_t character_set[]); +/** + * @private + * Lowers the string in-place, assuming that the content is ASCII. + * Return true if the content was ASCII. + */ +constexpr bool to_lower_ascii(char* input, size_t length) noexcept; +} // namespace ada::unicode + +#endif // ADA_UNICODE_H +/* end file include/ada/unicode.h */ +/* begin file include/ada/url_base-inl.h */ +/** + * @file url_base-inl.h + * @brief Inline functions for url base + */ +#ifndef ADA_URL_BASE_INL_H +#define ADA_URL_BASE_INL_H + + +#include +#if ADA_REGULAR_VISUAL_STUDIO +#include +#endif // ADA_REGULAR_VISUAL_STUDIO + +namespace ada { + +[[nodiscard]] ada_really_inline constexpr bool url_base::is_special() + const noexcept { + return type != ada::scheme::NOT_SPECIAL; +} + +[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept { + return ada::scheme::get_special_port(type); +} + +[[nodiscard]] ada_really_inline uint16_t +url_base::scheme_default_port() const noexcept { + return scheme::get_special_port(type); +} + +} // namespace ada + +#endif // ADA_URL_BASE_INL_H +/* end file include/ada/url_base-inl.h */ +/* begin file include/ada/url-inl.h */ +/** + * @file url-inl.h + * @brief Definitions for the URL + */ +#ifndef ADA_URL_INL_H +#define ADA_URL_INL_H + + +#include +#include +#include +#if ADA_REGULAR_VISUAL_STUDIO +#include +#endif // ADA_REGULAR_VISUAL_STUDIO + +namespace ada { +[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept { + return !username.empty() || !password.empty(); +} +[[nodiscard]] ada_really_inline bool url::has_port() const noexcept { + return port.has_value(); +} +[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const { + return !host.has_value() || host.value().empty() || + type == ada::scheme::type::FILE; +} +[[nodiscard]] inline bool url::has_empty_hostname() const noexcept { + if (!host.has_value()) { + return false; + } + return host.value().empty(); +} +[[nodiscard]] inline bool url::has_hostname() const noexcept { + return host.has_value(); +} +inline std::ostream &operator<<(std::ostream &out, const ada::url &u) { + return out << u.to_string(); +} + +[[nodiscard]] size_t url::get_pathname_length() const noexcept { + return path.size(); +} + +[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept { + return path; +} + +[[nodiscard]] ada_really_inline ada::url_components url::get_components() + const noexcept { + url_components out{}; + + // protocol ends with ':'. for example: "https:" + out.protocol_end = uint32_t(get_protocol().size()); + + // Trailing index is always the next character of the current one. + // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) + size_t running_index = out.protocol_end; + + if (host.has_value()) { + // 2 characters for "//" and 1 character for starting index + out.host_start = out.protocol_end + 2; + + if (has_credentials()) { + out.username_end = uint32_t(out.host_start + username.size()); + + out.host_start += uint32_t(username.size()); + + if (!password.empty()) { + out.host_start += uint32_t(password.size() + 1); + } + + out.host_end = uint32_t(out.host_start + host.value().size()); + } else { + out.username_end = out.host_start; + + // Host does not start with "@" if it does not include credentials. + out.host_end = uint32_t(out.host_start + host.value().size()) - 1; + } + + running_index = out.host_end + 1; + } else { + // Update host start and end date to the same index, since it does not + // exist. + out.host_start = out.protocol_end; + out.host_end = out.host_start; + + if (!has_opaque_path && path.starts_with("//")) { + // If url's host is null, url does not have an opaque path, url's path's + // size is greater than 1, and url's path[0] is the empty string, then + // append U+002F (/) followed by U+002E (.) to output. + running_index = out.protocol_end + 2; + } else { + running_index = out.protocol_end; + } + } + + if (port.has_value()) { + out.port = *port; + running_index += helpers::fast_digit_count(*port) + 1; // Port omits ':' + } + + out.pathname_start = uint32_t(running_index); + + running_index += path.size(); + + if (query.has_value()) { + out.search_start = uint32_t(running_index); + running_index += get_search().size(); + if (get_search().empty()) { + running_index++; + } + } + + if (hash.has_value()) { + out.hash_start = uint32_t(running_index); + } + + return out; +} + +inline void url::update_base_hostname(std::string_view input) { host = input; } inline void url::update_unencoded_base_hash(std::string_view input) { // We do the percent encoding @@ -5763,130 +6714,556 @@ inline void url::update_unencoded_base_hash(std::string_view input) { ada::character_sets::FRAGMENT_PERCENT_ENCODE); } -inline void url::update_base_search(std::string_view input, - const uint8_t query_percent_encode_set[]) { - query = ada::unicode::percent_encode(input, query_percent_encode_set); +inline void url::update_base_search(std::string_view input, + const uint8_t query_percent_encode_set[]) { + query = ada::unicode::percent_encode(input, query_percent_encode_set); +} + +inline void url::update_base_search(std::optional &&input) { + query = std::move(input); +} + +inline void url::update_base_pathname(const std::string_view input) { + path = input; +} + +inline void url::update_base_username(const std::string_view input) { + username = input; +} + +inline void url::update_base_password(const std::string_view input) { + password = input; +} + +inline void url::update_base_port(std::optional input) { + port = input; +} + +constexpr void url::clear_pathname() { path.clear(); } + +constexpr void url::clear_search() { query = std::nullopt; } + +[[nodiscard]] constexpr bool url::has_hash() const noexcept { + return hash.has_value(); +} + +[[nodiscard]] constexpr bool url::has_search() const noexcept { + return query.has_value(); +} + +constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; } + +inline void url::set_scheme(std::string &&new_scheme) noexcept { + type = ada::scheme::get_scheme_type(new_scheme); + // We only move the 'scheme' if it is non-special. + if (!is_special()) { + non_special_scheme = std::move(new_scheme); + } +} + +constexpr void url::copy_scheme(ada::url &&u) noexcept { + non_special_scheme = u.non_special_scheme; + type = u.type; +} + +constexpr void url::copy_scheme(const ada::url &u) { + non_special_scheme = u.non_special_scheme; + type = u.type; +} + +[[nodiscard]] ada_really_inline std::string url::get_href() const noexcept { + std::string output = get_protocol(); + + if (host.has_value()) { + output += "//"; + if (has_credentials()) { + output += username; + if (!password.empty()) { + output += ":" + get_password(); + } + output += "@"; + } + output += host.value(); + if (port.has_value()) { + output += ":" + get_port(); + } + } else if (!has_opaque_path && path.starts_with("//")) { + // If url's host is null, url does not have an opaque path, url's path's + // size is greater than 1, and url's path[0] is the empty string, then + // append U+002F (/) followed by U+002E (.) to output. + output += "/."; + } + output += path; + if (query.has_value()) { + output += "?" + query.value(); + } + if (hash.has_value()) { + output += "#" + hash.value(); + } + return output; +} + +ada_really_inline size_t url::parse_port(std::string_view view, + bool check_trailing_content) noexcept { + ada_log("parse_port('", view, "') ", view.size()); + if (!view.empty() && view[0] == '-') { + ada_log("parse_port: view[0] == '0' && view.size() > 1"); + is_valid = false; + return 0; + } + uint16_t parsed_port{}; + auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port); + if (r.ec == std::errc::result_out_of_range) { + ada_log("parse_port: r.ec == std::errc::result_out_of_range"); + is_valid = false; + return 0; + } + ada_log("parse_port: ", parsed_port); + const auto consumed = size_t(r.ptr - view.data()); + ada_log("parse_port: consumed ", consumed); + if (check_trailing_content) { + is_valid &= + (consumed == view.size() || view[consumed] == '/' || + view[consumed] == '?' || (is_special() && view[consumed] == '\\')); + } + ada_log("parse_port: is_valid = ", is_valid); + if (is_valid) { + // scheme_default_port can return 0, and we should allow 0 as a base port. + auto default_port = scheme_default_port(); + bool is_port_valid = (default_port == 0 && parsed_port == 0) || + (default_port != parsed_port); + port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port) + : std::nullopt; + } + return consumed; +} + +} // namespace ada + +#endif // ADA_URL_H +/* end file include/ada/url-inl.h */ +/* begin file include/ada/url_components-inl.h */ +/** + * @file url_components.h + * @brief Declaration for the URL Components + */ +#ifndef ADA_URL_COMPONENTS_INL_H +#define ADA_URL_COMPONENTS_INL_H + + +namespace ada { + +[[nodiscard]] constexpr bool url_components::check_offset_consistency() + const noexcept { + /** + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + // These conditions can be made more strict. + if (protocol_end == url_components::omitted) { + return false; + } + uint32_t index = protocol_end; + + if (username_end == url_components::omitted) { + return false; + } + if (username_end < index) { + return false; + } + index = username_end; + + if (host_start == url_components::omitted) { + return false; + } + if (host_start < index) { + return false; + } + index = host_start; + + if (port != url_components::omitted) { + if (port > 0xffff) { + return false; + } + uint32_t port_length = helpers::fast_digit_count(port) + 1; + if (index + port_length < index) { + return false; + } + index += port_length; + } + + if (pathname_start == url_components::omitted) { + return false; + } + if (pathname_start < index) { + return false; + } + index = pathname_start; + + if (search_start != url_components::omitted) { + if (search_start < index) { + return false; + } + index = search_start; + } + + if (hash_start != url_components::omitted) { + if (hash_start < index) { + return false; + } + } + + return true; } -inline void url::update_base_search(std::optional input) { - query = input; -} +} // namespace ada +#endif +/* end file include/ada/url_components-inl.h */ +/* begin file include/ada/url_aggregator.h */ +/** + * @file url_aggregator.h + * @brief Declaration for the basic URL definitions + */ +#ifndef ADA_URL_AGGREGATOR_H +#define ADA_URL_AGGREGATOR_H + +#include +#include +#include +#include + + +namespace ada { + +namespace parser {} + +/** + * @brief Lightweight URL struct. + * + * @details The url_aggregator class aims to minimize temporary memory + * allocation while representing a parsed URL. Internally, it contains a single + * normalized URL (the href), and it makes available the components, mostly + * using std::string_view. + */ +struct url_aggregator : url_base { + url_aggregator() = default; + url_aggregator(const url_aggregator &u) = default; + url_aggregator(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(const url_aggregator &u) = default; + ~url_aggregator() override = default; + + bool set_href(std::string_view input); + bool set_host(std::string_view input); + bool set_hostname(std::string_view input); + bool set_protocol(std::string_view input); + bool set_username(std::string_view input); + bool set_password(std::string_view input); + bool set_port(std::string_view input); + bool set_pathname(std::string_view input); + void set_search(std::string_view input); + void set_hash(std::string_view input); + + [[nodiscard]] bool has_valid_domain() const noexcept override; + /** + * The origin getter steps are to return the serialization of this's URL's + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + /** + * Return the normalized string. + * This function does not allocate memory. + * It is highly efficient. + * @return a constant reference to the underlying normalized URL. + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer + */ + [[nodiscard]] constexpr std::string_view get_href() const noexcept + ada_lifetime_bound; + /** + * The username getter steps are to return this's URL's username. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + [[nodiscard]] std::string_view get_username() const noexcept + ada_lifetime_bound; + /** + * The password getter steps are to return this's URL's password. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] std::string_view get_password() const noexcept + ada_lifetime_bound; + /** + * Return this's URL's port, serialized. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string_view get_port() const noexcept ada_lifetime_bound; + /** + * Return U+0023 (#), followed by this's URL's fragment. + * This function does not allocate memory. + * @return a lightweight std::string_view.. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string_view get_hash() const noexcept ada_lifetime_bound; + /** + * Return url's host, serialized, followed by U+003A (:) and url's port, + * serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + [[nodiscard]] std::string_view get_host() const noexcept ada_lifetime_bound; + /** + * Return this's URL's host, serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + [[nodiscard]] std::string_view get_hostname() const noexcept + ada_lifetime_bound; + /** + * The pathname getter steps are to return the result of URL path serializing + * this's URL. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + [[nodiscard]] constexpr std::string_view get_pathname() const noexcept + ada_lifetime_bound; + /** + * Compute the pathname length in bytes without instantiating a view or a + * string. + * @return size of the pathname in bytes + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept; + /** + * Return U+003F (?), followed by this's URL's query. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + [[nodiscard]] std::string_view get_search() const noexcept ada_lifetime_bound; + /** + * The protocol getter steps are to return this's URL's scheme, followed by + * U+003A (:). + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string_view get_protocol() const noexcept + ada_lifetime_bound; + + /** + * A URL includes credentials if its username or password is not the empty + * string. + */ + [[nodiscard]] ada_really_inline constexpr bool has_credentials() + const noexcept; + + /** + * Useful for implementing efficient serialization for the URL. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + * + * Inspired after servo/url + * + * @return a constant reference to the underlying component attribute. + * + * @see + * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 + */ + [[nodiscard]] ada_really_inline const url_components &get_components() + const noexcept; + /** + * Returns a string representation of this URL. + */ + [[nodiscard]] std::string to_string() const override; + /** + * Returns a string diagram of this URL. + */ + [[nodiscard]] std::string to_diagram() const; + + /** + * Verifies that the parsed URL could be valid. Useful for debugging purposes. + * @return true if the URL is valid, otherwise return true of the offsets are + * possible. + */ + [[nodiscard]] constexpr bool validate() const noexcept; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] constexpr bool has_empty_hostname() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] constexpr bool has_hostname() const noexcept; + /** @return true if the URL has a non-empty username */ + [[nodiscard]] constexpr bool has_non_empty_username() const noexcept; + /** @return true if the URL has a non-empty password */ + [[nodiscard]] constexpr bool has_non_empty_password() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] constexpr bool has_port() const noexcept; + /** @return true if the URL has a password */ + [[nodiscard]] constexpr bool has_password() const noexcept; + /** @return true if the URL has a hash component */ + [[nodiscard]] constexpr bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] constexpr bool has_search() const noexcept override; + + inline void clear_port(); + inline void clear_hash(); + inline void clear_search() override; + + private: + // helper methods + friend void helpers::strip_trailing_spaces_from_opaque_path( + url_aggregator &url) noexcept; + // parse_url methods + friend url_aggregator parser::parse_url( + std::string_view, const url_aggregator *); + + friend url_aggregator parser::parse_url_impl( + std::string_view, const url_aggregator *); + friend url_aggregator parser::parse_url_impl( + std::string_view, const url_aggregator *); + +#if ADA_INCLUDE_URL_PATTERN + // url_pattern methods + template + friend tl::expected, errors> + parse_url_pattern_impl( + std::variant &&input, + const std::string_view *base_url, const url_pattern_options *options); +#endif // ADA_INCLUDE_URL_PATTERN + + std::string buffer{}; + url_components components{}; -inline void url::update_base_pathname(const std::string_view input) { - path = input; -} + /** + * Returns true if neither the search, nor the hash nor the pathname + * have been set. + * @return true if the buffer is ready to receive the path. + */ + [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; -inline void url::update_base_username(const std::string_view input) { - username = input; -} + inline void add_authority_slashes_if_needed() noexcept; -inline void url::update_base_password(const std::string_view input) { - password = input; -} + /** + * To optimize performance, you may indicate how much memory to allocate + * within this instance. + */ + constexpr void reserve(uint32_t capacity); -inline void url::update_base_port(std::optional input) { - port = input; -} + ada_really_inline size_t parse_port( + std::string_view view, bool check_trailing_content) noexcept override; -inline void url::clear_pathname() { path.clear(); } + ada_really_inline size_t parse_port(std::string_view view) noexcept override { + return this->parse_port(view, false); + } -inline void url::clear_search() { query = std::nullopt; } + /** + * Return true on success. The 'in_place' parameter indicates whether the + * the string_view input is pointing in the buffer. When in_place is false, + * we must nearly always update the buffer. + * @see https://url.spec.whatwg.org/#concept-ipv4-parser + */ + [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place); -[[nodiscard]] inline bool url::has_hash() const noexcept { - return hash.has_value(); -} + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv6-parser + */ + [[nodiscard]] bool parse_ipv6(std::string_view input); -[[nodiscard]] inline bool url::has_search() const noexcept { - return query.has_value(); -} + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + */ + [[nodiscard]] bool parse_opaque_host(std::string_view input); -inline void url::set_protocol_as_file() { type = ada::scheme::type::FILE; } + ada_really_inline void parse_path(std::string_view input); -inline void url::set_scheme(std::string&& new_scheme) noexcept { - type = ada::scheme::get_scheme_type(new_scheme); - // We only move the 'scheme' if it is non-special. - if (!is_special()) { - non_special_scheme = new_scheme; - } -} + /** + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". + */ + [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const; -inline void url::copy_scheme(ada::url&& u) noexcept { - non_special_scheme = u.non_special_scheme; - type = u.type; -} + template + bool set_host_or_hostname(std::string_view input); -inline void url::copy_scheme(const ada::url& u) { - non_special_scheme = u.non_special_scheme; - type = u.type; -} + ada_really_inline bool parse_host(std::string_view input); -[[nodiscard]] ada_really_inline std::string url::get_href() const noexcept { - std::string output = get_protocol(); + inline void update_base_authority(std::string_view base_buffer, + const url_components &base); + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t *query_percent_encode_set); + inline void update_base_pathname(std::string_view input); + inline void update_base_username(std::string_view input); + inline void append_base_username(std::string_view input); + inline void update_base_password(std::string_view input); + inline void append_base_password(std::string_view input); + inline void update_base_port(uint32_t input); + inline void append_base_pathname(std::string_view input); + [[nodiscard]] inline uint32_t retrieve_base_port() const; + constexpr void clear_hostname(); + constexpr void clear_password(); + constexpr void clear_pathname() override; + [[nodiscard]] constexpr bool has_dash_dot() const noexcept; + void delete_dash_dot(); + inline void consume_prepared_path(std::string_view input); + template + [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( + std::string_view input); + ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, + std::string_view input); + [[nodiscard]] constexpr bool has_authority() const noexcept; + constexpr void set_protocol_as_file(); + inline void set_scheme(std::string_view new_scheme) noexcept; + /** + * Fast function to set the scheme from a view with a colon in the + * buffer, does not change type. + */ + inline void set_scheme_from_view_with_colon( + std::string_view new_scheme_with_colon) noexcept; + inline void copy_scheme(const url_aggregator &u) noexcept; - if (host.has_value()) { - output += "//"; - if (has_credentials()) { - output += username; - if (!password.empty()) { - output += ":" + get_password(); - } - output += "@"; - } - output += host.value(); - if (port.has_value()) { - output += ":" + get_port(); - } - } else if (!has_opaque_path && checkers::begins_with(path, "//")) { - // If url's host is null, url does not have an opaque path, url's path's - // size is greater than 1, and url's path[0] is the empty string, then - // append U+002F (/) followed by U+002E (.) to output. - output += "/."; - } - output += path; - if (query.has_value()) { - output += "?" + query.value(); - } - if (hash.has_value()) { - output += "#" + hash.value(); - } - return output; -} + inline void update_host_to_base_host(const std::string_view input) noexcept; -ada_really_inline size_t url::parse_port(std::string_view view, - bool check_trailing_content) noexcept { - ada_log("parse_port('", view, "') ", view.size()); - uint16_t parsed_port{}; - auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port); - if (r.ec == std::errc::result_out_of_range) { - ada_log("parse_port: std::errc::result_out_of_range"); - is_valid = false; - return 0; - } - ada_log("parse_port: ", parsed_port); - const size_t consumed = size_t(r.ptr - view.data()); - ada_log("parse_port: consumed ", consumed); - if (check_trailing_content) { - is_valid &= - (consumed == view.size() || view[consumed] == '/' || - view[consumed] == '?' || (is_special() && view[consumed] == '\\')); - } - ada_log("parse_port: is_valid = ", is_valid); - if (is_valid) { - // scheme_default_port can return 0, and we should allow 0 as a base port. - auto default_port = scheme_default_port(); - bool is_port_valid = (default_port == 0 && parsed_port == 0) || - (default_port != parsed_port); - port = (r.ec == std::errc() && is_port_valid) - ? std::optional(parsed_port) - : std::nullopt; - } - return consumed; -} +}; // url_aggregator +inline std::ostream &operator<<(std::ostream &out, const url &u); } // namespace ada -#endif // ADA_URL_H -/* end file include/ada/url-inl.h */ +#endif +/* end file include/ada/url_aggregator.h */ /* begin file include/ada/url_aggregator-inl.h */ /** * @file url_aggregator-inl.h @@ -5902,7 +7279,6 @@ ada_really_inline size_t url::parse_port(std::string_view view, */ #ifndef ADA_UNICODE_INL_H #define ADA_UNICODE_INL_H -#include /** * Unicode operations. These functions are not part of our public API and may @@ -5915,29 +7291,51 @@ ada_really_inline size_t url::parse_port(std::string_view view, namespace ada::unicode { ada_really_inline size_t percent_encode_index(const std::string_view input, const uint8_t character_set[]) { - return std::distance( - input.begin(), - std::find_if(input.begin(), input.end(), [character_set](const char c) { - return character_sets::bit_at(character_set, c); - })); + const char* data = input.data(); + const size_t size = input.size(); + + // Process 8 bytes at a time using unrolled loop + size_t i = 0; + for (; i + 8 <= size; i += 8) { + unsigned char chunk[8]; + std::memcpy(&chunk, data + i, + 8); // entices compiler to unconditionally process 8 characters + + // Check 8 characters at once + for (size_t j = 0; j < 8; j++) { + if (character_sets::bit_at(character_set, chunk[j])) { + return i + j; + } + } + } + + // Handle remaining bytes + for (; i < size; i++) { + if (character_sets::bit_at(character_set, data[i])) { + return i; + } + } + + return size; } } // namespace ada::unicode #endif // ADA_UNICODE_INL_H /* end file include/ada/unicode-inl.h */ -#include +#include +#include #include namespace ada { inline void url_aggregator::update_base_authority( - std::string_view base_buffer, const ada::url_components& base) { + std::string_view base_buffer, const ada::url_components &base) { std::string_view input = base_buffer.substr( base.protocol_end, base.host_start - base.protocol_end); ada_log("url_aggregator::update_base_authority ", input); - bool input_starts_with_dash = checkers::begins_with(input, "//"); + bool input_starts_with_dash = input.starts_with("//"); uint32_t diff = components.host_start - components.protocol_end; buffer.erase(components.protocol_end, @@ -6177,9 +7575,8 @@ inline void url_aggregator::update_base_pathname(const std::string_view input) { ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); ADA_ASSERT_TRUE(validate()); - const bool begins_with_dashdash = checkers::begins_with(input, "//"); + const bool begins_with_dashdash = input.starts_with("//"); if (!begins_with_dashdash && has_dash_dot()) { - ada_log("url_aggregator::update_base_pathname has /.: \n", to_diagram()); // We must delete the ./ delete_dash_dot(); } @@ -6202,8 +7599,6 @@ inline void url_aggregator::update_base_pathname(const std::string_view input) { if (components.hash_start != url_components::omitted) { components.hash_start += difference; } - ada_log("url_aggregator::update_base_pathname end '", input, "' [", - input.size(), " bytes] \n", to_diagram()); ADA_ASSERT_TRUE(validate()); } @@ -6322,8 +7717,8 @@ inline void url_aggregator::append_base_username(const std::string_view input) { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_password() { - ada_log("url_aggregator::clear_password ", to_string(), "\n", to_diagram()); +constexpr void url_aggregator::clear_password() { + ada_log("url_aggregator::clear_password ", to_string()); ADA_ASSERT_TRUE(validate()); if (!has_password()) { return; @@ -6544,7 +7939,7 @@ inline void url_aggregator::clear_hash() { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_pathname() { +constexpr void url_aggregator::clear_pathname() { ada_log("url_aggregator::clear_pathname"); ADA_ASSERT_TRUE(validate()); uint32_t ending_index = uint32_t(buffer.size()); @@ -6579,7 +7974,7 @@ inline void url_aggregator::clear_pathname() { ada_log("url_aggregator::clear_pathname completed, running checks... ok"); } -inline void url_aggregator::clear_hostname() { +constexpr void url_aggregator::clear_hostname() { ada_log("url_aggregator::clear_hostname"); ADA_ASSERT_TRUE(validate()); if (!has_authority()) { @@ -6616,33 +8011,34 @@ inline void url_aggregator::clear_hostname() { ADA_ASSERT_TRUE(validate()); } -[[nodiscard]] inline bool url_aggregator::has_hash() const noexcept { +[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept { ada_log("url_aggregator::has_hash"); return components.hash_start != url_components::omitted; } -[[nodiscard]] inline bool url_aggregator::has_search() const noexcept { +[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept { ada_log("url_aggregator::has_search"); return components.search_start != url_components::omitted; } -ada_really_inline bool url_aggregator::has_credentials() const noexcept { +constexpr bool url_aggregator::has_credentials() const noexcept { ada_log("url_aggregator::has_credentials"); return has_non_empty_username() || has_non_empty_password(); } -inline bool url_aggregator::cannot_have_credentials_or_port() const { +constexpr bool url_aggregator::cannot_have_credentials_or_port() const { ada_log("url_aggregator::cannot_have_credentials_or_port"); return type == ada::scheme::type::FILE || components.host_start == components.host_end; } -[[nodiscard]] ada_really_inline const ada::url_components& +[[nodiscard]] ada_really_inline const ada::url_components & url_aggregator::get_components() const noexcept { return components; } -[[nodiscard]] inline bool ada::url_aggregator::has_authority() const noexcept { +[[nodiscard]] constexpr bool ada::url_aggregator::has_authority() + const noexcept { ada_log("url_aggregator::has_authority"); // Performance: instead of doing this potentially expensive check, we could // have a boolean in the struct. @@ -6677,28 +8073,28 @@ inline void ada::url_aggregator::add_authority_slashes_if_needed() noexcept { ADA_ASSERT_TRUE(validate()); } -inline void ada::url_aggregator::reserve(uint32_t capacity) { +constexpr void ada::url_aggregator::reserve(uint32_t capacity) { buffer.reserve(capacity); } -inline bool url_aggregator::has_non_empty_username() const noexcept { +constexpr bool url_aggregator::has_non_empty_username() const noexcept { ada_log("url_aggregator::has_non_empty_username"); return components.protocol_end + 2 < components.username_end; } -inline bool url_aggregator::has_non_empty_password() const noexcept { +constexpr bool url_aggregator::has_non_empty_password() const noexcept { ada_log("url_aggregator::has_non_empty_password"); return components.host_start - components.username_end > 0; } -inline bool url_aggregator::has_password() const noexcept { +constexpr bool url_aggregator::has_password() const noexcept { ada_log("url_aggregator::has_password"); // This function does not care about the length of the password return components.host_start > components.username_end && buffer[components.username_end] == ':'; } -inline bool url_aggregator::has_empty_hostname() const noexcept { +constexpr bool url_aggregator::has_empty_hostname() const noexcept { if (!has_hostname()) { return false; } @@ -6711,18 +8107,18 @@ inline bool url_aggregator::has_empty_hostname() const noexcept { return components.username_end != components.host_start; } -inline bool url_aggregator::has_hostname() const noexcept { +constexpr bool url_aggregator::has_hostname() const noexcept { return has_authority(); } -inline bool url_aggregator::has_port() const noexcept { +constexpr bool url_aggregator::has_port() const noexcept { ada_log("url_aggregator::has_port"); // A URL cannot have a username/password/port if its host is null or the empty // string, or its scheme is "file". return has_hostname() && components.pathname_start != components.host_end; } -[[nodiscard]] inline bool url_aggregator::has_dash_dot() const noexcept { +[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept { // If url's host is null, url does not have an opaque path, url's path's size // is greater than 1, and url's path[0] is the empty string, then append // U+002F (/) followed by U+002E (.) to output. @@ -6754,8 +8150,8 @@ inline bool url_aggregator::has_port() const noexcept { buffer[components.host_end + 1] == '.'; } -[[nodiscard]] inline std::string_view url_aggregator::get_href() - const noexcept { +[[nodiscard]] constexpr std::string_view url_aggregator::get_href() + const noexcept ada_lifetime_bound { ada_log("url_aggregator::get_href"); return buffer; } @@ -6763,10 +8159,15 @@ inline bool url_aggregator::has_port() const noexcept { ada_really_inline size_t url_aggregator::parse_port( std::string_view view, bool check_trailing_content) noexcept { ada_log("url_aggregator::parse_port('", view, "') ", view.size()); + if (!view.empty() && view[0] == '-') { + ada_log("parse_port: view[0] == '0' && view.size() > 1"); + is_valid = false; + return 0; + } uint16_t parsed_port{}; auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port); if (r.ec == std::errc::result_out_of_range) { - ada_log("parse_port: std::errc::result_out_of_range"); + ada_log("parse_port: r.ec == std::errc::result_out_of_range"); is_valid = false; return 0; } @@ -6794,7 +8195,7 @@ ada_really_inline size_t url_aggregator::parse_port( return consumed; } -inline void url_aggregator::set_protocol_as_file() { +constexpr void url_aggregator::set_protocol_as_file() { ada_log("url_aggregator::set_protocol_as_file "); ADA_ASSERT_TRUE(validate()); type = ada::scheme::type::FILE; @@ -6822,11 +8223,218 @@ inline void url_aggregator::set_protocol_as_file() { components.hash_start += new_difference; } ADA_ASSERT_TRUE(validate()); -} - -inline std::ostream& operator<<(std::ostream& out, - const ada::url_aggregator& u) { - return out << u.to_string(); +} + +[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept { + if (!is_valid) { + return true; + } + if (!components.check_offset_consistency()) { + ada_log("url_aggregator::validate inconsistent components \n", + to_diagram()); + return false; + } + // We have a credible components struct, but let us investivate more + // carefully: + /** + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + if (components.protocol_end == url_components::omitted) { + ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram()); + return false; + } + if (components.username_end == url_components::omitted) { + ada_log("url_aggregator::validate omitted username_end \n", to_diagram()); + return false; + } + if (components.host_start == url_components::omitted) { + ada_log("url_aggregator::validate omitted host_start \n", to_diagram()); + return false; + } + if (components.host_end == url_components::omitted) { + ada_log("url_aggregator::validate omitted host_end \n", to_diagram()); + return false; + } + if (components.pathname_start == url_components::omitted) { + ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram()); + return false; + } + + if (components.protocol_end > buffer.size()) { + ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram()); + return false; + } + if (components.username_end > buffer.size()) { + ada_log("url_aggregator::validate username_end overflow \n", to_diagram()); + return false; + } + if (components.host_start > buffer.size()) { + ada_log("url_aggregator::validate host_start overflow \n", to_diagram()); + return false; + } + if (components.host_end > buffer.size()) { + ada_log("url_aggregator::validate host_end overflow \n", to_diagram()); + return false; + } + if (components.pathname_start > buffer.size()) { + ada_log("url_aggregator::validate pathname_start overflow \n", + to_diagram()); + return false; + } + + if (components.protocol_end > 0) { + if (buffer[components.protocol_end - 1] != ':') { + ada_log( + "url_aggregator::validate missing : at the end of the protocol \n", + to_diagram()); + return false; + } + } + + if (components.username_end != buffer.size() && + components.username_end > components.protocol_end + 2) { + if (buffer[components.username_end] != ':' && + buffer[components.username_end] != '@') { + ada_log( + "url_aggregator::validate missing : or @ at the end of the username " + "\n", + to_diagram()); + return false; + } + } + + if (components.host_start != buffer.size()) { + if (components.host_start > components.username_end) { + if (buffer[components.host_start] != '@') { + ada_log( + "url_aggregator::validate missing @ at the end of the password \n", + to_diagram()); + return false; + } + } else if (components.host_start == components.username_end && + components.host_end > components.host_start) { + if (components.host_start == components.protocol_end + 2) { + if (buffer[components.protocol_end] != '/' || + buffer[components.protocol_end + 1] != '/') { + ada_log( + "url_aggregator::validate missing // between protocol and host " + "\n", + to_diagram()); + return false; + } + } else { + if (components.host_start > components.protocol_end && + buffer[components.host_start] != '@') { + ada_log( + "url_aggregator::validate missing @ at the end of the username " + "\n", + to_diagram()); + return false; + } + } + } else { + if (components.host_end != components.host_start) { + ada_log("url_aggregator::validate expected omitted host \n", + to_diagram()); + return false; + } + } + } + if (components.host_end != buffer.size() && + components.pathname_start > components.host_end) { + if (components.pathname_start == components.host_end + 2 && + buffer[components.host_end] == '/' && + buffer[components.host_end + 1] == '.') { + if (components.pathname_start + 1 >= buffer.size() || + buffer[components.pathname_start] != '/' || + buffer[components.pathname_start + 1] != '/') { + ada_log( + "url_aggregator::validate expected the path to begin with // \n", + to_diagram()); + return false; + } + } else if (buffer[components.host_end] != ':') { + ada_log("url_aggregator::validate missing : at the port \n", + to_diagram()); + return false; + } + } + if (components.pathname_start != buffer.size() && + components.pathname_start < components.search_start && + components.pathname_start < components.hash_start && !has_opaque_path) { + if (buffer[components.pathname_start] != '/') { + ada_log("url_aggregator::validate missing / at the path \n", + to_diagram()); + return false; + } + } + if (components.search_start != url_components::omitted) { + if (buffer[components.search_start] != '?') { + ada_log("url_aggregator::validate missing ? at the search \n", + to_diagram()); + return false; + } + } + if (components.hash_start != url_components::omitted) { + if (buffer[components.hash_start] != '#') { + ada_log("url_aggregator::validate missing # at the hash \n", + to_diagram()); + return false; + } + } + + return true; +} + +[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() + const noexcept ada_lifetime_bound { + ada_log("url_aggregator::get_pathname pathname_start = ", + components.pathname_start, " buffer.size() = ", buffer.size(), + " components.search_start = ", components.search_start, + " components.hash_start = ", components.hash_start); + auto ending_index = uint32_t(buffer.size()); + if (components.search_start != url_components::omitted) { + ending_index = components.search_start; + } else if (components.hash_start != url_components::omitted) { + ending_index = components.hash_start; + } + return helpers::substring(buffer, components.pathname_start, ending_index); +} + +inline std::ostream &operator<<(std::ostream &out, + const ada::url_aggregator &u) { + return out << u.to_string(); +} + +void url_aggregator::update_host_to_base_host( + const std::string_view input) noexcept { + ada_log("url_aggregator::update_host_to_base_host ", input); + ADA_ASSERT_TRUE(validate()); + ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); + if (type != ada::scheme::type::FILE) { + // Let host be the result of host parsing host_view with url is not special. + if (input.empty() && !is_special()) { + if (has_hostname()) { + clear_hostname(); + } else if (has_dash_dot()) { + add_authority_slashes_if_needed(); + delete_dash_dot(); + } + return; + } + } + update_base_hostname(input); + ADA_ASSERT_TRUE(validate()); + return; } } // namespace ada @@ -6868,6 +8476,8 @@ using url_search_params_entries_iter = url_search_params_iter_type::ENTRIES>; /** + * We require all strings to be valid UTF-8. It is the user's responsibility to + * ensure that the provided strings are valid UTF-8. * @see https://url.spec.whatwg.org/#interface-urlsearchparams */ struct url_search_params { @@ -6877,17 +8487,20 @@ struct url_search_params { * @see * https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-constructor.any.js */ - url_search_params(const std::string_view input) { initialize(input); } + explicit url_search_params(const std::string_view input) { + initialize(input); + } - url_search_params(const url_search_params& u) = default; - url_search_params(url_search_params&& u) noexcept = default; - url_search_params& operator=(url_search_params&& u) noexcept = default; - url_search_params& operator=(const url_search_params& u) = default; + url_search_params(const url_search_params &u) = default; + url_search_params(url_search_params &&u) noexcept = default; + url_search_params &operator=(url_search_params &&u) noexcept = default; + url_search_params &operator=(const url_search_params &u) = default; ~url_search_params() = default; [[nodiscard]] inline size_t size() const noexcept; /** + * Both key and value must be valid UTF-8. * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append */ inline void append(std::string_view key, std::string_view value); @@ -6915,6 +8528,7 @@ struct url_search_params { inline bool has(std::string_view key, std::string_view value) noexcept; /** + * Both key and value must be valid UTF-8. * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set */ inline void set(std::string_view key, std::string_view value); @@ -6978,6 +8592,7 @@ struct url_search_params { std::vector params{}; /** + * The init parameter must be valid UTF-8. * @see https://url.spec.whatwg.org/#concept-urlencoded-parser */ void initialize(std::string_view init); @@ -6995,11 +8610,11 @@ struct url_search_params { template struct url_search_params_iter { inline url_search_params_iter() : params(EMPTY) {} - url_search_params_iter(const url_search_params_iter& u) = default; - url_search_params_iter(url_search_params_iter&& u) noexcept = default; - url_search_params_iter& operator=(url_search_params_iter&& u) noexcept = + url_search_params_iter(const url_search_params_iter &u) = default; + url_search_params_iter(url_search_params_iter &&u) noexcept = default; + url_search_params_iter &operator=(url_search_params_iter &&u) noexcept = default; - url_search_params_iter& operator=(const url_search_params_iter& u) = default; + url_search_params_iter &operator=(const url_search_params_iter &u) = default; ~url_search_params_iter() = default; /** @@ -7007,13 +8622,13 @@ struct url_search_params_iter { */ inline std::optional next(); - inline bool has_next(); + inline bool has_next() const; private: static url_search_params EMPTY; - inline url_search_params_iter(url_search_params& params_) : params(params_) {} + inline url_search_params_iter(url_search_params ¶ms_) : params(params_) {} - url_search_params& params; + url_search_params ¶ms; size_t pos = 0; friend struct url_search_params; @@ -7030,8 +8645,10 @@ struct url_search_params_iter { #ifndef ADA_URL_SEARCH_PARAMS_INL_H #define ADA_URL_SEARCH_PARAMS_INL_H + #include #include +#include #include #include #include @@ -7057,14 +8674,14 @@ inline void url_search_params::initialize(std::string_view input) { if (equal == std::string_view::npos) { std::string name(current); - std::replace(name.begin(), name.end(), '+', ' '); + std::ranges::replace(name, '+', ' '); params.emplace_back(unicode::percent_decode(name, name.find('%')), ""); } else { std::string name(current.substr(0, equal)); std::string value(current.substr(equal + 1)); - std::replace(name.begin(), name.end(), '+', ' '); - std::replace(value.begin(), value.end(), '+', ' '); + std::ranges::replace(name, '+', ' '); + std::ranges::replace(value, '+', ' '); params.emplace_back(unicode::percent_decode(name, name.find('%')), unicode::percent_decode(value, value.find('%'))); @@ -7096,8 +8713,8 @@ inline size_t url_search_params::size() const noexcept { return params.size(); } inline std::optional url_search_params::get( const std::string_view key) { - auto entry = std::find_if(params.begin(), params.end(), - [&key](auto& param) { return param.first == key; }); + auto entry = std::ranges::find_if( + params, [&key](const auto ¶m) { return param.first == key; }); if (entry == params.end()) { return std::nullopt; @@ -7110,7 +8727,7 @@ inline std::vector url_search_params::get_all( const std::string_view key) { std::vector out{}; - for (auto& param : params) { + for (auto ¶m : params) { if (param.first == key) { out.emplace_back(param.second); } @@ -7120,17 +8737,16 @@ inline std::vector url_search_params::get_all( } inline bool url_search_params::has(const std::string_view key) noexcept { - auto entry = std::find_if(params.begin(), params.end(), - [&key](auto& param) { return param.first == key; }); + auto entry = std::ranges::find_if( + params, [&key](const auto ¶m) { return param.first == key; }); return entry != params.end(); } inline bool url_search_params::has(std::string_view key, std::string_view value) noexcept { - auto entry = - std::find_if(params.begin(), params.end(), [&key, &value](auto& param) { - return param.first == key && param.second == value; - }); + auto entry = std::ranges::find_if(params, [&key, &value](const auto ¶m) { + return param.first == key && param.second == value; + }); return entry != params.end(); } @@ -7142,8 +8758,8 @@ inline std::string url_search_params::to_string() const { auto value = ada::unicode::percent_encode(params[i].second, character_set); // Performance optimization: Move this inside percent_encode. - std::replace(key.begin(), key.end(), ' ', '+'); - std::replace(value.begin(), value.end(), ' ', '+'); + std::ranges::replace(key, ' ', '+'); + std::ranges::replace(value, ' ', '+'); if (i != 0) { out += "&"; @@ -7157,9 +8773,9 @@ inline std::string url_search_params::to_string() const { inline void url_search_params::set(const std::string_view key, const std::string_view value) { - const auto find = [&key](auto& param) { return param.first == key; }; + const auto find = [&key](const auto ¶m) { return param.first == key; }; - auto it = std::find_if(params.begin(), params.end(), find); + auto it = std::ranges::find_if(params, find); if (it == params.end()) { params.emplace_back(key, value); @@ -7171,27 +8787,92 @@ inline void url_search_params::set(const std::string_view key, } inline void url_search_params::remove(const std::string_view key) { - params.erase( - std::remove_if(params.begin(), params.end(), - [&key](auto& param) { return param.first == key; }), - params.end()); + std::erase_if(params, + [&key](const auto ¶m) { return param.first == key; }); } inline void url_search_params::remove(const std::string_view key, const std::string_view value) { - params.erase(std::remove_if(params.begin(), params.end(), - [&key, &value](auto& param) { - return param.first == key && - param.second == value; - }), - params.end()); + std::erase_if(params, [&key, &value](const auto ¶m) { + return param.first == key && param.second == value; + }); } inline void url_search_params::sort() { - std::stable_sort(params.begin(), params.end(), - [](const key_value_pair& lhs, const key_value_pair& rhs) { - return lhs.first < rhs.first; - }); + // We rely on the fact that the content is valid UTF-8. + std::ranges::stable_sort(params, [](const key_value_pair &lhs, + const key_value_pair &rhs) { + size_t i = 0, j = 0; + uint32_t low_surrogate1 = 0, low_surrogate2 = 0; + while ((i < lhs.first.size() || low_surrogate1 != 0) && + (j < rhs.first.size() || low_surrogate2 != 0)) { + uint32_t codePoint1 = 0, codePoint2 = 0; + + if (low_surrogate1 != 0) { + codePoint1 = low_surrogate1; + low_surrogate1 = 0; + } else { + uint8_t c1 = uint8_t(lhs.first[i]); + if (c1 <= 0x7F) { + codePoint1 = c1; + i++; + } else if (c1 <= 0xDF) { + codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F); + i += 2; + } else if (c1 <= 0xEF) { + codePoint1 = ((c1 & 0x0F) << 12) | + ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) | + (uint8_t(lhs.first[i + 2]) & 0x3F); + i += 3; + } else { + codePoint1 = ((c1 & 0x07) << 18) | + ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) | + ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) | + (uint8_t(lhs.first[i + 3]) & 0x3F); + i += 4; + + codePoint1 -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10)); + low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF)); + codePoint1 = high_surrogate; + } + } + + if (low_surrogate2 != 0) { + codePoint2 = low_surrogate2; + low_surrogate2 = 0; + } else { + uint8_t c2 = uint8_t(rhs.first[j]); + if (c2 <= 0x7F) { + codePoint2 = c2; + j++; + } else if (c2 <= 0xDF) { + codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F); + j += 2; + } else if (c2 <= 0xEF) { + codePoint2 = ((c2 & 0x0F) << 12) | + ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) | + (uint8_t(rhs.first[j + 2]) & 0x3F); + j += 3; + } else { + codePoint2 = ((c2 & 0x07) << 18) | + ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) | + ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) | + (uint8_t(rhs.first[j + 3]) & 0x3F); + j += 4; + codePoint2 -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10)); + low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF)); + codePoint2 = high_surrogate; + } + } + + if (codePoint1 != codePoint2) { + return (codePoint1 < codePoint2); + } + } + return (j < rhs.first.size() || low_surrogate2 != 0); + }); } inline url_search_params_keys_iter url_search_params::get_keys() { @@ -7213,7 +8894,7 @@ inline url_search_params_entries_iter url_search_params::get_entries() { } template -inline bool url_search_params_iter::has_next() { +inline bool url_search_params_iter::has_next() const { return pos < params.params.size(); } @@ -7247,6 +8928,1584 @@ url_search_params_entries_iter::next() { #endif // ADA_URL_SEARCH_PARAMS_INL_H /* end file include/ada/url_search_params-inl.h */ +/* begin file include/ada/url_pattern-inl.h */ +/** + * @file url_pattern-inl.h + * @brief Declaration for the URLPattern inline functions. + */ +#ifndef ADA_URL_PATTERN_INL_H +#define ADA_URL_PATTERN_INL_H + + +#include +#include +#include + +#if ADA_INCLUDE_URL_PATTERN +namespace ada { + +inline bool url_pattern_init::operator==(const url_pattern_init& other) const { + return protocol == other.protocol && username == other.username && + password == other.password && hostname == other.hostname && + port == other.port && search == other.search && hash == other.hash && + pathname == other.pathname; +} + +inline bool url_pattern_component_result::operator==( + const url_pattern_component_result& other) const { + return input == other.input && groups == other.groups; +} + +template +url_pattern_component_result +url_pattern_component::create_component_match_result( + std::string&& input, + std::vector>&& exec_result) { + // Let result be a new URLPatternComponentResult. + // Set result["input"] to input. + // Let groups be a record. + auto result = + url_pattern_component_result{.input = std::move(input), .groups = {}}; + + // Optimization: Let's reserve the size. + result.groups.reserve(exec_result.size()); + + // We explicitly start iterating from 0 even though the spec + // says we should start from 1. This case is handled by the + // std_regex_provider. + for (size_t index = 0; index < exec_result.size(); index++) { + result.groups.insert({ + group_name_list[index], + std::move(exec_result[index]), + }); + } + return result; +} + +template +std::string_view url_pattern::get_protocol() const + ada_lifetime_bound { + // Return this's associated URL pattern's protocol component's pattern string. + return protocol_component.pattern; +} +template +std::string_view url_pattern::get_username() const + ada_lifetime_bound { + // Return this's associated URL pattern's username component's pattern string. + return username_component.pattern; +} +template +std::string_view url_pattern::get_password() const + ada_lifetime_bound { + // Return this's associated URL pattern's password component's pattern string. + return password_component.pattern; +} +template +std::string_view url_pattern::get_hostname() const + ada_lifetime_bound { + // Return this's associated URL pattern's hostname component's pattern string. + return hostname_component.pattern; +} +template +std::string_view url_pattern::get_port() const + ada_lifetime_bound { + // Return this's associated URL pattern's port component's pattern string. + return port_component.pattern; +} +template +std::string_view url_pattern::get_pathname() const + ada_lifetime_bound { + // Return this's associated URL pattern's pathname component's pattern string. + return pathname_component.pattern; +} +template +std::string_view url_pattern::get_search() const + ada_lifetime_bound { + // Return this's associated URL pattern's search component's pattern string. + return search_component.pattern; +} +template +std::string_view url_pattern::get_hash() const + ada_lifetime_bound { + // Return this's associated URL pattern's hash component's pattern string. + return hash_component.pattern; +} +template +bool url_pattern::ignore_case() const { + return ignore_case_; +} +template +bool url_pattern::has_regexp_groups() const { + // If this's associated URL pattern's has regexp groups, then return true. + return protocol_component.has_regexp_groups || + username_component.has_regexp_groups || + password_component.has_regexp_groups || + hostname_component.has_regexp_groups || + port_component.has_regexp_groups || + pathname_component.has_regexp_groups || + search_component.has_regexp_groups || hash_component.has_regexp_groups; +} + +inline bool url_pattern_part::is_regexp() const noexcept { + return type == url_pattern_part_type::REGEXP; +} + +inline std::string_view url_pattern_compile_component_options::get_delimiter() + const { + if (delimiter) { + return {&delimiter.value(), 1}; + } + return {}; +} + +inline std::string_view url_pattern_compile_component_options::get_prefix() + const { + if (prefix) { + return {&prefix.value(), 1}; + } + return {}; +} + +template +template +tl::expected, errors> +url_pattern_component::compile( + std::string_view input, F& encoding_callback, + url_pattern_compile_component_options& options) { + ada_log("url_pattern_component::compile input: ", input); + // Let part list be the result of running parse a pattern string given input, + // options, and encoding callback. + auto part_list = url_pattern_helpers::parse_pattern_string(input, options, + encoding_callback); + + if (!part_list) { + ada_log("parse_pattern_string failed"); + return tl::unexpected(part_list.error()); + } + + // Let (regular expression string, name list) be the result of running + // generate a regular expression and name list given part list and options. + auto [regular_expression_string, name_list] = + url_pattern_helpers::generate_regular_expression_and_name_list(*part_list, + options); + + ada_log("regular expression string: ", regular_expression_string); + + // Let pattern string be the result of running generate a pattern + // string given part list and options. + auto pattern_string = + url_pattern_helpers::generate_pattern_string(*part_list, options); + + // Let regular expression be RegExpCreate(regular expression string, + // flags). If this throws an exception, catch it, and throw a + // TypeError. + std::optional regular_expression = + regex_provider::create_instance(regular_expression_string, + options.ignore_case); + + if (!regular_expression) { + return tl::unexpected(errors::type_error); + } + + // For each part of part list: + // - If part's type is "regexp", then set has regexp groups to true. + const auto has_regexp = [](const auto& part) { return part.is_regexp(); }; + const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp); + + ada_log("has regexp groups: ", has_regexp_groups); + + // Return a new component whose pattern string is pattern string, regular + // expression is regular expression, group name list is name list, and has + // regexp groups is has regexp groups. + return url_pattern_component( + std::move(pattern_string), std::move(*regular_expression), + std::move(name_list), has_regexp_groups); +} + +template +result> url_pattern::exec( + const url_pattern_input& input, const std::string_view* base_url) { + // Return the result of match given this's associated URL pattern, input, and + // baseURL if given. + return match(input, base_url); +} + +template +result url_pattern::test( + const url_pattern_input& input, const std::string_view* base_url) { + // TODO: Optimization opportunity. Rather than returning `url_pattern_result` + // Implement a fast path just like `can_parse()` in ada_url. + // Let result be the result of match given this's associated URL pattern, + // input, and baseURL if given. + // If result is null, return false. + if (auto result = match(input, base_url); result.has_value()) { + return result->has_value(); + } + return tl::unexpected(errors::type_error); +} + +template +result> url_pattern::match( + const url_pattern_input& input, const std::string_view* base_url_string) { + std::string protocol{}; + std::string username{}; + std::string password{}; + std::string hostname{}; + std::string port{}; + std::string pathname{}; + std::string search{}; + std::string hash{}; + + // Let inputs be an empty list. + // Append input to inputs. + std::vector inputs{input}; + + // If input is a URLPatternInit then: + if (std::holds_alternative(input)) { + ada_log( + "url_pattern::match called with url_pattern_init and base_url_string=", + base_url_string); + // If baseURLString was given, throw a TypeError. + if (base_url_string) { + ada_log("failed to match because base_url_string was given"); + return tl::unexpected(errors::type_error); + } + + // Let applyResult be the result of process a URLPatternInit given input, + // "url", protocol, username, password, hostname, port, pathname, search, + // and hash. + auto apply_result = url_pattern_init::process( + std::get(input), url_pattern_init::process_type::url, + protocol, username, password, hostname, port, pathname, search, hash); + + // If this throws an exception, catch it, and return null. + if (!apply_result.has_value()) { + ada_log("match returned std::nullopt because process threw"); + return std::nullopt; + } + + // Set protocol to applyResult["protocol"]. + ADA_ASSERT_TRUE(apply_result->protocol.has_value()); + protocol = std::move(apply_result->protocol.value()); + + // Set username to applyResult["username"]. + ADA_ASSERT_TRUE(apply_result->username.has_value()); + username = std::move(apply_result->username.value()); + + // Set password to applyResult["password"]. + ADA_ASSERT_TRUE(apply_result->password.has_value()); + password = std::move(apply_result->password.value()); + + // Set hostname to applyResult["hostname"]. + ADA_ASSERT_TRUE(apply_result->hostname.has_value()); + hostname = std::move(apply_result->hostname.value()); + + // Set port to applyResult["port"]. + ADA_ASSERT_TRUE(apply_result->port.has_value()); + port = std::move(apply_result->port.value()); + + // Set pathname to applyResult["pathname"]. + ADA_ASSERT_TRUE(apply_result->pathname.has_value()); + pathname = std::move(apply_result->pathname.value()); + + // Set search to applyResult["search"]. + ADA_ASSERT_TRUE(apply_result->search.has_value()); + if (apply_result->search->starts_with("?")) { + search = apply_result->search->substr(1); + } else { + search = std::move(apply_result->search.value()); + } + + // Set hash to applyResult["hash"]. + ADA_ASSERT_TRUE(apply_result->hash.has_value()); + ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#")); + hash = std::move(apply_result->hash.value()); + } else { + ADA_ASSERT_TRUE(std::holds_alternative(input)); + + // Let baseURL be null. + result base_url; + + // If baseURLString was given, then: + if (base_url_string) { + // Let baseURL be the result of parsing baseURLString. + base_url = ada::parse(*base_url_string, nullptr); + + // If baseURL is failure, return null. + if (!base_url) { + ada_log("match returned std::nullopt because failed to parse base_url=", + *base_url_string); + return std::nullopt; + } + + // Append baseURLString to inputs. + inputs.emplace_back(*base_url_string); + } + + url_aggregator* base_url_value = + base_url.has_value() ? &*base_url : nullptr; + + // Set url to the result of parsing input given baseURL. + auto url = ada::parse(std::get(input), + base_url_value); + + // If url is failure, return null. + if (!url) { + ada_log("match returned std::nullopt because url failed"); + return std::nullopt; + } + + // Set protocol to url's scheme. + // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':' + // is removed. Similar work was done on workerd: + // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038 + protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1); + // Set username to url's username. + username = url->get_username(); + // Set password to url's password. + password = url->get_password(); + // Set hostname to url's host, serialized, or the empty string if the value + // is null. + hostname = url->get_hostname(); + // Set port to url's port, serialized, or the empty string if the value is + // null. + port = url->get_port(); + // Set pathname to the result of URL path serializing url. + pathname = url->get_pathname(); + // Set search to url's query or the empty string if the value is null. + // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?' + // is removed. Similar work was done on workerd: + // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232 + if (url->has_search()) { + auto view = url->get_search(); + search = view.starts_with("?") ? url->get_search().substr(1) : view; + } + // Set hash to url's fragment or the empty string if the value is null. + // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is + // removed. Similar work was done on workerd: + // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242 + if (url->has_hash()) { + auto view = url->get_hash(); + hash = view.starts_with("#") ? url->get_hash().substr(1) : view; + } + } + + // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol + // component's regular expression, protocol). + auto protocol_exec_result = + regex_provider::regex_search(protocol, protocol_component.regexp); + + if (!protocol_exec_result) { + return std::nullopt; + } + + // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username + // component's regular expression, username). + auto username_exec_result = + regex_provider::regex_search(username, username_component.regexp); + + if (!username_exec_result) { + return std::nullopt; + } + + // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password + // component's regular expression, password). + auto password_exec_result = + regex_provider::regex_search(password, password_component.regexp); + + if (!password_exec_result) { + return std::nullopt; + } + + // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname + // component's regular expression, hostname). + auto hostname_exec_result = + regex_provider::regex_search(hostname, hostname_component.regexp); + + if (!hostname_exec_result) { + return std::nullopt; + } + + // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's + // regular expression, port). + auto port_exec_result = + regex_provider::regex_search(port, port_component.regexp); + + if (!port_exec_result) { + return std::nullopt; + } + + // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname + // component's regular expression, pathname). + auto pathname_exec_result = + regex_provider::regex_search(pathname, pathname_component.regexp); + + if (!pathname_exec_result) { + return std::nullopt; + } + + // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's + // regular expression, search). + auto search_exec_result = + regex_provider::regex_search(search, search_component.regexp); + + if (!search_exec_result) { + return std::nullopt; + } + + // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's + // regular expression, hash). + auto hash_exec_result = + regex_provider::regex_search(hash, hash_component.regexp); + + if (!hash_exec_result) { + return std::nullopt; + } + + // Let result be a new URLPatternResult. + auto result = url_pattern_result{}; + // Set result["inputs"] to inputs. + result.inputs = std::move(inputs); + // Set result["protocol"] to the result of creating a component match result + // given urlPattern's protocol component, protocol, and protocolExecResult. + result.protocol = protocol_component.create_component_match_result( + std::move(protocol), std::move(*protocol_exec_result)); + + // Set result["username"] to the result of creating a component match result + // given urlPattern's username component, username, and usernameExecResult. + result.username = username_component.create_component_match_result( + std::move(username), std::move(*username_exec_result)); + + // Set result["password"] to the result of creating a component match result + // given urlPattern's password component, password, and passwordExecResult. + result.password = password_component.create_component_match_result( + std::move(password), std::move(*password_exec_result)); + + // Set result["hostname"] to the result of creating a component match result + // given urlPattern's hostname component, hostname, and hostnameExecResult. + result.hostname = hostname_component.create_component_match_result( + std::move(hostname), std::move(*hostname_exec_result)); + + // Set result["port"] to the result of creating a component match result given + // urlPattern's port component, port, and portExecResult. + result.port = port_component.create_component_match_result( + std::move(port), std::move(*port_exec_result)); + + // Set result["pathname"] to the result of creating a component match result + // given urlPattern's pathname component, pathname, and pathnameExecResult. + result.pathname = pathname_component.create_component_match_result( + std::move(pathname), std::move(*pathname_exec_result)); + + // Set result["search"] to the result of creating a component match result + // given urlPattern's search component, search, and searchExecResult. + result.search = search_component.create_component_match_result( + std::move(search), std::move(*search_exec_result)); + + // Set result["hash"] to the result of creating a component match result given + // urlPattern's hash component, hash, and hashExecResult. + result.hash = hash_component.create_component_match_result( + std::move(hash), std::move(*hash_exec_result)); + + return result; +} + +} // namespace ada +#endif // ADA_INCLUDE_URL_PATTERN +#endif +/* end file include/ada/url_pattern-inl.h */ +/* begin file include/ada/url_pattern_helpers-inl.h */ +/** + * @file url_pattern_helpers-inl.h + * @brief Declaration for the URLPattern helpers. + */ +#ifndef ADA_URL_PATTERN_HELPERS_INL_H +#define ADA_URL_PATTERN_HELPERS_INL_H + +#include +#include + + +#if ADA_INCLUDE_URL_PATTERN +namespace ada::url_pattern_helpers { +#if defined(ADA_TESTING) || defined(ADA_LOGGING) +inline std::string to_string(token_type type) { + switch (type) { + case token_type::INVALID_CHAR: + return "INVALID_CHAR"; + case token_type::OPEN: + return "OPEN"; + case token_type::CLOSE: + return "CLOSE"; + case token_type::REGEXP: + return "REGEXP"; + case token_type::NAME: + return "NAME"; + case token_type::CHAR: + return "CHAR"; + case token_type::ESCAPED_CHAR: + return "ESCAPED_CHAR"; + case token_type::OTHER_MODIFIER: + return "OTHER_MODIFIER"; + case token_type::ASTERISK: + return "ASTERISK"; + case token_type::END: + return "END"; + default: + ada::unreachable(); + } +} +#endif // defined(ADA_TESTING) || defined(ADA_LOGGING) + +template +constexpr void constructor_string_parser::rewind() { + // Set parser's token index to parser's component start. + token_index = component_start; + // Set parser's token increment to 0. + token_increment = 0; +} + +template +constexpr bool constructor_string_parser::is_hash_prefix() { + // Return the result of running is a non-special pattern char given parser, + // parser's token index and "#". + return is_non_special_pattern_char(token_index, '#'); +} + +template +constexpr bool constructor_string_parser::is_search_prefix() { + // If result of running is a non-special pattern char given parser, parser's + // token index and "?" is true, then return true. + if (is_non_special_pattern_char(token_index, '?')) { + return true; + } + + // If parser's token list[parser's token index]'s value is not "?", then + // return false. + if (token_list[token_index].value != "?") { + return false; + } + + // If previous index is less than 0, then return true. + if (token_index == 0) return true; + // Let previous index be parser's token index - 1. + auto previous_index = token_index - 1; + // Let previous token be the result of running get a safe token given parser + // and previous index. + auto previous_token = get_safe_token(previous_index); + ADA_ASSERT_TRUE(previous_token); + // If any of the following are true, then return false: + // - previous token's type is "name". + // - previous token's type is "regexp". + // - previous token's type is "close". + // - previous token's type is "asterisk". + return !(previous_token->type == token_type::NAME || + previous_token->type == token_type::REGEXP || + previous_token->type == token_type::CLOSE || + previous_token->type == token_type::ASTERISK); +} + +template +constexpr bool +constructor_string_parser::is_non_special_pattern_char( + size_t index, uint32_t value) const { + // Let token be the result of running get a safe token given parser and index. + auto token = get_safe_token(index); + ADA_ASSERT_TRUE(token); + + // If token's value is not value, then return false. + // TODO: Remove this once we make sure get_safe_token returns a non-empty + // string. + if (!token->value.empty() && + static_cast(token->value[0]) != value) { + return false; + } + + // If any of the following are true: + // - token's type is "char"; + // - token's type is "escaped-char"; or + // - token's type is "invalid-char", + // - then return true. + return token->type == token_type::CHAR || + token->type == token_type::ESCAPED_CHAR || + token->type == token_type::INVALID_CHAR; +} + +template +constexpr const token* +constructor_string_parser::get_safe_token(size_t index) const { + // If index is less than parser's token list's size, then return parser's + // token list[index]. + if (index < token_list.size()) [[likely]] { + return &token_list[index]; + } + + // Assert: parser's token list's size is greater than or equal to 1. + ADA_ASSERT_TRUE(!token_list.empty()); + + // Let token be parser's token list[last index]. + // Assert: token's type is "end". + ADA_ASSERT_TRUE(token_list.back().type == token_type::END); + + // Return token. + return &token_list.back(); +} + +template +constexpr bool constructor_string_parser::is_group_open() + const { + // If parser's token list[parser's token index]'s type is "open", then return + // true. + return token_list[token_index].type == token_type::OPEN; +} + +template +constexpr bool constructor_string_parser::is_group_close() + const { + // If parser's token list[parser's token index]'s type is "close", then return + // true. + return token_list[token_index].type == token_type::CLOSE; +} + +template +constexpr bool +constructor_string_parser::next_is_authority_slashes() const { + // If the result of running is a non-special pattern char given parser, + // parser's token index + 1, and "/" is false, then return false. + if (!is_non_special_pattern_char(token_index + 1, '/')) { + return false; + } + // If the result of running is a non-special pattern char given parser, + // parser's token index + 2, and "/" is false, then return false. + if (!is_non_special_pattern_char(token_index + 2, '/')) { + return false; + } + return true; +} + +template +constexpr bool constructor_string_parser::is_protocol_suffix() + const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and ":". + return is_non_special_pattern_char(token_index, ':'); +} + +template +void constructor_string_parser::change_state(State new_state, + size_t skip) { + // If parser's state is not "init", not "authority", and not "done", then set + // parser's result[parser's state] to the result of running make a component + // string given parser. + if (state != State::INIT && state != State::AUTHORITY && + state != State::DONE) { + auto value = make_component_string(); + // TODO: Simplify this. + switch (state) { + case State::PROTOCOL: { + result.protocol = value; + break; + } + case State::USERNAME: { + result.username = value; + break; + } + case State::PASSWORD: { + result.password = value; + break; + } + case State::HOSTNAME: { + result.hostname = value; + break; + } + case State::PORT: { + result.port = value; + break; + } + case State::PATHNAME: { + result.pathname = value; + break; + } + case State::SEARCH: { + result.search = value; + break; + } + case State::HASH: { + result.hash = value; + break; + } + default: + ada::unreachable(); + } + } + + // If parser's state is not "init" and new state is not "done", then: + if (state != State::INIT && new_state != State::DONE) { + // If parser's state is "protocol", "authority", "username", or "password"; + // new state is "port", "pathname", "search", or "hash"; and parser's + // result["hostname"] does not exist, then set parser's result["hostname"] + // to the empty string. + if ((state == State::PROTOCOL || state == State::AUTHORITY || + state == State::USERNAME || state == State::PASSWORD) && + (new_state == State::PORT || new_state == State::PATHNAME || + new_state == State::SEARCH || new_state == State::HASH) && + !result.hostname) + result.hostname = ""; + } + + // If parser's state is "protocol", "authority", "username", "password", + // "hostname", or "port"; new state is "search" or "hash"; and parser's + // result["pathname"] does not exist, then: + if ((state == State::PROTOCOL || state == State::AUTHORITY || + state == State::USERNAME || state == State::PASSWORD || + state == State::HOSTNAME || state == State::PORT) && + (new_state == State::SEARCH || new_state == State::HASH) && + !result.pathname) { + if (protocol_matches_a_special_scheme_flag) { + result.pathname = "/"; + } else { + // Otherwise, set parser's result["pathname"] to the empty string. + result.pathname = ""; + } + } + + // If parser's state is "protocol", "authority", "username", "password", + // "hostname", "port", or "pathname"; new state is "hash"; and parser's + // result["search"] does not exist, then set parser's result["search"] to + // the empty string. + if ((state == State::PROTOCOL || state == State::AUTHORITY || + state == State::USERNAME || state == State::PASSWORD || + state == State::HOSTNAME || state == State::PORT || + state == State::PATHNAME) && + new_state == State::HASH && !result.search) { + result.search = ""; + } + + // Set parser's state to new state. + state = new_state; + // Increment parser's token index by skip. + token_index += skip; + // Set parser's component start to parser's token index. + component_start = token_index; + // Set parser's token increment to 0. + token_increment = 0; +} + +template +std::string constructor_string_parser::make_component_string() { + // Assert: parser's token index is less than parser's token list's size. + ADA_ASSERT_TRUE(token_index < token_list.size()); + + // Let token be parser's token list[parser's token index]. + // Let end index be token's index. + const auto end_index = token_list[token_index].index; + // Let component start token be the result of running get a safe token given + // parser and parser's component start. + const auto component_start_token = get_safe_token(component_start); + ADA_ASSERT_TRUE(component_start_token); + // Let component start input index be component start token's index. + const auto component_start_input_index = component_start_token->index; + // Return the code point substring from component start input index to end + // index within parser's input. + return input.substr(component_start_input_index, + end_index - component_start_input_index); +} + +template +constexpr bool +constructor_string_parser::is_an_identity_terminator() const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and "@". + return is_non_special_pattern_char(token_index, '@'); +} + +template +constexpr bool constructor_string_parser::is_pathname_start() + const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and "/". + return is_non_special_pattern_char(token_index, '/'); +} + +template +constexpr bool constructor_string_parser::is_password_prefix() + const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and ":". + return is_non_special_pattern_char(token_index, ':'); +} + +template +constexpr bool constructor_string_parser::is_an_ipv6_open() + const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and "[". + return is_non_special_pattern_char(token_index, '['); +} + +template +constexpr bool constructor_string_parser::is_an_ipv6_close() + const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and "]". + return is_non_special_pattern_char(token_index, ']'); +} + +template +constexpr bool constructor_string_parser::is_port_prefix() + const { + // Return the result of running is a non-special pattern char given parser, + // parser's token index, and ":". + return is_non_special_pattern_char(token_index, ':'); +} + +constexpr void Tokenizer::get_next_code_point() { + ada_log("Tokenizer::get_next_code_point called with index=", next_index); + ADA_ASSERT_TRUE(next_index < input.size()); + // this assumes that we have a valid, non-truncated UTF-8 stream. + code_point = 0; + size_t number_bytes = 0; + unsigned char first_byte = input[next_index]; + + if ((first_byte & 0x80) == 0) { + // 1-byte character (ASCII) + next_index++; + code_point = first_byte; + ada_log("Tokenizer::get_next_code_point returning ASCII code point=", + uint32_t(code_point)); + ada_log("Tokenizer::get_next_code_point next_index =", next_index, + " input.size()=", input.size()); + return; + } + ada_log("Tokenizer::get_next_code_point read first byte=", + uint32_t(first_byte)); + if ((first_byte & 0xE0) == 0xC0) { + code_point = first_byte & 0x1F; + number_bytes = 2; + ada_log("Tokenizer::get_next_code_point two bytes"); + } else if ((first_byte & 0xF0) == 0xE0) { + code_point = first_byte & 0x0F; + number_bytes = 3; + ada_log("Tokenizer::get_next_code_point three bytes"); + } else if ((first_byte & 0xF8) == 0xF0) { + code_point = first_byte & 0x07; + number_bytes = 4; + ada_log("Tokenizer::get_next_code_point four bytes"); + } + ADA_ASSERT_TRUE(number_bytes + next_index <= input.size()); + + for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) { + unsigned char byte = input[i]; + ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte)); + code_point = (code_point << 6) | (byte & 0x3F); + } + ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=", + uint32_t(code_point)); + ada_log("Tokenizer::get_next_code_point next_index =", next_index, + " input.size()=", input.size()); + next_index += number_bytes; +} + +constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) { + ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=", + new_index); + // Set tokenizer's next index to index. + next_index = new_index; + // Run get the next code point given tokenizer. + get_next_code_point(); +} + +inline void Tokenizer::add_token(token_type type, size_t next_position, + size_t value_position, size_t value_length) { + ada_log("Tokenizer::add_token called with type=", to_string(type), + " next_position=", next_position, " value_position=", value_position); + ADA_ASSERT_TRUE(next_position >= value_position); + + // Let token be a new token. + // Set token's type to type. + // Set token's index to tokenizer's index. + // Set token's value to the code point substring from value position with + // length value length within tokenizer's input. + // Append token to the back of tokenizer's token list. + token_list.emplace_back(type, index, + input.substr(value_position, value_length)); + // Set tokenizer's index to next position. + index = next_position; +} + +inline void Tokenizer::add_token_with_default_length(token_type type, + size_t next_position, + size_t value_position) { + // Let computed length be next position - value position. + auto computed_length = next_position - value_position; + // Run add a token given tokenizer, type, next position, value position, and + // computed length. + add_token(type, next_position, value_position, computed_length); +} + +inline void Tokenizer::add_token_with_defaults(token_type type) { + ada_log("Tokenizer::add_token_with_defaults called with type=", + to_string(type)); + // Run add a token with default length given tokenizer, type, tokenizer's next + // index, and tokenizer's index. + add_token_with_default_length(type, next_index, index); +} + +inline ada_warn_unused std::optional +Tokenizer::process_tokenizing_error(size_t next_position, + size_t value_position) { + // If tokenizer's policy is "strict", then throw a TypeError. + if (policy == token_policy::strict) { + ada_log("process_tokenizing_error failed with next_position=", + next_position, " value_position=", value_position); + return errors::type_error; + } + // Assert: tokenizer's policy is "lenient". + ADA_ASSERT_TRUE(policy == token_policy::lenient); + // Run add a token with default length given tokenizer, "invalid-char", next + // position, and value position. + add_token_with_default_length(token_type::INVALID_CHAR, next_position, + value_position); + return std::nullopt; +} + +template +token* url_pattern_parser::try_consume_modifier_token() { + // Let token be the result of running try to consume a token given parser and + // "other-modifier". + auto token = try_consume_token(token_type::OTHER_MODIFIER); + // If token is not null, then return token. + if (token) return token; + // Set token to the result of running try to consume a token given parser and + // "asterisk". + // Return token. + return try_consume_token(token_type::ASTERISK); +} + +template +token* url_pattern_parser::try_consume_regexp_or_wildcard_token( + const token* name_token) { + // Let token be the result of running try to consume a token given parser and + // "regexp". + auto token = try_consume_token(token_type::REGEXP); + // If name token is null and token is null, then set token to the result of + // running try to consume a token given parser and "asterisk". + if (!name_token && !token) { + token = try_consume_token(token_type::ASTERISK); + } + // Return token. + return token; +} + +template +token* url_pattern_parser::try_consume_token(token_type type) { + ada_log("url_pattern_parser::try_consume_token called with type=", + to_string(type)); + // Assert: parser's index is less than parser's token list size. + ADA_ASSERT_TRUE(index < tokens.size()); + // Let next token be parser's token list[parser's index]. + auto& next_token = tokens[index]; + // If next token's type is not type return null. + if (next_token.type != type) return nullptr; + // Increase parser's index by 1. + index++; + // Return next token. + return &next_token; +} + +template +std::string url_pattern_parser::consume_text() { + // Let result be the empty string. + std::string result{}; + // While true: + while (true) { + // Let token be the result of running try to consume a token given parser + // and "char". + auto token = try_consume_token(token_type::CHAR); + // If token is null, then set token to the result of running try to consume + // a token given parser and "escaped-char". + if (!token) token = try_consume_token(token_type::ESCAPED_CHAR); + // If token is null, then break. + if (!token) break; + // Append token's value to the end of result. + result.append(token->value); + } + // Return result. + return result; +} + +template +bool url_pattern_parser::consume_required_token(token_type type) { + ada_log("url_pattern_parser::consume_required_token called with type=", + to_string(type)); + // Let result be the result of running try to consume a token given parser and + // type. + return try_consume_token(type) != nullptr; +} + +template +std::optional +url_pattern_parser::maybe_add_part_from_the_pending_fixed_value() { + // If parser's pending fixed value is the empty string, then return. + if (pending_fixed_value.empty()) { + ada_log("pending_fixed_value is empty"); + return std::nullopt; + } + // Let encoded value be the result of running parser's encoding callback given + // parser's pending fixed value. + auto encoded_value = encoding_callback(pending_fixed_value); + if (!encoded_value) { + ada_log("failed to encode pending_fixed_value: ", pending_fixed_value); + return encoded_value.error(); + } + // Set parser's pending fixed value to the empty string. + pending_fixed_value.clear(); + // Let part be a new part whose type is "fixed-text", value is encoded value, + // and modifier is "none". + // Append part to parser's part list. + parts.emplace_back(url_pattern_part_type::FIXED_TEXT, + std::move(*encoded_value), + url_pattern_part_modifier::none); + return std::nullopt; +} + +template +std::optional url_pattern_parser::add_part( + std::string_view prefix, token* name_token, token* regexp_or_wildcard_token, + std::string_view suffix, token* modifier_token) { + // Let modifier be "none". + auto modifier = url_pattern_part_modifier::none; + // If modifier token is not null: + if (modifier_token) { + // If modifier token's value is "?" then set modifier to "optional". + if (modifier_token->value == "?") { + modifier = url_pattern_part_modifier::optional; + } else if (modifier_token->value == "*") { + // Otherwise if modifier token's value is "*" then set modifier to + // "zero-or-more". + modifier = url_pattern_part_modifier::zero_or_more; + } else if (modifier_token->value == "+") { + // Otherwise if modifier token's value is "+" then set modifier to + // "one-or-more". + modifier = url_pattern_part_modifier::one_or_more; + } + } + // If name token is null and regexp or wildcard token is null and modifier + // is "none": + if (!name_token && !regexp_or_wildcard_token && + modifier == url_pattern_part_modifier::none) { + // Append prefix to the end of parser's pending fixed value. + pending_fixed_value.append(prefix); + return std::nullopt; + } + // Run maybe add a part from the pending fixed value given parser. + if (auto error = maybe_add_part_from_the_pending_fixed_value()) { + return *error; + } + // If name token is null and regexp or wildcard token is null: + if (!name_token && !regexp_or_wildcard_token) { + // Assert: suffix is the empty string. + ADA_ASSERT_TRUE(suffix.empty()); + // If prefix is the empty string, then return. + if (prefix.empty()) return std::nullopt; + // Let encoded value be the result of running parser's encoding callback + // given prefix. + auto encoded_value = encoding_callback(prefix); + if (!encoded_value) { + return encoded_value.error(); + } + // Let part be a new part whose type is "fixed-text", value is encoded + // value, and modifier is modifier. + // Append part to parser's part list. + parts.emplace_back(url_pattern_part_type::FIXED_TEXT, + std::move(*encoded_value), modifier); + return std::nullopt; + } + // Let regexp value be the empty string. + std::string regexp_value{}; + // If regexp or wildcard token is null, then set regexp value to parser's + // segment wildcard regexp. + if (!regexp_or_wildcard_token) { + regexp_value = segment_wildcard_regexp; + } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) { + // Otherwise if regexp or wildcard token's type is "asterisk", then set + // regexp value to the full wildcard regexp value. + regexp_value = ".*"; + } else { + // Otherwise set regexp value to regexp or wildcard token's value. + regexp_value = regexp_or_wildcard_token->value; + } + // Let type be "regexp". + auto type = url_pattern_part_type::REGEXP; + // If regexp value is parser's segment wildcard regexp: + if (regexp_value == segment_wildcard_regexp) { + // Set type to "segment-wildcard". + type = url_pattern_part_type::SEGMENT_WILDCARD; + // Set regexp value to the empty string. + regexp_value.clear(); + } else if (regexp_value == ".*") { + // Otherwise if regexp value is the full wildcard regexp value: + // Set type to "full-wildcard". + type = url_pattern_part_type::FULL_WILDCARD; + // Set regexp value to the empty string. + regexp_value.clear(); + } + // Let name be the empty string. + std::string name{}; + // If name token is not null, then set name to name token's value. + if (name_token) { + name = name_token->value; + } else if (regexp_or_wildcard_token != nullptr) { + // Otherwise if regexp or wildcard token is not null: + // Set name to parser's next numeric name, serialized. + name = std::to_string(next_numeric_name); + // Increment parser's next numeric name by 1. + next_numeric_name++; + } + // If the result of running is a duplicate name given parser and name is + // true, then throw a TypeError. + if (std::ranges::any_of( + parts, [&name](const auto& part) { return part.name == name; })) { + return errors::type_error; + } + // Let encoded prefix be the result of running parser's encoding callback + // given prefix. + auto encoded_prefix = encoding_callback(prefix); + if (!encoded_prefix) return encoded_prefix.error(); + // Let encoded suffix be the result of running parser's encoding callback + // given suffix. + auto encoded_suffix = encoding_callback(suffix); + if (!encoded_suffix) return encoded_suffix.error(); + // Let part be a new part whose type is type, value is regexp value, + // modifier is modifier, name is name, prefix is encoded prefix, and suffix + // is encoded suffix. + // Append part to parser's part list. + parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name), + std::move(*encoded_prefix), std::move(*encoded_suffix)); + return std::nullopt; +} + +template +tl::expected, errors> parse_pattern_string( + std::string_view input, url_pattern_compile_component_options& options, + F& encoding_callback) { + ada_log("parse_pattern_string input=", input); + // Let parser be a new pattern parser whose encoding callback is encoding + // callback and segment wildcard regexp is the result of running generate a + // segment wildcard regexp given options. + auto parser = url_pattern_parser( + encoding_callback, generate_segment_wildcard_regexp(options)); + // Set parser's token list to the result of running tokenize given input and + // "strict". + auto tokenize_result = tokenize(input, token_policy::strict); + if (!tokenize_result) { + ada_log("parse_pattern_string tokenize failed"); + return tl::unexpected(tokenize_result.error()); + } + parser.tokens = std::move(*tokenize_result); + + // While parser's index is less than parser's token list's size: + while (parser.can_continue()) { + // Let char token be the result of running try to consume a token given + // parser and "char". + auto char_token = parser.try_consume_token(token_type::CHAR); + // Let name token be the result of running try to consume a token given + // parser and "name". + auto name_token = parser.try_consume_token(token_type::NAME); + // Let regexp or wildcard token be the result of running try to consume a + // regexp or wildcard token given parser and name token. + auto regexp_or_wildcard_token = + parser.try_consume_regexp_or_wildcard_token(name_token); + // If name token is not null or regexp or wildcard token is not null: + if (name_token || regexp_or_wildcard_token) { + // Let prefix be the empty string. + std::string prefix{}; + // If char token is not null then set prefix to char token's value. + if (char_token) prefix = char_token->value; + // If prefix is not the empty string and not options's prefix code point: + if (!prefix.empty() && prefix != options.get_prefix()) { + // Append prefix to the end of parser's pending fixed value. + parser.pending_fixed_value.append(prefix); + // Set prefix to the empty string. + prefix.clear(); + } + // Run maybe add a part from the pending fixed value given parser. + if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) { + ada_log("maybe_add_part_from_the_pending_fixed_value failed"); + return tl::unexpected(*error); + } + // Let modifier token be the result of running try to consume a modifier + // token given parser. + auto modifier_token = parser.try_consume_modifier_token(); + // Run add a part given parser, prefix, name token, regexp or wildcard + // token, the empty string, and modifier token. + if (auto error = + parser.add_part(prefix, name_token, regexp_or_wildcard_token, "", + modifier_token)) { + ada_log("parser.add_part failed"); + return tl::unexpected(*error); + } + // Continue. + continue; + } + + // Let fixed token be char token. + auto fixed_token = char_token; + // If fixed token is null, then set fixed token to the result of running try + // to consume a token given parser and "escaped-char". + if (!fixed_token) + fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR); + // If fixed token is not null: + if (fixed_token) { + // Append fixed token's value to parser's pending fixed value. + parser.pending_fixed_value.append(fixed_token->value); + // Continue. + continue; + } + // Let open token be the result of running try to consume a token given + // parser and "open". + auto open_token = parser.try_consume_token(token_type::OPEN); + // If open token is not null: + if (open_token) { + // Set prefix be the result of running consume text given parser. + auto prefix_ = parser.consume_text(); + // Set name token to the result of running try to consume a token given + // parser and "name". + name_token = parser.try_consume_token(token_type::NAME); + // Set regexp or wildcard token to the result of running try to consume a + // regexp or wildcard token given parser and name token. + regexp_or_wildcard_token = + parser.try_consume_regexp_or_wildcard_token(name_token); + // Let suffix be the result of running consume text given parser. + auto suffix_ = parser.consume_text(); + // Run consume a required token given parser and "close". + if (!parser.consume_required_token(token_type::CLOSE)) { + ada_log("parser.consume_required_token failed"); + return tl::unexpected(errors::type_error); + } + // Set modifier token to the result of running try to consume a modifier + // token given parser. + auto modifier_token = parser.try_consume_modifier_token(); + // Run add a part given parser, prefix, name token, regexp or wildcard + // token, suffix, and modifier token. + if (auto error = + parser.add_part(prefix_, name_token, regexp_or_wildcard_token, + suffix_, modifier_token)) { + return tl::unexpected(*error); + } + // Continue. + continue; + } + // Run maybe add a part from the pending fixed value given parser. + if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) { + ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992"); + return tl::unexpected(*error); + } + // Run consume a required token given parser and "end". + if (!parser.consume_required_token(token_type::END)) { + return tl::unexpected(errors::type_error); + } + } + ada_log("parser.parts size is: ", parser.parts.size()); + // Return parser's part list. + return parser.parts; +} + +template +bool protocol_component_matches_special_scheme( + url_pattern_component& component) { + // let's avoid unnecessary copy here. + auto& regex = component.regexp; + return regex_provider::regex_match("http", regex) || + regex_provider::regex_match("https", regex) || + regex_provider::regex_match("ws", regex) || + regex_provider::regex_match("wss", regex) || + regex_provider::regex_match("ftp", regex); +} + +template +inline std::optional constructor_string_parser< + regex_provider>::compute_protocol_matches_special_scheme_flag() { + ada_log( + "constructor_string_parser::compute_protocol_matches_special_scheme_" + "flag"); + // Let protocol string be the result of running make a component string given + // parser. + auto protocol_string = make_component_string(); + // Let protocol component be the result of compiling a component given + // protocol string, canonicalize a protocol, and default options. + auto protocol_component = url_pattern_component::compile( + protocol_string, canonicalize_protocol, + url_pattern_compile_component_options::DEFAULT); + if (!protocol_component) { + ada_log("url_pattern_component::compile failed for protocol_string ", + protocol_string); + return protocol_component.error(); + } + // If the result of running protocol component matches a special scheme given + // protocol component is true, then set parser's protocol matches a special + // scheme flag to true. + if (protocol_component_matches_special_scheme(*protocol_component)) { + protocol_matches_a_special_scheme_flag = true; + } + return std::nullopt; +} + +template +tl::expected +constructor_string_parser::parse(std::string_view input) { + ada_log("constructor_string_parser::parse input=", input); + // Let parser be a new constructor string parser whose input is input and + // token list is the result of running tokenize given input and "lenient". + auto token_list = tokenize(input, token_policy::lenient); + if (!token_list) { + return tl::unexpected(token_list.error()); + } + auto parser = constructor_string_parser(input, std::move(*token_list)); + + // While parser's token index is less than parser's token list size: + while (parser.token_index < parser.token_list.size()) { + // Set parser's token increment to 1. + parser.token_increment = 1; + + // If parser's token list[parser's token index]'s type is "end" then: + if (parser.token_list[parser.token_index].type == token_type::END) { + // If parser's state is "init": + if (parser.state == State::INIT) { + // Run rewind given parser. + parser.rewind(); + // If the result of running is a hash prefix given parser is true, then + // run change state given parser, "hash" and 1. + if (parser.is_hash_prefix()) { + parser.change_state(State::HASH, 1); + } else if (parser.is_search_prefix()) { + // Otherwise if the result of running is a search prefix given parser + // is true: Run change state given parser, "search" and 1. + parser.change_state(State::SEARCH, 1); + } else { + // Run change state given parser, "pathname" and 0. + parser.change_state(State::PATHNAME, 0); + } + // Increment parser's token index by parser's token increment. + parser.token_index += parser.token_increment; + // Continue. + continue; + } + + if (parser.state == State::AUTHORITY) { + // If parser's state is "authority": + // Run rewind and set state given parser, and "hostname". + parser.rewind(); + parser.change_state(State::HOSTNAME, 0); + // Increment parser's token index by parser's token increment. + parser.token_index += parser.token_increment; + // Continue. + continue; + } + + // Run change state given parser, "done" and 0. + parser.change_state(State::DONE, 0); + // Break. + break; + } + + // If the result of running is a group open given parser is true: + if (parser.is_group_open()) { + // Increment parser's group depth by 1. + parser.group_depth += 1; + // Increment parser's token index by parser's token increment. + parser.token_index += parser.token_increment; + } + + // If parser's group depth is greater than 0: + if (parser.group_depth > 0) { + // If the result of running is a group close given parser is true, then + // decrement parser's group depth by 1. + if (parser.is_group_close()) { + parser.group_depth -= 1; + } else { + // Increment parser's token index by parser's token increment. + parser.token_index += parser.token_increment; + continue; + } + } + + // Switch on parser's state and run the associated steps: + switch (parser.state) { + case State::INIT: { + // If the result of running is a protocol suffix given parser is true: + if (parser.is_protocol_suffix()) { + // Run rewind and set state given parser and "protocol". + parser.rewind(); + parser.change_state(State::PROTOCOL, 0); + } + break; + } + case State::PROTOCOL: { + // If the result of running is a protocol suffix given parser is true: + if (parser.is_protocol_suffix()) { + // Run compute protocol matches a special scheme flag given parser. + if (const auto error = + parser.compute_protocol_matches_special_scheme_flag()) { + ada_log("compute_protocol_matches_special_scheme_flag failed"); + return tl::unexpected(*error); + } + // Let next state be "pathname". + auto next_state = State::PATHNAME; + // Let skip be 1. + auto skip = 1; + // If the result of running next is authority slashes given parser is + // true: + if (parser.next_is_authority_slashes()) { + // Set next state to "authority". + next_state = State::AUTHORITY; + // Set skip to 3. + skip = 3; + } else if (parser.protocol_matches_a_special_scheme_flag) { + // Otherwise if parser's protocol matches a special scheme flag is + // true, then set next state to "authority". + next_state = State::AUTHORITY; + } + + // Run change state given parser, next state, and skip. + parser.change_state(next_state, skip); + } + break; + } + case State::AUTHORITY: { + // If the result of running is an identity terminator given parser is + // true, then run rewind and set state given parser and "username". + if (parser.is_an_identity_terminator()) { + parser.rewind(); + parser.change_state(State::USERNAME, 0); + } else if (parser.is_pathname_start() || parser.is_search_prefix() || + parser.is_hash_prefix()) { + // Otherwise if any of the following are true: + // - the result of running is a pathname start given parser; + // - the result of running is a search prefix given parser; or + // - the result of running is a hash prefix given parser, + // then run rewind and set state given parser and "hostname". + parser.rewind(); + parser.change_state(State::HOSTNAME, 0); + } + break; + } + case State::USERNAME: { + // If the result of running is a password prefix given parser is true, + // then run change state given parser, "password", and 1. + if (parser.is_password_prefix()) { + parser.change_state(State::PASSWORD, 1); + } else if (parser.is_an_identity_terminator()) { + // Otherwise if the result of running is an identity terminator given + // parser is true, then run change state given parser, "hostname", + // and 1. + parser.change_state(State::HOSTNAME, 1); + } + break; + } + case State::PASSWORD: { + // If the result of running is an identity terminator given parser is + // true, then run change state given parser, "hostname", and 1. + if (parser.is_an_identity_terminator()) { + parser.change_state(State::HOSTNAME, 1); + } + break; + } + case State::HOSTNAME: { + // If the result of running is an IPv6 open given parser is true, then + // increment parser's hostname IPv6 bracket depth by 1. + if (parser.is_an_ipv6_open()) { + parser.hostname_ipv6_bracket_depth += 1; + } else if (parser.is_an_ipv6_close()) { + // Otherwise if the result of running is an IPv6 close given parser is + // true, then decrement parser's hostname IPv6 bracket depth by 1. + parser.hostname_ipv6_bracket_depth -= 1; + } else if (parser.is_port_prefix() && + parser.hostname_ipv6_bracket_depth == 0) { + // Otherwise if the result of running is a port prefix given parser is + // true and parser's hostname IPv6 bracket depth is zero, then run + // change state given parser, "port", and 1. + parser.change_state(State::PORT, 1); + } else if (parser.is_pathname_start()) { + // Otherwise if the result of running is a pathname start given parser + // is true, then run change state given parser, "pathname", and 0. + parser.change_state(State::PATHNAME, 0); + } else if (parser.is_search_prefix()) { + // Otherwise if the result of running is a search prefix given parser + // is true, then run change state given parser, "search", and 1. + parser.change_state(State::SEARCH, 1); + } else if (parser.is_hash_prefix()) { + // Otherwise if the result of running is a hash prefix given parser is + // true, then run change state given parser, "hash", and 1. + parser.change_state(State::HASH, 1); + } + + break; + } + case State::PORT: { + // If the result of running is a pathname start given parser is true, + // then run change state given parser, "pathname", and 0. + if (parser.is_pathname_start()) { + parser.change_state(State::PATHNAME, 0); + } else if (parser.is_search_prefix()) { + // Otherwise if the result of running is a search prefix given parser + // is true, then run change state given parser, "search", and 1. + parser.change_state(State::SEARCH, 1); + } else if (parser.is_hash_prefix()) { + // Otherwise if the result of running is a hash prefix given parser is + // true, then run change state given parser, "hash", and 1. + parser.change_state(State::HASH, 1); + } + break; + } + case State::PATHNAME: { + // If the result of running is a search prefix given parser is true, + // then run change state given parser, "search", and 1. + if (parser.is_search_prefix()) { + parser.change_state(State::SEARCH, 1); + } else if (parser.is_hash_prefix()) { + // Otherwise if the result of running is a hash prefix given parser is + // true, then run change state given parser, "hash", and 1. + parser.change_state(State::HASH, 1); + } + break; + } + case State::SEARCH: { + // If the result of running is a hash prefix given parser is true, then + // run change state given parser, "hash", and 1. + if (parser.is_hash_prefix()) { + parser.change_state(State::HASH, 1); + } + break; + } + case State::HASH: { + // Do nothing + break; + } + default: { + // Assert: This step is never reached. + unreachable(); + } + } + + // Increment parser's token index by parser's token increment. + parser.token_index += parser.token_increment; + } + + // If parser's result contains "hostname" and not "port", then set parser's + // result["port"] to the empty string. + if (parser.result.hostname && !parser.result.port) { + parser.result.port = ""; + } + + // Return parser's result. + return parser.result; +} + +} // namespace ada::url_pattern_helpers +#endif // ADA_INCLUDE_URL_PATTERN +#endif +/* end file include/ada/url_pattern_helpers-inl.h */ + // Public API /* begin file include/ada/ada_version.h */ /** @@ -7256,13 +10515,13 @@ url_search_params_entries_iter::next() { #ifndef ADA_ADA_VERSION_H #define ADA_ADA_VERSION_H -#define ADA_VERSION "2.9.0" +#define ADA_VERSION "3.3.0" namespace ada { enum { - ADA_VERSION_MAJOR = 2, - ADA_VERSION_MINOR = 9, + ADA_VERSION_MAJOR = 3, + ADA_VERSION_MINOR = 3, ADA_VERSION_REVISION = 0, }; @@ -7270,61 +10529,35 @@ enum { #endif // ADA_ADA_VERSION_H /* end file include/ada/ada_version.h */ -/* begin file include/ada/implementation.h */ +/* begin file include/ada/implementation-inl.h */ /** - * @file implementation.h - * @brief Definitions for user facing functions for parsing URL and it's - * components. + * @file implementation-inl.h */ -#ifndef ADA_IMPLEMENTATION_H -#define ADA_IMPLEMENTATION_H - -#include -#include +#ifndef ADA_IMPLEMENTATION_INL_H +#define ADA_IMPLEMENTATION_INL_H -namespace ada { -enum class errors { generic_error }; -template -using result = tl::expected; -/** - * The URL parser takes a scalar value string input, with an optional null or - * base URL base (default null). The parser assumes the input is a valid ASCII - * or UTF-8 string. - * - * @param input the string input to analyze (must be valid ASCII or UTF-8) - * @param base_url the optional URL input to use as a base url. - * @return a parsed URL. - */ -template -ada_warn_unused ada::result parse( - std::string_view input, const result_type* base_url = nullptr); +#include +#include -extern template ada::result parse(std::string_view input, - const url* base_url); -extern template ada::result parse( - std::string_view input, const url_aggregator* base_url); +namespace ada { -/** - * Verifies whether the URL strings can be parsed. The function assumes - * that the inputs are valid ASCII or UTF-8 strings. - * @see https://url.spec.whatwg.org/#dom-url-canparse - * @return If URL can be parsed or not. - */ -bool can_parse(std::string_view input, - const std::string_view* base_input = nullptr); +#if ADA_INCLUDE_URL_PATTERN +template +ada_warn_unused tl::expected, errors> +parse_url_pattern(std::variant&& input, + const std::string_view* base_url, + const url_pattern_options* options) { + return parser::parse_url_pattern_impl(std::move(input), + base_url, options); +} +#endif // ADA_INCLUDE_URL_PATTERN -/** - * Computes a href string from a file path. The function assumes - * that the input is a valid ASCII or UTF-8 string. - * @return a href string (starts with file:://) - */ -std::string href_from_file(std::string_view path); } // namespace ada -#endif // ADA_IMPLEMENTATION_H -/* end file include/ada/implementation.h */ +#endif // ADA_IMPLEMENTATION_INL_H +/* end file include/ada/implementation-inl.h */ #endif // ADA_H /* end file include/ada.h */ diff --git a/test-app/runtime/src/main/cpp/napi/common/bytecode_container.h b/test-app/runtime/src/main/cpp/napi/common/bytecode_container.h new file mode 100644 index 00000000..81854a42 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/common/bytecode_container.h @@ -0,0 +1,53 @@ +// +// Shared helpers for the NativeScript bytecode container used by the QuickJS +// family (and any engine that wraps engine bytecode in our container). +// +// Container layout (see tools/bytecode-compiler/native/*-compile.c): +// [8-byte magic][4-byte format version, little-endian][engine payload] +// +// Hermes is the exception: it stores raw HBC (its own magic, no container), so +// it doesn't use these helpers. +// +#ifndef NS_BYTECODE_CONTAINER_H +#define NS_BYTECODE_CONTAINER_H + +#include +#include +#include +#include + +namespace nsbc { + +// magic(8) + version(4) +static const size_t kHeaderLen = 12; + +// Turn a script source URL into the on-disk path we read bytecode from. Returns +// false for synthetic / non-file sources (e.g. ""). +inline bool ResolvePath(const char *file, std::string &out) { + if (file == nullptr) return false; + std::string f(file); + static const std::string scheme = "file://"; + if (f.rfind(scheme, 0) == 0) { + out = f.substr(scheme.size()); + } else if (!f.empty() && f[0] == '/') { + out = f; + } else { + return false; + } + return !out.empty(); +} + +// Cheaply check whether `path` starts with the 8-byte container magic. `magic8` +// must point to 8 bytes (e.g. the string literal "NSBCQJS" — 7 chars + NUL). +inline bool HasMagic(const std::string &path, const char *magic8) { + uint8_t head[8]; + FILE *fp = fopen(path.c_str(), "rb"); + if (!fp) return false; + size_t n = fread(head, 1, sizeof(head), fp); + fclose(fp); + return n == sizeof(head) && memcmp(head, magic8, sizeof(head)) == 0; +} + +} // namespace nsbc + +#endif // NS_BYTECODE_CONTAINER_H diff --git a/test-app/runtime/src/main/cpp/napi/common/js_native_api.h b/test-app/runtime/src/main/cpp/napi/common/js_native_api.h index 97715d0b..c9b43624 100644 --- a/test-app/runtime/src/main/cpp/napi/common/js_native_api.h +++ b/test-app/runtime/src/main/cpp/napi/common/js_native_api.h @@ -3,8 +3,28 @@ #include "js_native_api_types.h" -#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) -typedef uint16_t char16_t; +// If you need __declspec(dllimport), either include instead, or +// define NAPI_EXTERN as __declspec(dllimport) on the compiler's command line. +#ifndef NAPI_EXTERN +#ifdef _WIN32 +#define NAPI_EXTERN __declspec(dllexport) +#elif defined(__wasm__) +#define NAPI_EXTERN \ + __attribute__((visibility("default"))) \ + __attribute__((__import_module__("napi"))) +#else +#define NAPI_EXTERN __attribute__((visibility("default"))) +#endif +#endif + +#define NAPI_AUTO_LENGTH SIZE_MAX + +#ifdef __cplusplus +#define EXTERN_C_START extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_START +#define EXTERN_C_END #endif EXTERN_C_START @@ -17,6 +37,8 @@ EXTERN_C_START #define NAPI_VERSION_EXPERIMENTAL 2147483647 #define NAPI_VERSION 8 +#define NAPI_EXPERIMENTAL 1 + NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info **result); // Getters for defined singletons @@ -429,7 +451,12 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_reject_deferred(napi_env env, NAPI_EXTERN napi_status NAPI_CDECL napi_is_promise(napi_env env, napi_value value, bool *is_promise); - +#ifdef __PRIMJS__ +// Running a script +NAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env, const char* script, + size_t length, const char* filename, + napi_value* result); +#else // Running a script NAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env, napi_value script, @@ -439,6 +466,8 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_run_script_source(napi_env env, napi_value script, const char* source_url, napi_value* result); +#endif + // Memory management NAPI_EXTERN napi_status NAPI_CDECL napi_adjust_external_memory( @@ -538,11 +567,35 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env, napi_value object); #ifdef USE_HOST_OBJECT -NAPI_EXTERN napi_status NAPI_CDECL napi_create_host_object(napi_env env, napi_value value, napi_finalize finalize, void* data, bool is_array, napi_value getter, napi_value setter, napi_value* result); +// Creates a host object: a transparent proxy whose property operations are +// dispatched to the native `methods` (see napi_host_object_methods). `data` is +// passed to every callback and is also retrievable via +// napi_get_host_object_data; `finalize` (optional) runs when the host object is +// garbage-collected. `methods` and its `get`/`set` members are required. +NAPI_EXTERN napi_status NAPI_CDECL +napi_create_host_object(napi_env env, + napi_finalize finalize, + void* data, + const napi_host_object_methods* methods, + napi_value* result); -NAPI_EXTERN napi_status NAPI_CDECL napi_get_host_object_data(napi_env env, napi_value object, void** data); +NAPI_EXTERN napi_status NAPI_CDECL napi_get_host_object_data(napi_env env, + napi_value object, + void** data); + +NAPI_EXTERN napi_status NAPI_CDECL napi_is_host_object(napi_env env, + napi_value object, + bool* result); +#endif -NAPI_EXTERN napi_status NAPI_CDECL napi_is_host_object(napi_env env, napi_value object, bool* result); +#ifdef NAPI_EXPERIMENTAL +// Defers `finalize_cb` to a safe pass after the GC finalizer, where reference +// and other JS/GC-state-affecting Node-API calls are allowed. Implemented for +// the V8 engine. +NAPI_EXTERN napi_status NAPI_CDECL node_api_post_finalizer(napi_env env, + napi_finalize finalize_cb, + void* finalize_data, + void* finalize_hint); #endif #endif // NAPI_VERSION >= 8 diff --git a/test-app/runtime/src/main/cpp/napi/common/js_native_api_types.h b/test-app/runtime/src/main/cpp/napi/common/js_native_api_types.h index 7bff8110..c6bc066c 100644 --- a/test-app/runtime/src/main/cpp/napi/common/js_native_api_types.h +++ b/test-app/runtime/src/main/cpp/napi/common/js_native_api_types.h @@ -1,19 +1,42 @@ #ifndef SRC_JS_NATIVE_API_TYPES_H_ #define SRC_JS_NATIVE_API_TYPES_H_ -#include +// Use INT_MAX, this should only be consumed by the pre-processor anyway. +#define NAPI_VERSION_EXPERIMENTAL 2147483647 +#ifndef NAPI_VERSION +#ifdef NAPI_EXPERIMENTAL +#define NAPI_VERSION NAPI_VERSION_EXPERIMENTAL +#else +// The baseline version for Node-API. +// NAPI_VERSION controls which version is used by default when compiling +// a native addon. If the addon developer wants to use functions from a +// newer Node-API version not yet available in all LTS versions, they can +// set NAPI_VERSION to explicitly depend on that version. +#define NAPI_VERSION 8 +#endif +#endif -#ifdef __cplusplus -#define EXTERN_C_START \ -extern "C" \ -{ -#define EXTERN_C_END } +#if defined(NAPI_EXPERIMENTAL) && \ + !defined(NODE_API_EXPERIMENTAL_NO_WARNING) && \ + !defined(NODE_WANT_INTERNALS) +#ifdef _MSC_VER +#pragma message("NAPI_EXPERIMENTAL is enabled. " \ + "Experimental features may be unstable.") #else -#define EXTERN_C_START -#define EXTERN_C_END +#warning "NAPI_EXPERIMENTAL is enabled. " \ + "Experimental features may be unstable." #endif +#endif + +// This file needs to be compatible with C compilers. +// This is a public include file, and these includes have essentially +// become part of its API. +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) -#define NAPI_EXTERN __attribute__((visibility("default"))) +#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) +typedef uint16_t char16_t; +#endif #ifndef NAPI_CDECL #ifdef _WIN32 @@ -23,56 +46,69 @@ extern "C" #endif #endif -EXTERN_C_START -typedef struct napi_runtime__ *napi_runtime; -typedef struct napi_env__ *napi_env; -typedef struct napi_value__ *napi_value; -typedef struct napi_ref__ *napi_ref; -typedef struct napi_handle_scope__ *napi_handle_scope; -typedef struct napi_handle_scope__ *napi_escapable_handle_scope; -typedef struct napi_callback_info__ *napi_callback_info; -typedef struct napi_deferred__* napi_deferred; +// JSVM API types are all opaque pointers for ABI stability +// typedef undefined structs instead of void* for compile time type safety +typedef struct napi_env__* napi_env; + +// We need to mark APIs which can be called during garbage collection (GC), +// meaning that they do not affect the state of the JS engine, and can +// therefore be called synchronously from a finalizer that itself runs +// synchronously during GC. Such APIs can receive either a `napi_env` or a +// `node_api_basic_env` as their first parameter, because we should be able to +// also call them during normal, non-garbage-collecting operations, whereas +// APIs that affect the state of the JS engine can only receive a `napi_env` as +// their first parameter, because we must not call them during GC. In lieu of +// inheritance, we use the properties of the const qualifier to accomplish +// this, because both a const and a non-const value can be passed to an API +// expecting a const value, but only a non-const value can be passed to an API +// expecting a non-const value. +// +// In conjunction with appropriate CFLAGS to warn us if we're passing a const +// (basic) environment into an API that expects a non-const environment, and +// the definition of basic finalizer function pointer types below, which +// receive a basic environment as their first parameter, and can thus only call +// basic APIs (unless the user explicitly casts the environment), we achieve +// the ability to ensure at compile time that we do not call APIs that affect +// the state of the JS engine from a synchronous (basic) finalizer. +#if !defined(NAPI_EXPERIMENTAL) || \ + (defined(NAPI_EXPERIMENTAL) && \ + (defined(NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT) || \ + defined(NODE_API_EXPERIMENTAL_BASIC_ENV_OPT_OUT))) +typedef struct napi_env__* node_api_nogc_env; +#else +typedef const struct napi_env__* node_api_nogc_env; +#endif +typedef node_api_nogc_env node_api_basic_env; +typedef struct napi_value__* napi_value; +typedef struct napi_ref__* napi_ref; +typedef struct napi_handle_scope__* napi_handle_scope; +typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope; +typedef struct napi_callback_info__* napi_callback_info; +typedef struct napi_deferred__* napi_deferred; -typedef enum -{ +typedef enum { napi_default = 0, napi_writable = 1 << 0, napi_enumerable = 1 << 1, napi_configurable = 1 << 2, - + // Used with napi_define_class to distinguish static properties // from instance properties. Ignored by napi_define_properties. napi_static = 1 << 10, - + +#if NAPI_VERSION >= 8 // Default for class methods. napi_default_method = napi_writable | napi_configurable, - + // Default for object properties, like in JS obj[prop]. napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable, +#endif // NAPI_VERSION >= 8 } napi_property_attributes; -typedef napi_value (*napi_callback)(napi_env env, napi_callback_info callbackInfo); - -typedef void (*napi_finalize)(napi_env env, void *finalizeData, void *finalizeHint); - -typedef struct { - // One of utf8name or name should be NULL. - const char* utf8name; - napi_value name; - - napi_callback method; - napi_callback getter; - napi_callback setter; - napi_value value; - - napi_property_attributes attributes; - void* data; -} napi_property_descriptor; - -typedef enum -{ +typedef enum { + // ES6 types (corresponds to typeof) napi_undefined, napi_null, napi_boolean, @@ -85,6 +121,22 @@ typedef enum napi_bigint, } napi_valuetype; +typedef enum { + napi_int8_array, + napi_uint8_array, + napi_uint8_clamped_array, + napi_int16_array, + napi_uint16_array, + napi_int32_array, + napi_uint32_array, + napi_float32_array, + napi_float64_array, + napi_bigint64_array, + napi_biguint64_array, +#define NODE_API_HAS_FLOAT16_ARRAY + napi_float16_array, +} napi_typedarray_type; + typedef enum { napi_ok, napi_invalid_arg, @@ -107,60 +159,140 @@ typedef enum { napi_date_expected, napi_arraybuffer_expected, napi_detachable_arraybuffer_expected, - napi_would_deadlock, /* unused */ + napi_would_deadlock, // unused napi_no_external_buffers_allowed, napi_cannot_run_js, - // Custom errors - napi_handle_scope_empty, - napi_memory_error, - napi_promise_exception } napi_status; +// Note: when adding a new enum value to `napi_status`, please also update +// * `const int last_status` in the definition of `napi_get_last_error_info()' +// in file js_native_api_v8.cc. +// * `const char* error_messages[]` in file js_native_api_v8.cc with a brief +// message explaining the error. +// * the definition of `napi_status` in doc/api/n-api.md to reflect the newly +// added value(s). -typedef enum { - napi_int8_array, - napi_uint8_array, - napi_uint8_clamped_array, - napi_int16_array, - napi_uint16_array, - napi_int32_array, - napi_uint32_array, - napi_float32_array, - napi_float64_array, - napi_bigint64_array, - napi_biguint64_array, -} napi_typedarray_type; +typedef napi_value(NAPI_CDECL* napi_callback)(napi_env env, + napi_callback_info info); +typedef void(NAPI_CDECL* napi_finalize)(napi_env env, + void* finalize_data, + void* finalize_hint); + +#if !defined(NAPI_EXPERIMENTAL) || \ + (defined(NAPI_EXPERIMENTAL) && \ + (defined(NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT) || \ + defined(NODE_API_EXPERIMENTAL_BASIC_ENV_OPT_OUT))) +typedef napi_finalize node_api_nogc_finalize; +#else +typedef void(NAPI_CDECL* node_api_nogc_finalize)(node_api_nogc_env env, + void* finalize_data, + void* finalize_hint); +#endif +typedef node_api_nogc_finalize node_api_basic_finalize; + +// A finalizer that can be called from any thread and at any time. +typedef void(NAPI_CDECL* node_api_noenv_finalize)(void* finalize_data, + void* finalize_hint); + +typedef struct { + // One of utf8name or name should be NULL. + const char* utf8name; + napi_value name; + + napi_callback method; + napi_callback getter; + napi_callback setter; + napi_value value; + + napi_property_attributes attributes; + void* data; +} napi_property_descriptor; + +typedef struct { + const char* error_message; + void* engine_reserved; + uint32_t engine_error_code; + napi_status error_code; +} napi_extended_error_info; +#if NAPI_VERSION >= 6 typedef enum { - napi_key_include_prototypes, - napi_key_own_only + napi_key_include_prototypes, + napi_key_own_only } napi_key_collection_mode; typedef enum { - napi_key_keep_numbers, - napi_key_numbers_to_strings -} napi_key_conversion; + napi_key_all_properties = 0, + napi_key_writable = 1, + napi_key_enumerable = 1 << 1, + napi_key_configurable = 1 << 2, + napi_key_skip_strings = 1 << 3, + napi_key_skip_symbols = 1 << 4 +} napi_key_filter; typedef enum { - napi_key_all_properties = 0, - napi_key_writable = 1, - napi_key_enumerable = 1 << 1, - napi_key_configurable = 1 << 2, - napi_key_skip_strings = 1 << 3, - napi_key_skip_symbols = 1 << 4 -} napi_key_filter; + napi_key_keep_numbers, + napi_key_numbers_to_strings +} napi_key_conversion; +#endif // NAPI_VERSION >= 6 +#if NAPI_VERSION >= 8 typedef struct { - uint64_t lower; - uint64_t upper; + uint64_t lower; + uint64_t upper; } napi_type_tag; +#endif // NAPI_VERSION >= 8 + + + +#ifdef USE_HOST_OBJECT +// Native handlers for a host object. The host object is a transparent proxy: +// every property operation is dispatched to these callbacks. Each receives the +// host object itself as `host_object` and the `data` pointer given to +// napi_create_host_object. `property` is the key as a napi_value (a number for +// indexed access, a string or symbol otherwise). +// +// `get` and `set` are required; `has`, `delete_property` and `own_keys` are +// optional (NULL means the operation is not intercepted / reports absent). +typedef napi_value(NAPI_CDECL* napi_host_object_get_cb)(napi_env env, + napi_value host_object, + napi_value property, + void* data); +typedef void(NAPI_CDECL* napi_host_object_set_cb)(napi_env env, + napi_value host_object, + napi_value property, + napi_value value, + void* data); +typedef int (NAPI_CDECL* napi_host_object_has_cb)(napi_env env, + napi_value host_object, + napi_value property, + void* data); +typedef int (NAPI_CDECL* napi_host_object_delete_cb)(napi_env env, + napi_value host_object, + napi_value property, + void* data); +typedef napi_value(NAPI_CDECL* napi_host_object_own_keys_cb)( + napi_env env, napi_value host_object, void* data); + +// Optional fast paths for integer-indexed access. +typedef napi_value(NAPI_CDECL* napi_host_object_indexed_get_cb)( + napi_env env, napi_value host_object, uint32_t index, void* data); +typedef void(NAPI_CDECL* napi_host_object_indexed_set_cb)( + napi_env env, + napi_value host_object, + uint32_t index, + napi_value value, + void* data); typedef struct { - const char* error_message; - void* engine_reserved; - uint32_t engine_error_code; - napi_status error_code; -} napi_extended_error_info; + napi_host_object_get_cb get; + napi_host_object_set_cb set; + napi_host_object_has_cb has; + napi_host_object_delete_cb delete_property; + napi_host_object_own_keys_cb own_keys; + napi_host_object_indexed_get_cb indexed_get; + napi_host_object_indexed_set_cb indexed_set; +} napi_host_object_methods; +#endif -EXTERN_C_END -#endif // SRC_JS_NATIVE_API_TYPES_H_ +#endif // SRC_JS_NATIVE_API_TYPES_H_ diff --git a/test-app/runtime/src/main/cpp/napi/common/jsr_common.h b/test-app/runtime/src/main/cpp/napi/common/jsr_common.h index 76dbb466..1f64c524 100644 --- a/test-app/runtime/src/main/cpp/napi/common/jsr_common.h +++ b/test-app/runtime/src/main/cpp/napi/common/jsr_common.h @@ -7,13 +7,15 @@ #include "js_native_api.h" -napi_status js_create_runtime(napi_runtime* runtime); -napi_status js_create_napi_env(napi_env* env, napi_runtime runtime); +typedef struct jsr_ns_runtime__ *jsr_ns_runtime; + +napi_status js_create_runtime(jsr_ns_runtime* runtime); +napi_status js_create_napi_env(napi_env* env, jsr_ns_runtime runtime); napi_status js_set_runtime_flags(const char* flags); napi_status js_lock_env(napi_env env); napi_status js_unlock_env(napi_env env); napi_status js_free_napi_env(napi_env env); -napi_status js_free_runtime(napi_runtime runtime); +napi_status js_free_runtime(jsr_ns_runtime runtime); napi_status js_execute_script(napi_env env, napi_value script, const char *file, @@ -26,6 +28,26 @@ napi_status js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64 napi_status js_cache_script(napi_env env, const char *source, const char *file); napi_status js_run_cached_script(napi_env env, const char * file, napi_value script, void* cache, napi_value *result); +/** + * Compile-time bytecode fast path. The runtime calls this BEFORE reading a + * module's source, so a precompiled module is never read/compiled as text. + * + * `file` is the module's source URL (e.g. "file:///.../app/foo.js"). If it holds + * precompiled bytecode for this engine, load + run it and set *result to the + * module wrapper function (mirroring js_execute_script for the equivalent source). + * + * The bytecode-loading implementation lives entirely in each engine's jsr.cpp; + * this is just the thin entry point the module loader calls. + * + * Returns: + * - napi_ok : `file` was bytecode; it ran; *result is set. + * - napi_cannot_run_js : `file` is NOT bytecode (caller compiles source). + * Detection only peeks the header — no full read — and + * engines without bytecode support always return this. + * - other : `file` was bytecode but failed/threw (do NOT fall back). + */ +napi_status js_run_bytecode_file(napi_env env, const char *file, napi_value *result); + napi_status js_get_runtime_version(napi_env env, napi_value* version); #endif //TEST_APP_JSR_COMMON_H diff --git a/test-app/runtime/src/main/cpp/napi/common/native_api_util.h b/test-app/runtime/src/main/cpp/napi/common/native_api_util.h index 40121280..97b745c6 100644 --- a/test-app/runtime/src/main/cpp/napi/common/native_api_util.h +++ b/test-app/runtime/src/main/cpp/napi/common/native_api_util.h @@ -5,6 +5,10 @@ #include #include +#ifdef __ANDROID__ +#include +#endif + #ifndef NAPI_PREAMBLE #define NAPI_PREAMBLE napi_status status; #endif @@ -41,6 +45,36 @@ } \ } +// Faster varargs prologue for hot callbacks. Reads arguments into a fixed stack +// buffer with a SINGLE napi_get_cb_info call (no heap allocation, no redundant +// argc-probe call), falling back to a heap vector only when the real arity +// exceeds the inline capacity `stackn`. Exposes `napi_value *argv` + `size_t +// argc`, so call sites use `argv`/`argv[i]` (a pointer) instead of a vector. +#define NAPI_CALLBACK_BEGIN_VARGS_FAST(stackn) \ + napi_status status; \ + size_t argc = (stackn); \ + void *data; \ + napi_value jsThis; \ + napi_value __argv_stack[(stackn)]; \ + NAPI_GUARD(napi_get_cb_info(env, info, &argc, __argv_stack, &jsThis, &data)) \ + { \ + NAPI_THROW_LAST_ERROR \ + return NULL; \ + } \ + std::vector __argv_heap; \ + napi_value *argv = __argv_stack; \ + if (argc > (stackn)) \ + { \ + __argv_heap.resize(argc); \ + NAPI_GUARD( \ + napi_get_cb_info(env, info, &argc, __argv_heap.data(), nullptr, nullptr)) \ + { \ + NAPI_THROW_LAST_ERROR \ + return NULL; \ + } \ + argv = __argv_heap.data(); \ + } + #define NAPI_ERROR_INFO \ const napi_extended_error_info *error_info = \ (napi_extended_error_info *)malloc(sizeof(napi_extended_error_info)); \ @@ -50,28 +84,30 @@ NAPI_ERROR_INFO \ napi_throw_error(env, NULL, error_info->error_message); -#ifndef DEBUG +#ifdef __ANDROID__ +#define NAPI_LOG_ERROR(status_val, expr_str) \ + __android_log_print(ANDROID_LOG_ERROR, "TNS.Native", \ + "Node-API returned error: %d\n %s\n ^\n at %s:%d", \ + (int)(status_val), (expr_str), __FILE__, __LINE__) +#else +#define NAPI_LOG_ERROR(status_val, expr_str) ((void)0) +#endif +// NAPI_GUARD(expr) { ...on-error block... } +// Assigns the result of `expr` to the in-scope `status`, logs a diagnostic +// (status, expression, file:line) on failure so an invalid runtime state is +// traceable, and runs the trailing block when the call did not return napi_ok. +// napi_pending_exception is JS-level control flow (a callback threw), not an +// invalid runtime state, so it is intentionally not logged — the caller's +// exception handling deals with it. #define NAPI_GUARD(expr) \ status = expr; \ - if (status != napi_ok) \ + if (status != napi_ok && status != napi_pending_exception) \ { \ - NAPI_ERROR_INFO \ - std::stringstream msg; \ - msg << "Node-API returned error: " << status << "\n " << #expr \ - << "\n ^\n " \ - << "at " << __FILE__ << ":" << __LINE__ << ""; \ + NAPI_LOG_ERROR(status, #expr); \ } \ if (status != napi_ok) -#else - -#define NAPI_GUARD(expr) \ - status = expr; \ - if (status != napi_ok) - -#endif - #define NAPI_FUNCTION(name) \ napi_value JS_##name(napi_env env, napi_callback_info cbinfo) @@ -86,66 +122,107 @@ #define UNDEFINED \ napi_util::undefined(env); +// The helpers below are convenience wrappers around the raw Node-API. Every one +// of them guards the status of the underlying napi_* call via NAPI_GUARD and +// returns a well-defined fallback on failure instead of reading a possibly +// uninitialized out-parameter: +// - value accessors return nullptr, +// - boolean predicates return false, +// - numeric accessors return 0. +// This keeps a single failed napi_* call (e.g. an unclassifiable value, or a +// call made while a JS exception is pending) from producing undefined behavior +// in the caller. namespace napi_util { inline napi_value undefined(napi_env env) { - napi_value undefined; - napi_get_undefined(env, &undefined); + napi_status status; + napi_value undefined = nullptr; + NAPI_GUARD(napi_get_undefined(env, &undefined)) { + return nullptr; + } return undefined; } inline napi_value null(napi_env env) { - napi_value null; - napi_get_null(env, &null); + napi_status status; + napi_value null = nullptr; + NAPI_GUARD(napi_get_null(env, &null)) { + return nullptr; + } return null; } inline napi_ref make_ref(napi_env env, napi_value value, uint32_t initialCount = 1) { - napi_ref ref; - napi_create_reference(env, value, initialCount, &ref); + napi_status status; + napi_ref ref = nullptr; + NAPI_GUARD(napi_create_reference(env, value, initialCount, &ref)) { + return nullptr; + } return ref; } inline napi_value get_ref_value(napi_env env, napi_ref ref) { - napi_value value; - napi_get_reference_value(env, ref, &value); + napi_status status; + napi_value value = nullptr; + NAPI_GUARD(napi_get_reference_value(env, ref, &value)) { + return nullptr; + } return value; } inline napi_value get__proto__(napi_env env, napi_value object) { - napi_value proto; - napi_get_named_property(env, object, "__proto__", &proto); + napi_status status; + napi_value proto = nullptr; + NAPI_GUARD(napi_get_named_property(env, object, "__proto__", &proto)) { + return nullptr; + } return proto; } inline void set__proto__(napi_env env, napi_value object, napi_value __proto__) { - napi_set_named_property(env, object, "__proto__", __proto__); + napi_status status; + NAPI_GUARD(napi_set_named_property(env, object, "__proto__", __proto__)) {} } inline napi_value getPrototypeOf(napi_env env, napi_value object) { - napi_value proto; - napi_get_prototype(env, object, &proto); + napi_status status; + napi_value proto = nullptr; + NAPI_GUARD(napi_get_prototype(env, object, &proto)) { + return nullptr; + } return proto; } inline napi_value get_prototype(napi_env env, napi_value object) { - napi_value prototype; - napi_get_named_property(env, object, "prototype", &prototype); + napi_status status; + napi_value prototype = nullptr; + NAPI_GUARD(napi_get_named_property(env, object, "prototype", &prototype)) { + return nullptr; + } return prototype; } inline void set_prototype(napi_env env, napi_value object, napi_value prototype) { - napi_set_named_property(env, object, "prototype", prototype); + napi_status status; + NAPI_GUARD(napi_set_named_property(env, object, "prototype", prototype)) {} } inline char *get_string_value(napi_env env, napi_value str, size_t size = 0) { + napi_status status; size_t str_size = size; if (str_size == 0) { - napi_get_value_string_utf8(env, str, nullptr, 0, &str_size); + // Probe the required length first; bail out (rather than allocating a + // garbage-sized buffer) if the value is not a readable string. + NAPI_GUARD(napi_get_value_string_utf8(env, str, nullptr, 0, &str_size)) { + return nullptr; + } } char *buffer = new char[str_size + 1]; - napi_get_value_string_utf8(env, str, buffer, str_size + 1, nullptr); + NAPI_GUARD(napi_get_value_string_utf8(env, str, buffer, str_size + 1, nullptr)) { + delete[] buffer; + return nullptr; + } return buffer; } @@ -177,16 +254,17 @@ namespace napi_util { } inline void setPrototypeOf(napi_env env, napi_value object, napi_value prototype) { - napi_value global, global_object, set_proto; + napi_status status; + napi_value global = nullptr, global_object = nullptr, set_proto = nullptr; // Get the global object - napi_get_global(env, &global); + NAPI_GUARD(napi_get_global(env, &global)) { return; } // Get the Object global object - napi_get_named_property(env, global, OBJECT, &global_object); + NAPI_GUARD(napi_get_named_property(env, global, OBJECT, &global_object)) { return; } // Get the setPrototypeOf function from the Object global object - napi_get_named_property(env, global_object, SET_PROTOTYPE_OF, &set_proto); + NAPI_GUARD(napi_get_named_property(env, global_object, SET_PROTOTYPE_OF, &set_proto)) { return; } // Prepare the arguments for the setPrototypeOf call napi_value argv[] { @@ -194,192 +272,261 @@ namespace napi_util { prototype }; // Call setPrototypeOf(object, prototype) - napi_call_function(env, global, set_proto, 2, argv, nullptr); + NAPI_GUARD(napi_call_function(env, global, set_proto, 2, argv, nullptr)) {} } inline bool is_object_explicit(napi_env env, napi_value value) { - napi_valuetype type; - napi_typeof(env, value, &type); + napi_status status; + napi_valuetype type = napi_undefined; + NAPI_GUARD(napi_typeof(env, value, &type)) { + return false; + } return type == napi_object; } inline bool is_object(napi_env env, napi_value value) { - napi_valuetype type; - napi_typeof(env, value, &type); + napi_status status; + napi_valuetype type = napi_undefined; + NAPI_GUARD(napi_typeof(env, value, &type)) { + return false; + } return type == napi_object || type == napi_function; } inline bool is_of_type(napi_env env, napi_value value, napi_valuetype expected_type) { - napi_valuetype type; - napi_typeof(env, value, &type); + // napi_typeof can fail (and leave `type` unwritten) for values it cannot + // classify, e.g. an engine exception sentinel; guarding the status keeps + // this from returning a spurious match on an uninitialized value. + napi_status status; + napi_valuetype type = napi_undefined; + NAPI_GUARD(napi_typeof(env, value, &type)) { + return false; + } return type == expected_type; } inline bool is_number_object(napi_env env, napi_value value) { - bool result; - napi_value numberCtor; - napi_value global; - napi_get_global(env, &global); - napi_get_named_property(env, global, "Number", &numberCtor); - napi_instanceof(env, value, numberCtor, &result); + napi_status status; + bool result = false; + napi_value numberCtor = nullptr; + napi_value global = nullptr; + NAPI_GUARD(napi_get_global(env, &global)) { return false; } + NAPI_GUARD(napi_get_named_property(env, global, "Number", &numberCtor)) { return false; } + NAPI_GUARD(napi_instanceof(env, value, numberCtor, &result)) { return false; } return result; } inline napi_value valueOf(napi_env env, napi_value value) { - napi_value valueOf, result; - napi_get_named_property(env, value, "valueOf", &valueOf); - napi_call_function(env, value, valueOf, 0, nullptr, &result); + napi_status status; + napi_value valueOf = nullptr, result = nullptr; + NAPI_GUARD(napi_get_named_property(env, value, "valueOf", &valueOf)) { return nullptr; } + NAPI_GUARD(napi_call_function(env, value, valueOf, 0, nullptr, &result)) { return nullptr; } return result; } inline bool is_string_object(napi_env env, napi_value value) { - bool result; - napi_value stringCtor; - napi_value global; - napi_get_global(env, &global); - napi_get_named_property(env, global, "String", &stringCtor); - napi_instanceof(env, value, stringCtor, &result); + napi_status status; + bool result = false; + napi_value stringCtor = nullptr; + napi_value global = nullptr; + NAPI_GUARD(napi_get_global(env, &global)) { return false; } + NAPI_GUARD(napi_get_named_property(env, global, "String", &stringCtor)) { return false; } + NAPI_GUARD(napi_instanceof(env, value, stringCtor, &result)) { return false; } return result; } inline bool is_boolean_object(napi_env env, napi_value value) { - bool result; - napi_value booleanCtor; - napi_value global; - napi_get_global(env, &global); - napi_get_named_property(env, global, "Boolean", &booleanCtor); - napi_instanceof(env, value, booleanCtor, &result); + napi_status status; + bool result = false; + napi_value booleanCtor = nullptr; + napi_value global = nullptr; + NAPI_GUARD(napi_get_global(env, &global)) { return false; } + NAPI_GUARD(napi_get_named_property(env, global, "Boolean", &booleanCtor)) { return false; } + NAPI_GUARD(napi_instanceof(env, value, booleanCtor, &result)) { return false; } return result; } - + inline bool is_array(napi_env env, napi_value value) { - bool result; - napi_is_array(env, value, &result); + napi_status status; + bool result = false; + NAPI_GUARD(napi_is_array(env, value, &result)) { + return false; + } return result; } inline bool is_arraybuffer(napi_env env, napi_value value) { - bool result; - napi_is_arraybuffer(env, value, &result); + napi_status status; + bool result = false; + NAPI_GUARD(napi_is_arraybuffer(env, value, &result)) { + return false; + } return result; } inline bool is_dataview(napi_env env, napi_value value) { - bool result; - napi_is_dataview(env, value, &result); + napi_status status; + bool result = false; + NAPI_GUARD(napi_is_dataview(env, value, &result)) { + return false; + } return result; } inline bool is_typedarray(napi_env env, napi_value value) { - bool result; - napi_is_typedarray(env, value, &result); + napi_status status; + bool result = false; + NAPI_GUARD(napi_is_typedarray(env, value, &result)) { + return false; + } return result; } inline bool is_date(napi_env env, napi_value value) { - bool result; - napi_is_date(env, value, &result); + napi_status status; + bool result = false; + NAPI_GUARD(napi_is_date(env, value, &result)) { + return false; + } return result; } inline bool is_undefined(napi_env env, napi_value value) { if (value == nullptr) return true; - napi_valuetype type; - napi_typeof(env, value, &type); + napi_status status; + napi_valuetype type = napi_undefined; + // A value that cannot be classified is unusable; treat it as undefined so + // callers guarding with is_null_or_undefined skip it (matches the + // value == nullptr case above). + NAPI_GUARD(napi_typeof(env, value, &type)) { + return true; + } return type == napi_undefined; } inline bool is_null(napi_env env, napi_value value) { - napi_valuetype type; - napi_typeof(env, value, &type); + napi_status status; + napi_valuetype type = napi_undefined; + NAPI_GUARD(napi_typeof(env, value, &type)) { + return false; + } return type == napi_null; } inline napi_value get_true(napi_env env) { - napi_value trueValue; - napi_get_boolean(env, true, &trueValue); + napi_status status; + napi_value trueValue = nullptr; + NAPI_GUARD(napi_get_boolean(env, true, &trueValue)) { + return nullptr; + } return trueValue; } inline napi_value get_false(napi_env env) { - napi_value falseValue; - napi_get_boolean(env, false, &falseValue); + napi_status status; + napi_value falseValue = nullptr; + NAPI_GUARD(napi_get_boolean(env, false, &falseValue)) { + return nullptr; + } return falseValue; } inline bool get_bool(napi_env env, napi_value value) { - bool result; - napi_get_value_bool(env, value, &result); + napi_status status; + bool result = false; + + if (!napi_util::is_of_type(env, value, napi_boolean)) return false; + + NAPI_GUARD(napi_get_value_bool(env, value, &result)) { + return false; + } return result; } inline bool is_float(napi_env env, napi_value value) { - napi_value global, number, is_int, result; - napi_get_global(env, &global); - napi_get_named_property(env, global, "Number", &number); - napi_get_named_property(env, number, "isInteger", &is_int); - napi_call_function(env, number, is_int, 1, &value, &result); + napi_status status; + napi_value global = nullptr, number = nullptr, is_int = nullptr, result = nullptr; + NAPI_GUARD(napi_get_global(env, &global)) { return false; } + NAPI_GUARD(napi_get_named_property(env, global, "Number", &number)) { return false; } + NAPI_GUARD(napi_get_named_property(env, number, "isInteger", &is_int)) { return false; } + NAPI_GUARD(napi_call_function(env, number, is_int, 1, &value, &result)) { return false; } return !napi_util::get_bool(env, result); } // Same as Object.create()` inline napi_value object_create_from(napi_env env, napi_value object) { - napi_value new_object; - napi_create_object(env, &new_object); - napi_set_named_property(env, new_object, "prototype", object); + napi_status status; + napi_value new_object = nullptr; + NAPI_GUARD(napi_create_object(env, &new_object)) { return nullptr; } + NAPI_GUARD(napi_set_named_property(env, new_object, "prototype", object)) {} return new_object; } inline bool strict_equal(napi_env env, napi_value v1, napi_value v2) { - bool equal; - napi_strict_equals(env, v1, v2, &equal); + napi_status status; + bool equal = false; + NAPI_GUARD(napi_strict_equals(env, v1, v2, &equal)) { + return false; + } return equal; } inline double get_number(napi_env env, napi_value value) { - double result; - napi_get_value_double(env, value, &result); + napi_status status; + double result = 0; + NAPI_GUARD(napi_get_value_double(env, value, &result)) { + return 0; + } return result; } inline int32_t get_int32(napi_env env, napi_value value) { - int32_t result; - napi_get_value_int32(env, value, &result); + napi_status status; + int32_t result = 0; + NAPI_GUARD(napi_get_value_int32(env, value, &result)) { + return 0; + } return result; } template inline void run_in_handle_scope(napi_env env, Func func, Args &&...args) { - napi_handle_scope scope; - napi_open_handle_scope(env, &scope); + napi_status status; + napi_handle_scope scope = nullptr; + NAPI_GUARD(napi_open_handle_scope(env, &scope)) { + return; + } // Call the provided function func(std::forward(args)...); - napi_close_handle_scope(env, scope); + NAPI_GUARD(napi_close_handle_scope(env, scope)) {} } template inline napi_value run_in_escapable_handle_scope(napi_env env, Func func, Args &&...args) { - napi_escapable_handle_scope scope; - napi_value result, escaped = nullptr; + napi_status status; + napi_escapable_handle_scope scope = nullptr; + napi_value result = nullptr, escaped = nullptr; - napi_open_escapable_handle_scope(env, &scope); + NAPI_GUARD(napi_open_escapable_handle_scope(env, &scope)) { + return nullptr; + } // Call the provided function with forwarded arguments and get the result result = func(std::forward(args)...); if (result != nullptr) { // Escape the result - napi_escape_handle(env, scope, result, &escaped); + NAPI_GUARD(napi_escape_handle(env, scope, result, &escaped)) {} } - napi_close_escapable_handle_scope(env, scope); + NAPI_GUARD(napi_close_escapable_handle_scope(env, scope)) {} return escaped; } @@ -387,9 +534,12 @@ namespace napi_util { inline napi_value napi_set_function(napi_env env, napi_value object, const char *name, napi_callback callback, void *data = nullptr) { - napi_value fn; - napi_create_function(env, name, strlen(name), callback, data, &fn); - napi_set_named_property(env, object, name, fn); + napi_status status; + napi_value fn = nullptr; + NAPI_GUARD(napi_create_function(env, name, strlen(name), callback, data, &fn)) { + return nullptr; + } + NAPI_GUARD(napi_set_named_property(env, object, name, fn)) {} return fn; } @@ -404,51 +554,52 @@ namespace napi_util { } inline napi_value global(napi_env env) { - napi_value global; - napi_get_global(env, &global); + napi_status status; + napi_value global = nullptr; + NAPI_GUARD(napi_get_global(env, &global)) { + return nullptr; + } return global; } inline void log_value(napi_env env, napi_value value) { - napi_value global; - napi_value console; - napi_value log; - napi_get_global(env, &global); - napi_get_named_property(env, global, "console", &console); - napi_get_named_property(env, console, "log", &log); + napi_status status; + napi_value global = nullptr; + napi_value console = nullptr; + napi_value log = nullptr; + NAPI_GUARD(napi_get_global(env, &global)) { return; } + NAPI_GUARD(napi_get_named_property(env, global, "console", &console)) { return; } + NAPI_GUARD(napi_get_named_property(env, console, "log", &log)) { return; } napi_value argv[] = { value }; - napi_call_function(env, console, log, 1, argv, nullptr); + NAPI_GUARD(napi_call_function(env, console, log, 1, argv, nullptr)) {} } inline void napi_inherits(napi_env env, napi_value ctor, napi_value super_ctor) { - napi_value global, global_object, set_proto, ctor_proto_prop, - super_ctor_proto_prop; + napi_status status; + napi_value global = nullptr, global_object = nullptr, set_proto = nullptr, + ctor_proto_prop = nullptr, super_ctor_proto_prop = nullptr; napi_value argv[2]; - napi_get_global(env, &global); - napi_get_named_property(env, global, OBJECT, &global_object); - napi_get_named_property(env, global_object, SET_PROTOTYPE_OF, &set_proto); - napi_get_named_property(env, ctor, PROTOTYPE, &ctor_proto_prop); - napi_get_named_property(env, super_ctor, PROTOTYPE, &super_ctor_proto_prop); - - bool exception; - - napi_is_exception_pending(env, &exception); + NAPI_GUARD(napi_get_global(env, &global)) { return; } + NAPI_GUARD(napi_get_named_property(env, global, OBJECT, &global_object)) { return; } + NAPI_GUARD(napi_get_named_property(env, global_object, SET_PROTOTYPE_OF, &set_proto)) { return; } + NAPI_GUARD(napi_get_named_property(env, ctor, PROTOTYPE, &ctor_proto_prop)) { return; } + NAPI_GUARD(napi_get_named_property(env, super_ctor, PROTOTYPE, &super_ctor_proto_prop)) { return; } argv[0] = ctor_proto_prop; argv[1] = super_ctor_proto_prop; - napi_call_function(env, global, set_proto, 2, argv, nullptr); + NAPI_GUARD(napi_call_function(env, global, set_proto, 2, argv, nullptr)) { return; } argv[0] = ctor; argv[1] = super_ctor; - napi_call_function(env, global, set_proto, 2, argv, nullptr); + NAPI_GUARD(napi_call_function(env, global, set_proto, 2, argv, nullptr)) {} } } -#endif /* NATIVE_API_UTIL_H_ */ \ No newline at end of file +#endif /* NATIVE_API_UTIL_H_ */ diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/AsyncDebuggerAPI.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/AsyncDebuggerAPI.h index ea718dd4..7dcb79cb 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/AsyncDebuggerAPI.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/AsyncDebuggerAPI.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_ASYNCDEBUGGERAPI_H -#define HERMES_ASYNCDEBUGGERAPI_H +#pragma once #ifdef HERMES_ENABLE_DEBUGGER @@ -68,8 +67,6 @@ using DebuggerEventCallback = std::function; -using DebuggerEventCallbackID = uint32_t; -constexpr const uint32_t kInvalidDebuggerEventCallbackID = 0; using InterruptCallback = std::function; using EvalCompleteCallback = std::function< void(HermesRuntime &runtime, const debugger::EvalResult &result)>; @@ -82,36 +79,34 @@ using EvalCompleteCallback = std::function< /// functions that are safe to call on any thread. All other functions must be /// called on the runtime thread. class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { - /// Hide the constructor so users can only construct via static create - /// methods. - AsyncDebuggerAPI(HermesRuntime &runtime); - public: - /// Creates an AsyncDebuggerAPI for use with the provided HermesRuntime. This - /// should be called and created at the same time as creating HermesRuntime. - static std::unique_ptr create(HermesRuntime &runtime); + /// Constructs an AsyncDebuggerAPI for use with the provided HermesRuntime. + /// This should be called and created at the same time as creating + /// HermesRuntime. + explicit AsyncDebuggerAPI(HermesRuntime &runtime); /// Must be destroyed on the runtime thread or when you're sure nothing is /// interacting with the runtime. Must be destroyed before destroying /// HermesRuntime. ~AsyncDebuggerAPI() override; - /// Add a callback function to invoke when the runtime pauses due to various + /// Set a callback function to invoke when the runtime pauses due to various /// conditions such as hitting a "debugger;" statement. Can be called from any - /// thread. If there are no DebuggerEventCallback, then any reason that might + /// thread. If there is no DebuggerEventCallback, then any reason that might /// trigger a pause, such as a "debugger;" statement or breakpoints, will not - /// actually pause and will simply continue execution. Any caller that adds an + /// actually pause and will simply continue execution. Any caller that sets an /// event callback cannot just be observing events and never call /// \p resumeFromPaused in any of its code paths. The caller must either /// expose UI enabling human action for controlling the debugger, or it must /// have programmatic logic that controls the debugger via /// \p resumeFromPaused. - DebuggerEventCallbackID addDebuggerEventCallback_TS( - DebuggerEventCallback callback); + /// + /// The provided callback must be non-empty. Use \p + /// clearDebuggerEventCallback_TS to clear the callback. + void setDebuggerEventCallback_TS(DebuggerEventCallback callback); - /// Remove a previously added callback function. If there is no callback - /// registered using the provided \p id, the function does nothing. - void removeDebuggerEventCallback_TS(DebuggerEventCallbackID id); + /// Clear the debugger event callback. Can be called from any thread. + void clearDebuggerEventCallback_TS(); /// Whether the runtime is currently paused waiting for the next action. /// Should only be called from the runtime thread. @@ -120,7 +115,7 @@ class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { /// Whether the runtime is currently paused for any reason (e.g. script /// parsed, running interrupts, or waiting for a command). /// Should only be called from the runtime thread. - bool isPaused(); + bool isPaused() const; /// Provide the next action to perform. Should only be called from the runtime /// thread and only if the next command is expected to be set. @@ -145,11 +140,6 @@ class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { debugger::Command didPause(debugger::Debugger &debugger) override; private: - struct EventCallbackEntry { - DebuggerEventCallbackID id; - DebuggerEventCallback callback; - }; - /// This function infinite loops and uses \p signal_ to block the runtime /// thread. It gets woken up if new InterruptCallback is queued or if /// DebuggerEventCallback changes. @@ -163,11 +153,8 @@ class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { /// to run all interrupts, but will stop if any interrupt sets a next command. void runInterrupts(bool ignoreNextCommand = true); - /// Returns the next DebuggerEventCallback to execute if any. - std::optional takeNextEventCallback(); - - /// Runs every DebuggerEventCallback that has been registered. - void runEventCallbacks(DebuggerEventType event); + /// Runs the DebuggerEventCallback that has been registered (if any). + void runEventCallback(DebuggerEventType event); HermesRuntime &runtime_; @@ -186,19 +173,8 @@ class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { /// calls to didPause. bool inDidPause_ = false; - /// Next ID to use when adding a DebuggerEventCallback. - uint32_t nextEventCallbackID_ TSA_GUARDED_BY(mutex_); - - /// Callback functions to invoke to notify events in \p didPause. Using - /// std::list which requires O(N) search when removing an element, but removal - /// should be a rare event. So the choice of using std::list is to optimize - /// for typical usage. - std::list eventCallbacks_ TSA_GUARDED_BY(mutex_){}; - - /// Iterator for eventCallbacks_. Used to traverse through the list when - /// running the callbacks. - std::list::iterator eventCallbackIterator_ - TSA_GUARDED_BY(mutex_); + /// The debugger event callback to invoke when the runtime pauses. + DebuggerEventCallback eventCallback_ TSA_GUARDED_BY(mutex_){}; /// Queue of interrupt callback functions to invoke. std::queue interruptCallbacks_ TSA_GUARDED_BY(mutex_){}; @@ -257,32 +233,25 @@ using DebuggerEventCallback = std::function; -using DebuggerEventCallbackID = uint32_t; -constexpr const uint32_t kInvalidDebuggerEventCallbackID = 0; using InterruptCallback = std::function; using EvalCompleteCallback = std::function< void(HermesRuntime &runtime, const debugger::EvalResult &result)>; class HERMES_EXPORT AsyncDebuggerAPI { public: - static std::unique_ptr create(HermesRuntime &runtime) { - return nullptr; - } + explicit AsyncDebuggerAPI(HermesRuntime &) {} ~AsyncDebuggerAPI() {} - DebuggerEventCallbackID addDebuggerEventCallback_TS( - DebuggerEventCallback callback) { - return kInvalidDebuggerEventCallbackID; - } + void setDebuggerEventCallback_TS(DebuggerEventCallback callback) {} - void removeDebuggerEventCallback_TS(DebuggerEventCallbackID id) {} + void clearDebuggerEventCallback_TS() {} bool isWaitingForCommand() { return false; } - bool isPaused() { + bool isPaused() const { return false; } @@ -305,5 +274,3 @@ class HERMES_EXPORT AsyncDebuggerAPI { } // namespace facebook #endif // !HERMES_ENABLE_DEBUGGER - -#endif // HERMES_ASYNCDEBUGGERAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/CompileJS.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/CompileJS.h index 562eeae7..46c7451e 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/CompileJS.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/CompileJS.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_COMPILEJS_H -#define HERMES_COMPILEJS_H +#pragma once #include #include @@ -68,6 +67,31 @@ bool compileJS( std::string &bytecode, bool optimize = true); -} // namespace hermes +/// Options for overload of compileJS that accepts CompileJSOptions. +struct CompileJSOptions { + /// If true, the bytecode will be optimized. + bool optimize{true}; + /// Maximum number of instructions (in addition to parameter handling) + /// that is allowed for inlining of small functions. + unsigned inlineMaxSize{50}; + /// If true, the bytecode will be interruptable. + bool emitAsyncBreakCheck{false}; + /// If true, debugging information will be generated in the bytecode. + bool debug{false}; + /// Enable ES6 block scoping support. + bool enableES6BlockScoping{false}; + /// Enable async generators support. + bool enableAsyncGenerators{false}; +}; -#endif +/// Like the other compileJS overloads, but takes a struct of options with some +/// additional configurability. +bool compileJS( + const std::string &str, + const std::string &sourceURL, + std::string &bytecode, + const CompileJSOptions &options, + DiagnosticHandler *diagHandler, + std::optional sourceMapBuf = std::nullopt); + +} // namespace hermes diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/DebuggerAPI.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/DebuggerAPI.h index e444c41c..fe11b0cf 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/DebuggerAPI.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/DebuggerAPI.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_DEBUGGERAPI_H -#define HERMES_DEBUGGERAPI_H +#pragma once #ifdef HERMES_ENABLE_DEBUGGER @@ -31,6 +30,9 @@ class HermesValue; namespace facebook { namespace hermes { class HermesRuntime; +// Forward declaration of the internal Root API class, which is marked as a +// friend of the Debugger. +class HermesRootAPI; namespace debugger { @@ -269,6 +271,7 @@ class HERMES_EXPORT Debugger { ::facebook::jsi::Value getThrownValue(); private: + friend HermesRootAPI; friend std::unique_ptr hermes::makeHermesRuntime( const ::hermes::vm::RuntimeConfig &); friend std::unique_ptr @@ -497,5 +500,3 @@ class EventObserver { } // namespace facebook #endif // !HERMES_ENABLE_DEBUGGER - -#endif // HERMES_DEBUGGERAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/MurmurHash.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/MurmurHash.h deleted file mode 100644 index 3d2e53ee..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/MurmurHash.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#pragma once - -#include -#include - -// Computes the hash of key using MurmurHash3 algorithm, the value is planced in the "hash" output parameter -// The function returns whether or not key is comprised of only ASCII characters (<=127) -bool murmurhash(const uint8_t *key, size_t length, uint64_t &hash); \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/Buffer.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/Buffer.h deleted file mode 100644 index 3a4e8c26..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/Buffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_BUFFER_H -#define HERMES_PUBLIC_BUFFER_H - -#include - -#include -#include - -namespace hermes { - -/// A generic buffer interface. E.g. for memmapped bytecode. -class HERMES_EXPORT Buffer { - public: - Buffer() : data_(nullptr), size_(0) {} - - Buffer(const uint8_t *data, size_t size) : data_(data), size_(size) {} - - virtual ~Buffer(); - - const uint8_t *data() const { - return data_; - }; - - size_t size() const { - return size_; - } - - protected: - const uint8_t *data_ = nullptr; - size_t size_ = 0; -}; - -} // namespace hermes - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/CtorConfig.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/CtorConfig.h index aff3f398..ba213ee0 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/CtorConfig.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/CtorConfig.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_CTORCONFIG_H -#define HERMES_PUBLIC_CTORCONFIG_H +#pragma once #include @@ -128,11 +127,11 @@ return TypeAsSingleToken{__VA_ARGS__}; \ } -#define _HERMES_CTORCONFIG_SETTER(CX, TYPE, NAME, ...) \ - inline auto with##NAME(TYPE NAME)->decltype(*this) { \ - config_.NAME##_ = std::move(NAME); \ - NAME##Explicit_ = true; \ - return *this; \ +#define _HERMES_CTORCONFIG_SETTER(CX, TYPE, NAME, ...) \ + inline auto with##NAME(TYPE NAME) -> decltype(*this) { \ + config_.NAME##_ = std::move(NAME); \ + NAME##Explicit_ = true; \ + return *this; \ } #define _HERMES_CTORCONFIG_BUILDER_GETTER(CX, TYPE, NAME, ...) \ @@ -144,5 +143,3 @@ if (newConfig.has##NAME()) { \ with##NAME(newConfig.config_.get##NAME()); \ } - -#endif // HERMES_PUBLIC_CTORCONFIG_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/DebuggerTypes.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/DebuggerTypes.h index 88184c07..a549e81d 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/DebuggerTypes.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/DebuggerTypes.h @@ -5,17 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_DEBUGGERTYPES_H -#define HERMES_PUBLIC_DEBUGGERTYPES_H +#pragma once #include #include #include -#pragma GCC diagnostic push -#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32 -#pragma GCC diagnostic ignored "-Wshorten-64-to-32" -#endif namespace hermes { namespace vm { class Debugger; @@ -88,7 +83,7 @@ struct StackTrace { private: explicit StackTrace(std::vector frames) - : frames_(std::move(frames)){}; + : frames_(std::move(frames)) {}; friend ProgramState; friend ::hermes::vm::Debugger; std::vector frames_; @@ -196,5 +191,3 @@ struct BreakpointInfo { } // namespace debugger } // namespace hermes } // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCConfig.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCConfig.h index 8d3f316f..419305af 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCConfig.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCConfig.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_GCCONFIG_H -#define HERMES_PUBLIC_GCCONFIG_H +#pragma once #include "hermes/Public/CtorConfig.h" #include "hermes/Public/GCTripwireContext.h" @@ -25,10 +24,9 @@ namespace hermes { namespace vm { /// A type big enough to accomodate the entire allocated address space. -/// Individual allocations are always 'uint32_t', but on a 64-bit machine we -/// might want to accommodate a larger total heap (or not, in which case we keep -/// it 32-bit). -using gcheapsize_t = uint32_t; +/// Individual allocations are always 'uint32_t', but on a 64-bit machine, when +/// compressed pointer is OFF, we want to accommodate a larger total heap. +using gcheapsize_t = size_t; /// Represents a value before and after an event. /// NOTE: Not a std::pair because using the names are more readable than first @@ -144,9 +142,6 @@ enum class GCEventKind { /// Parameters for GC Initialisation. Check documentation in README.md /// constexpr indicates that the default value is constexpr. #define GC_FIELDS(F) \ - /* Minimum heap size hint. */ \ - F(constexpr, gcheapsize_t, MinHeapSize, 0) \ - \ /* Initial heap size hint. */ \ F(constexpr, gcheapsize_t, InitHeapSize, 32 << 20) \ \ @@ -206,19 +201,6 @@ enum class GCEventKind { /* GC_FIELDS END */ _HERMES_CTORCONFIG_STRUCT(GCConfig, GC_FIELDS, { - if (builder.hasMinHeapSize()) { - if (builder.hasInitHeapSize()) { - // If both are specified, normalize the initial size up to the minimum, - // if necessary. - InitHeapSize_ = std::max(MinHeapSize_, InitHeapSize_); - } else { - // If the minimum is set explicitly, but the initial heap size is not, - // use the minimum as the initial size. - InitHeapSize_ = MinHeapSize_; - } - } - assert(InitHeapSize_ >= MinHeapSize_); - // Make sure the max is at least the Init. MaxHeapSize_ = std::max(InitHeapSize_, MaxHeapSize_); }) @@ -227,5 +209,3 @@ _HERMES_CTORCONFIG_STRUCT(GCConfig, GC_FIELDS, { } // namespace vm } // namespace hermes - -#endif // HERMES_PUBLIC_GCCONFIG_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCTripwireContext.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCTripwireContext.h index 4a8f500f..e7a614fc 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCTripwireContext.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/GCTripwireContext.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_GCTRIPWIRECONTEXT_H -#define HERMES_PUBLIC_GCTRIPWIRECONTEXT_H +#pragma once #include @@ -39,5 +38,3 @@ class HERMES_EXPORT GCTripwireContext { } // namespace vm } // namespace hermes - -#endif // HERMES_PUBLIC_GCTRIPWIRECONTEXT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/JSOutOfMemoryError.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/JSOutOfMemoryError.h index 95093ab7..38f18a10 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/JSOutOfMemoryError.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/JSOutOfMemoryError.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_JSOUTOFMEMORYERROR_H -#define HERMES_PUBLIC_JSOUTOFMEMORYERROR_H +#pragma once #include @@ -26,5 +25,3 @@ class HERMES_EXPORT JSOutOfMemoryError : public std::runtime_error { } // namespace vm } // namespace hermes - -#endif // HERMES_PUBLIC_JSOUTOFMEMORYERROR_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/RuntimeConfig.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/RuntimeConfig.h index 858f1f50..faa1b7e1 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/RuntimeConfig.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/RuntimeConfig.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_RUNTIMECONFIG_H -#define HERMES_PUBLIC_RUNTIMECONFIG_H +#pragma once #include "hermes/Public/CrashManager.h" #include "hermes/Public/CtorConfig.h" @@ -48,6 +47,9 @@ class PinnedHermesValue; /* Native stack remaining before assuming overflow */ \ F(constexpr, unsigned, NativeStackGap, 64 * 1024) \ \ + /* Whether or not the JIT is enabled */ \ + F(constexpr, bool, EnableJIT, false) \ + \ /* Whether to allow eval and Function ctor */ \ F(constexpr, bool, EnableEval, true) \ \ @@ -60,23 +62,20 @@ class PinnedHermesValue; /* Whether to emit async break check instructions in eval code */ \ F(constexpr, bool, AsyncBreakCheckInEval, true) \ \ - /* Support for ES6 Promise. */ \ - F(constexpr, bool, ES6Promise, true) \ - \ /* Support for ES6 Proxy. */ \ F(constexpr, bool, ES6Proxy, true) \ \ - /* Support for ES6 Class. */ \ - F(constexpr, bool, ES6Class, false) \ + /* Support for ES6 block scoping. */ \ + F(constexpr, bool, ES6BlockScoping, false) \ + \ + /* Support for async generators in eval. */ \ + F(constexpr, bool, EnableAsyncGenerators, false) \ \ /* Support for ECMA-402 Intl APIs. */ \ F(constexpr, bool, Intl, true) \ \ - /* Support for ArrayBuffer, DataView and typed arrays. */ \ - F(constexpr, bool, ArrayBuffer, true) \ - \ /* Support for using microtasks. */ \ - F(constexpr, bool, MicrotaskQueue, false) \ + F(constexpr, bool, MicrotaskQueue, true) \ \ /* Runtime set up for synth trace. */ \ F(constexpr, SynthTraceMode, SynthTraceMode, SynthTraceMode::None) \ @@ -121,8 +120,17 @@ class PinnedHermesValue; /* The flags passed from a VM experiment */ \ F(constexpr, uint32_t, VMExperimentFlags, 0) \ \ - /* Whether or not block scoping is enabled */ \ - F(constexpr, bool, EnableBlockScoping, false) \ + /* Force JIT compilation on all functions. */ \ + F(constexpr, bool, ForceJIT, false) \ + \ + /* JIT compilation threshold (number of calls before JIT'ing). */ \ + F(constexpr, uint32_t, JITThreshold, 1 << 5) \ + \ + /* JIT memory limit, after which no more code will be JIT'ed. */ \ + F(constexpr, uint32_t, JITMemoryLimit, 32u << 20) \ + \ + /* Increase compliance with test262 (stricter checks at runtime). */ \ + F(constexpr, bool, Test262, false) \ /* RUNTIME_FIELDS END */ _HERMES_CTORCONFIG_STRUCT(RuntimeConfig, RUNTIME_FIELDS, {}) @@ -131,5 +139,3 @@ _HERMES_CTORCONFIG_STRUCT(RuntimeConfig, RUNTIME_FIELDS, {}) } // namespace vm } // namespace hermes - -#endif // HERMES_PUBLIC_RUNTIMECONFIG_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/SamplingProfiler.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/SamplingProfiler.h similarity index 98% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/SamplingProfiler.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/SamplingProfiler.h index 0d8583ed..5c2a3a71 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/SamplingProfiler.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/Public/SamplingProfiler.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_PUBLIC_SAMPLINGPROFILER_H -#define HERMES_PUBLIC_SAMPLINGPROFILER_H +#pragma once #include @@ -269,5 +268,3 @@ class HERMES_EXPORT Profile { } // namespace sampling_profiler } // namespace hermes } // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/RuntimeTaskRunner.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/RuntimeTaskRunner.h index 367b267a..dd1cd98a 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/RuntimeTaskRunner.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/RuntimeTaskRunner.h @@ -5,10 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_RUNTIMETASKRUNNER_H -#define HERMES_RUNTIMETASKRUNNER_H +#pragma once -#include "AsyncDebuggerAPI.h" +#include namespace facebook { namespace hermes { @@ -63,5 +62,3 @@ class RuntimeTaskRunner } // namespace debugger } // namespace hermes } // namespace facebook - -#endif // HERMES_RUNTIMETASKRUNNER_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ScriptStore.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ScriptStore.h deleted file mode 100644 index e7365cc5..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ScriptStore.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#pragma once - -#include -#include - -namespace facebook { -namespace jsi { - -// Integer type as it's persist friendly. -using ScriptVersion_t = uint64_t; // It should be std::optional once we have c++17 available everywhere. Until - // then, 0 implies versioning not available. -using JSRuntimeVersion_t = uint64_t; // 0 implies version can't be computed. We assert whenever that happens. - -struct VersionedBuffer { - std::shared_ptr buffer; - ScriptVersion_t version; -}; - -struct ScriptSignature { - std::string url; - ScriptVersion_t version; -}; - -struct JSRuntimeSignature { - std::string runtimeName; // e.g. Chakra, V8 - JSRuntimeVersion_t version; -}; - -// Most JSI::Runtime implementation offer some form of prepared JavaScript which offers better performance -// characteristics when loading comparing to plain JavaScript. Embedders can provide an instance of this interface -// (through JSI::Runtime implementation's factory method), to enable persistance of the prepared script and retrieval on -// subsequent evaluation of a script. -struct PreparedScriptStore { - virtual ~PreparedScriptStore() = default; - - // Try to retrieve the prepared javascript for a given combination of script & runtime. - // scriptSignature : Javascript url and version - // RuntimeSignature : Javascript engine type and version - // prepareTag : Custom tag to uniquely identify JS engine specific preparation schemes. It is usually useful while - // experimentation and can be null. It is possible that no prepared script is available for a given script & runtime - // signature. This method should null if so - virtual std::shared_ptr tryGetPreparedScript( - const ScriptSignature &scriptSignature, - const JSRuntimeSignature &runtimeSignature, - const char *prepareTag // Optional tag. For e.g. eagerly evaluated vs lazy cache. - ) noexcept = 0; - - // Persist the prepared javascript for a given combination of script & runtime. - // scriptSignature : Javascript url and version - // RuntimeSignature : Javascript engine type and version - // prepareTag : Custom tag to uniquely identify JS engine specific preparation schemes. It is usually useful while - // experimentation and can be null. It is possible that no prepared script is available for a given script & runtime - // signature. This method should null if so Any failure in persistance should be identified during the subsequent - // retrieval through the integrity mechanism which must be put into the storage. - virtual void persistPreparedScript( - std::shared_ptr preparedScript, - const ScriptSignature &scriptMetadata, - const JSRuntimeSignature &runtimeMetadata, - const char *prepareTag // Optional tag. For e.g. eagerly evaluated vs lazy cache. - ) noexcept = 0; -}; - -// JSI::Runtime implementation must be provided an instance on this interface to enable version sensitive capabilities -// such as usage of pre-prepared javascript script. Alternatively, this entity can be used to directly provide the -// Javascript buffer and rich metadata to the JSI::Runtime instance. -struct ScriptStore { - virtual ~ScriptStore() = default; - - // Return the Javascript buffer and version corresponding to a given url. - virtual VersionedBuffer getVersionedScript(const std::string &url) noexcept = 0; - - // Return the version of the Javascript buffer corresponding to a given url. - virtual ScriptVersion_t getScriptVersion(const std::string &url) noexcept = 0; -}; - -} // namespace jsi -} // namespace facebook \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTrace.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTrace.h index f8d174c8..857445a9 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTrace.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTrace.h @@ -5,15 +5,16 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_SYNTHTRACE_H -#define HERMES_SYNTHTRACE_H +#pragma once +#include "hermes/ADT/StringSetVector.h" #include "hermes/Public/RuntimeConfig.h" #include "hermes/Support/JSONEmitter.h" #include "hermes/Support/SHA1.h" -#include "hermes/Support/StringSetVector.h" #include "hermes/VM/GCExecTrace.h" +#include "jsi/jsi.h" + #include #include #include @@ -171,43 +172,71 @@ class SynthTrace { } val_; }; + /// Represents the encoding type of a String or PropNameId + enum class StringEncodingType { ASCII, UTF8, UTF16 }; + + /// Represents the type of JavaScript Error being created. + enum class JSErrorType { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError, + }; + /// A TimePoint is a time when some event occurred. using TimePoint = std::chrono::steady_clock::time_point; using TimeSinceStart = std::chrono::milliseconds; -#define SYNTH_TRACE_RECORD_TYPES(RECORD) \ - RECORD(BeginExecJS) \ - RECORD(EndExecJS) \ - RECORD(Marker) \ - RECORD(CreateObject) \ - RECORD(CreateString) \ - RECORD(CreatePropNameID) \ - RECORD(CreateHostObject) \ - RECORD(CreateHostFunction) \ - RECORD(QueueMicrotask) \ - RECORD(DrainMicrotasks) \ - RECORD(GetProperty) \ - RECORD(SetProperty) \ - RECORD(HasProperty) \ - RECORD(GetPropertyNames) \ - RECORD(CreateArray) \ - RECORD(ArrayRead) \ - RECORD(ArrayWrite) \ - RECORD(CallFromNative) \ - RECORD(ConstructFromNative) \ - RECORD(ReturnFromNative) \ - RECORD(ReturnToNative) \ - RECORD(CallToNative) \ - RECORD(GetPropertyNative) \ - RECORD(GetPropertyNativeReturn) \ - RECORD(SetPropertyNative) \ - RECORD(SetPropertyNativeReturn) \ - RECORD(GetNativePropertyNames) \ - RECORD(GetNativePropertyNamesReturn) \ - RECORD(CreateBigInt) \ - RECORD(BigIntToString) \ - RECORD(SetExternalMemoryPressure) \ - RECORD(Utf8) \ +#define SYNTH_TRACE_RECORD_TYPES(RECORD) \ + RECORD(BeginExecJS) \ + RECORD(EndExecJS) \ + RECORD(Marker) \ + RECORD(CreateObject) \ + RECORD(CreateObjectWithPrototype) \ + RECORD(CreateString) \ + RECORD(CreatePropNameID) \ + RECORD(CreatePropNameIDWithValue) \ + RECORD(CreateHostObject) \ + RECORD(CreateHostFunction) \ + RECORD(QueueMicrotask) \ + RECORD(DrainMicrotasks) \ + RECORD(GetProperty) \ + RECORD(SetProperty) \ + RECORD(HasProperty) \ + RECORD(GetPropertyNames) \ + RECORD(CreateArray) \ + RECORD(ArrayRead) \ + RECORD(ArrayWrite) \ + RECORD(ArrayPush) \ + RECORD(CallFromNative) \ + RECORD(ConstructFromNative) \ + RECORD(ReturnFromNative) \ + RECORD(ReturnToNative) \ + RECORD(CallToNative) \ + RECORD(GetPropertyNative) \ + RECORD(GetPropertyNativeReturn) \ + RECORD(SetPropertyNative) \ + RECORD(SetPropertyNativeReturn) \ + RECORD(GetNativePropertyNames) \ + RECORD(GetNativePropertyNamesReturn) \ + RECORD(CreateBigInt) \ + RECORD(BigIntToString) \ + RECORD(SetExternalMemoryPressure) \ + RECORD(Utf8) \ + RECORD(Utf16) \ + RECORD(GetStringData) \ + RECORD(GetPrototype) \ + RECORD(SetPrototype) \ + RECORD(DeleteProperty) \ + RECORD(Serialize) \ + RECORD(Deserialize) \ + RECORD(CreateUInt8Array) \ + RECORD(CreateUInt8ArrayFromArrayBuffer) \ + RECORD(GetBufferFromTypedArray) \ + RECORD(CreateJSError) \ RECORD(Global) /// RecordType is a tag used to differentiate which type of record it is. @@ -279,8 +308,12 @@ class SynthTrace { template void emplace_back(Args &&...args) { - records_.emplace_back(new T(std::forward(args)...)); - flushRecordsIfNecessary(); + if (json_) { + T record(std::forward(args)...); + record.toJSON(*json_); + } else { + records_.emplace_back(new T(std::forward(args)...)); + } } const std::vector> &records() const { @@ -315,9 +348,17 @@ class SynthTrace { /// Decodes a string into a trace value. static TraceValue decode(const std::string &); +#ifdef HERMESVM_API_TRACE_DEBUG + /// Given a Value, return a descriptive string. This should only be used to + /// provide more debugging info when creating records. + static std::string getDescriptiveString( + jsi::Runtime &runtime, + const jsi::Value &value); +#endif + /// The version of the Synth Benchmark constexpr static uint32_t synthVersion() { - return 4; + return 5; } static const char *nameFromReleaseUnused(::hermes::vm::ReleaseUnused ru); @@ -328,27 +369,14 @@ class SynthTrace { return (*traceStream_); } - /// If we're tracing to a file, and the number of accumulated - /// records has reached the limit kTraceRecordsToFlush, below, - /// flush the records to the file, and reset the accumulated records - /// to be empty. - void flushRecordsIfNecessary(); - - /// Assumes we're tracing to a file; flush accumulated records to - /// the file, and reset the accumulated records to be empty. - void flushRecords(); - - static constexpr unsigned kTraceRecordsToFlush = 100; - /// If we're tracing to a file, pointer to a stream onto /// traceFilename_. Null otherwise. std::unique_ptr traceStream_; /// If we're tracing to a file, pointer to a JSONEmitter writting /// into *traceStream_. Null otherwise. std::unique_ptr<::hermes::JSONEmitter> json_; - /// The records currently being accumulated in the trace. If we are - /// tracing to a file, these will be only the records not yet - /// written to the file. + /// The records accumulated in the trace. Only used when not tracing + /// to a file (i.e., when json_ is null). std::vector> records_; /// The id of the global object. /// Note: Keeping this as optional to support replaying the older trace @@ -557,8 +585,10 @@ class SynthTrace { /// The string that was passed to Runtime::createStringFromAscii() or /// Runtime::createStringFromUtf8() when the string was created. std::string chars_; - /// Whether the string was created from ASCII (true) or UTF8 (false). - bool ascii_; + /// The string that was passed to Runtime::createStringFromUtf16() + std::u16string chars16_; + /// Whether the String was created from ASCII, UTF-8 or UTF-16 + StringEncodingType encodingType_; // General UTF-8. CreateStringRecord( @@ -569,14 +599,27 @@ class SynthTrace { : Record(time), objID_(objID), chars_(reinterpret_cast(chars), length), - ascii_(false) {} + encodingType_(StringEncodingType::UTF8) {} // Ascii. CreateStringRecord( TimeSinceStart time, ObjectID objID, const char *chars, size_t length) - : Record(time), objID_(objID), chars_(chars, length), ascii_(true) {} + : Record(time), + objID_(objID), + chars_(chars, length), + encodingType_(StringEncodingType::ASCII) {} + // UTF-16. + CreateStringRecord( + TimeSinceStart time, + ObjectID objID, + const char16_t *chars, + size_t length) + : Record(time), + objID_(objID), + chars16_(chars, length), + encodingType_(StringEncodingType::UTF16) {} void toJSONInternal(::hermes::JSONEmitter &json) const override; RecordType getType() const override { @@ -596,19 +639,15 @@ class SynthTrace { /// created by the native code. struct CreatePropNameIDRecord : public Record { static constexpr RecordType type{RecordType::CreatePropNameID}; - /// The ObjectID of the PropNameID that was created by - /// Runtime::createPropNameIDFromXxx() functions. + /// The ObjectID of the PropNameID that was created. const ObjectID propNameID_; /// The string that was passed to Runtime::createPropNameIDFromAscii() or /// Runtime::createPropNameIDFromUtf8(). std::string chars_; - /// The String for Symbol that was passed to - /// Runtime::createPropNameIDFromString() or - /// Runtime::createPropNameIDFromSymbol(). - const TraceValue traceValue_{TraceValue::encodeUndefinedValue()}; - /// Whether the PropNameID was created from ASCII, UTF8, jsi::String - /// (TRACEVALUE) or jsi::Symbol (TRACEVALUE). - enum ValueType { ASCII, UTF8, TRACEVALUE } valueType_; + /// The string that was passed to Runtime::createPropNameIDFromUtf16() + std::u16string chars16_; + /// Whether the PropNameID was created from ASCII, UTF-8, or UTF-16 + StringEncodingType encodingType_; // General UTF-8. CreatePropNameIDRecord( @@ -619,7 +658,7 @@ class SynthTrace { : Record(time), propNameID_(propNameID), chars_(reinterpret_cast(chars), length), - valueType_(UTF8) {} + encodingType_(StringEncodingType::UTF8) {} // Ascii. CreatePropNameIDRecord( TimeSinceStart time, @@ -629,16 +668,49 @@ class SynthTrace { : Record(time), propNameID_(propNameID), chars_(chars, length), - valueType_(ASCII) {} - // jsi::String or jsi::Symbol. + encodingType_(StringEncodingType::ASCII) {} + // UTF16 CreatePropNameIDRecord( TimeSinceStart time, ObjectID propNameID, - TraceValue traceValue) + const char16_t *chars, + size_t length) : Record(time), propNameID_(propNameID), - traceValue_(traceValue), - valueType_(TRACEVALUE) {} + chars16_(chars, length), + encodingType_(StringEncodingType::UTF16) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + RecordType getType() const override { + return type; + } + + std::vector defs() const override { + return {propNameID_}; + } + + std::vector uses() const override { + return {}; + } + }; + + /// A CreatePropNameIDWithValueRecord is an event where a jsi::PropNameID is + /// created by the native code from JSI Value + struct CreatePropNameIDWithValueRecord : public Record { + static constexpr RecordType type{RecordType::CreatePropNameIDWithValue}; + /// The ObjectID of the PropNameID that was created. + const ObjectID propNameID_; + /// The String or Symbol that was passed to + /// Runtime::createPropNameIDFromString() or + /// Runtime::createPropNameIDFromSymbol(). + const TraceValue traceValue_; + + // jsi::String or jsi::Symbol. + CreatePropNameIDWithValueRecord( + TimeSinceStart time, + ObjectID propNameID, + TraceValue traceValue) + : Record(time), propNameID_(propNameID), traceValue_(traceValue) {} void toJSONInternal(::hermes::JSONEmitter &json) const override; RecordType getType() const override { @@ -656,6 +728,31 @@ class SynthTrace { } }; + struct CreateObjectWithPrototypeRecord : public Record { + static constexpr RecordType type{RecordType::CreateObjectWithPrototype}; + const ObjectID objID_; + /// The prototype being assigned + const TraceValue prototype_; + + CreateObjectWithPrototypeRecord( + TimeSinceStart time, + ObjectID objID, + TraceValue prototype) + : Record(time), objID_(objID), prototype_(prototype) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + std::vector uses{objID_}; + pushIfTrackedValue(prototype_, uses); + return uses; + } + }; + struct CreateHostObjectRecord final : public CreateObjectRecord { static constexpr RecordType type{RecordType::CreateHostObject}; using CreateObjectRecord::CreateObjectRecord; @@ -741,7 +838,7 @@ class SynthTrace { struct GetPropertyRecord : public Record { /// The ObjectID of the object that was accessed for its property. const ObjectID objID_; - /// String or PropNameID passed to getProperty. + /// String or PropNameID or Value passed to getProperty. const TraceValue propID_; #ifdef HERMESVM_API_TRACE_DEBUG std::string propNameDbg_; @@ -785,7 +882,7 @@ class SynthTrace { struct SetPropertyRecord : public Record { /// The ObjectID of the object that was accessed for its property. const ObjectID objID_; - /// String or PropNameID passed to setProperty. + /// String or PropNameID or Value passed to setProperty. const TraceValue propID_; #ifdef HERMESVM_API_TRACE_DEBUG std::string propNameDbg_; @@ -883,6 +980,71 @@ class SynthTrace { } }; + /// A SetPrototypeRecord is an event where native code sets the prototype of a + /// JS Object + struct SetPrototypeRecord : public Record { + static constexpr RecordType type{RecordType::SetPrototype}; + /// The ObjectID of the object that was accessed for its prototype. + const ObjectID objID_; + /// The custom prototype being assigned + const TraceValue value_; + SetPrototypeRecord(TimeSinceStart time, ObjectID objID, TraceValue value) + : Record(time), objID_(objID), value_(value) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + + RecordType getType() const override { + return type; + } + std::vector uses() const override { + std::vector uses{objID_}; + pushIfTrackedValue(value_, uses); + return uses; + } + }; + + struct DeletePropertyRecord final : public Record { + static constexpr RecordType type{RecordType::DeleteProperty}; + /// The object ID of the object that was accessed for its property + const ObjectID objID_; + /// The name of the property being deleted + const TraceValue propID_; + + DeletePropertyRecord(TimeSinceStart time, ObjectID objID, TraceValue propID) + : Record(time), objID_(objID), propID_(propID) {} + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + std::vector uses{objID_}; + pushIfTrackedValue(propID_, uses); + return uses; + } + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + }; + + /// A GetPrototypeRecord is an event where native code gets the prototype of a + /// JS Object + struct GetPrototypeRecord : public Record { + static constexpr RecordType type{RecordType::GetPrototype}; + /// The ObjectID of the object that was accessed for its prototype. + const ObjectID objID_; + GetPrototypeRecord(TimeSinceStart time, ObjectID objID) + : Record(time), objID_(objID) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + + RecordType getType() const override { + return type; + } + std::vector uses() const override { + return {objID_}; + } + }; + /// A CreateArrayRecord is an event where a new array is created of a specific /// length. struct CreateArrayRecord final : public Record { @@ -907,6 +1069,128 @@ class SynthTrace { } }; + /// A CreateUInt8ArrayRecord is an event where a new UInt8Array is created + /// with a specific length. + struct CreateUInt8ArrayRecord final : public Record { + static constexpr RecordType type{RecordType::CreateUInt8Array}; + /// The ObjectID of the UInt8Array that was created by createUint8Array(). + const ObjectID objID_; + /// The length of the UInt8Array that was passed to createUint8Array(). + const size_t length_; + + explicit CreateUInt8ArrayRecord( + TimeSinceStart time, + ObjectID objID, + size_t length) + : Record(time), objID_(objID), length_(length) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + RecordType getType() const override { + return type; + } + std::vector defs() const override { + return {objID_}; + } + }; + + /// A CreateUInt8ArrayFromArrayBufferRecord is an event where a new UInt8Array + /// is created from an existing ArrayBuffer with offset and length. + struct CreateUInt8ArrayFromArrayBufferRecord final : public Record { + static constexpr RecordType type{ + RecordType::CreateUInt8ArrayFromArrayBuffer}; + /// The ObjectID of the UInt8Array that was created by createUint8Array(). + const ObjectID objID_; + /// The ObjectID of the ArrayBuffer used to create the UInt8Array. + const ObjectID bufferID_; + /// The byte offset into the ArrayBuffer. + const size_t offset_; + /// The length of the UInt8Array view. + const size_t length_; + + explicit CreateUInt8ArrayFromArrayBufferRecord( + TimeSinceStart time, + ObjectID objID, + ObjectID bufferID, + size_t offset, + size_t length) + : Record(time), + objID_(objID), + bufferID_(bufferID), + offset_(offset), + length_(length) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + RecordType getType() const override { + return type; + } + std::vector defs() const override { + return {objID_}; + } + std::vector uses() const override { + return {bufferID_}; + } + }; + + /// A GetBufferFromTypedArrayRecord is an event where the underlying + /// ArrayBuffer of a TypedArray is retrieved. + struct GetBufferFromTypedArrayRecord final : public Record { + static constexpr RecordType type{RecordType::GetBufferFromTypedArray}; + /// The ObjectID of the ArrayBuffer returned by buffer(). + const ObjectID bufferID_; + /// The ObjectID of the TypedArray whose buffer was queried. + const ObjectID typedArrayID_; + + explicit GetBufferFromTypedArrayRecord( + TimeSinceStart time, + ObjectID bufferID, + ObjectID typedArrayID) + : Record(time), bufferID_(bufferID), typedArrayID_(typedArrayID) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + RecordType getType() const override { + return type; + } + std::vector defs() const override { + return {bufferID_}; + } + std::vector uses() const override { + return {typedArrayID_}; + } + }; + + /// A CreateJSErrorRecord is an event where a JavaScript Error object is + /// created with a specific type and message. + struct CreateJSErrorRecord final : public Record { + static constexpr RecordType type{RecordType::CreateJSError}; + /// The ObjectID of the error Value that was created. + const ObjectID objID_; + /// The type of error being created. + const JSErrorType errorType_; + /// The ObjectID of the message String passed to create the error. + const ObjectID messageID_; + + explicit CreateJSErrorRecord( + TimeSinceStart time, + ObjectID objID, + JSErrorType errorType, + ObjectID messageID) + : Record(time), + objID_(objID), + errorType_(errorType), + messageID_(messageID) {} + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + RecordType getType() const override { + return type; + } + std::vector defs() const override { + return {objID_}; + } + std::vector uses() const override { + return {messageID_}; + } + }; + /// An ArrayReadRecord is an event where a value was read from an index /// of an array. /// It is modeled separately from GetProperty because it is more efficient to @@ -959,6 +1243,37 @@ class SynthTrace { void toJSONInternal(::hermes::JSONEmitter &json) const override; }; + struct ArrayPushRecord final : public Record { + static constexpr RecordType type{RecordType::ArrayPush}; + /// The ObjectID of the array + const ObjectID objID_; + /// The elements being pushed to the array + const std::vector elements_; + /// The returned length from calling Array.push + size_t length_; + + explicit ArrayPushRecord( + TimeSinceStart time, + ObjectID objID, + const std::vector &elements, + size_t length) + : Record(time), objID_(objID), elements_(elements), length_(length) {} + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + std::vector uses{objID_}; + for (const auto &val : elements_) { + pushIfTrackedValue(val, uses); + } + return uses; + } + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + }; + struct CallRecord : public Record { /// The ObjectID of the function JS object that was called from /// JS or native. @@ -1285,6 +1600,112 @@ class SynthTrace { void toJSONInternal(::hermes::JSONEmitter &json) const override; }; + /// A Utf16Record is an event where a PropNameID or String was converted to + /// UTF-16. + struct Utf16Record final : public Record { + static constexpr RecordType type{RecordType::Utf16}; + /// PropNameID, String passed to utf16() as an argument + const TraceValue objID_; + /// Returned string from utf16(). + const std::u16string retVal_; + + explicit Utf16Record( + TimeSinceStart time, + const TraceValue objID, + std::u16string retval) + : Record(time), objID_(objID), retVal_(std::move(retval)) {} + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + std::vector vec; + pushIfTrackedValue(objID_, vec); + return vec; + } + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + }; + + /// A GetStringData is an event where getStringData or getPropNameIdData was + /// invoked. + struct GetStringDataRecord final : public Record { + static constexpr RecordType type{RecordType::GetStringData}; + /// The String or PropNameID passed into getStringData or getPropNameIdData + const TraceValue objID_; + /// The string content in the String or PropNameID that was passed into the + /// callback + const std::u16string strData_; + + explicit GetStringDataRecord( + TimeSinceStart time, + const TraceValue objID, + std::u16string strData) + : Record(time), objID_(objID), strData_(std::move(strData)) {} + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + std::vector vec; + pushIfTrackedValue(objID_, vec); + return vec; + } + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + }; + + struct SerializeRecord final : public Record { + static constexpr RecordType type{RecordType::Serialize}; + /// The jsi::Value being serialized + const TraceValue value_; + + explicit SerializeRecord(TimeSinceStart time, TraceValue value) + : Record(time), value_(value) {} + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + std::vector uses; + pushIfTrackedValue(value_, uses); + return uses; + } + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + }; + + struct DeserializeRecord final : public Record { + static constexpr RecordType type{RecordType::Deserialize}; + /// This mirrors the structure of vm::SerializedValue + std::vector offsets_; + std::vector content_; + std::vector strings_; + + explicit DeserializeRecord( + TimeSinceStart time, + const std::vector &offsets, + const std::vector &content, + const std::vector &strings) + : Record(time), + offsets_(offsets), + content_(content), + strings_(strings) {} + + RecordType getType() const override { + return type; + } + + std::vector uses() const override { + return {}; + } + + void toJSONInternal(::hermes::JSONEmitter &json) const override; + }; + struct GlobalRecord final : public Record { static constexpr RecordType type{RecordType::Global}; const ObjectID objID_; // global's ObjectID returned from Runtime::global(). @@ -1312,5 +1733,3 @@ class SynthTrace { } // namespace tracing } // namespace hermes } // namespace facebook - -#endif // HERMES_SYNTHTRACE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTraceParser.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTraceParser.h index 7844ee50..eee8a94a 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTraceParser.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/SynthTraceParser.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_SYNTHTRACEPARSER_H -#define HERMES_SYNTHTRACEPARSER_H +#pragma once #include @@ -36,5 +35,3 @@ parseSynthTrace(const std::string &tracefile); } // namespace tracing } // namespace hermes } // namespace facebook - -#endif // HERMES_SYNTHTRACEPARSER_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ThreadSafetyAnalysis.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ThreadSafetyAnalysis.h index 39e6cf66..f69df8ca 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ThreadSafetyAnalysis.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/ThreadSafetyAnalysis.h @@ -7,8 +7,7 @@ // Based on mutex.h from https://clang.llvm.org/docs/ThreadSafetyAnalysis.html -#ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H -#define THREAD_SAFETY_ANALYSIS_MUTEX_H +#pragma once // Enable thread safety attributes only with clang. // The attributes can be safely erased when compiling with other compilers. @@ -74,5 +73,3 @@ #define TSA_NO_THREAD_SAFETY_ANALYSIS \ TSA_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#endif // THREAD_SAFETY_ANALYSIS_MUTEX_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TraceInterpreter.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TraceInterpreter.h index 0a1240c1..bcef27ce 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TraceInterpreter.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TraceInterpreter.h @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include #include @@ -46,6 +48,13 @@ class TraceInterpreter final { /// the trace. If false, start from the default config. bool useTraceConfig{false}; + /// Enable basic block profiling. + bool basicBlockProfiling{false}; + + // If non-empty, write profiling output to this file, rather than + // to stderr. + std::string profilingOutFile; + /// Number of initial executions whose stats are discarded. int warmupReps{0}; @@ -53,10 +62,20 @@ class TraceInterpreter final { /// with the median totalTime. int reps{1}; + /// If non-null, holds statistics for every garbage collection that occurs. + const std::vector<::hermes::vm::GCAnalyticsEvent> *gcAnalyticsEvents{ + nullptr}; + /// If true, run a complete collection before printing stats. Useful for /// guaranteeing there's no garbage in heap size numbers. bool forceGCBeforeStats{false}; + /// If true, use the Hermes VM JIT during execution. + bool enableJIT{false}; + + /// If true, force JIT compilation on all functions. + bool forceJIT{false}; + /// If true, remove the requirement that the input bytecode was compiled /// from the same source used to record the trace. There must only be one /// input bytecode file in this case. If its observable behavior deviates @@ -108,6 +127,8 @@ class TraceInterpreter final { llvh::raw_ostream *traceStream_; // Map from source hash to source file to run. std::map<::hermes::SHA1, std::shared_ptr> bundles_; + // Map from source hash to shermes unit creator function. + std::map<::hermes::SHA1, SHUnitCreator> shermesUnitCreatorFns_; const SynthTrace &trace_; /// The last use of each object. @@ -151,18 +172,34 @@ class TraceInterpreter final { const std::string &traceFile, const std::vector &bytecodeFiles, const ExecuteOptions &options, - const std::function( + const std::function( const ::hermes::vm::RuntimeConfig &runtimeConfig)> &createRuntime); +#ifndef _WIN32 + /// Execute the trace given by \p traceFile, that was the trace of executing + /// the bundle from which \p shermesUnitLibFiles are generated. + /// \param shermesUnitCreatorFns A map from source hash to the shermes unit + /// creator function (i.e., sh_export_), which is defined in the + /// shermes generated C file. For each BeginExecJSRecord, the shermes unit + /// with matching source hash will be evaluated. + /// \return The stats collected by the runtime about times and memory usage. + static std::string execNativeWithRuntime( + const std::string &traceFile, + const std::map<::hermes::SHA1, SHUnitCreator> &shermesUnitCreatorFns, + const ExecuteOptions &options, + const std::function( + const ::hermes::vm::RuntimeConfig &runtimeConfig)> &createRuntime); +#endif + /// \param traceStream If non-null, write a trace of the execution into this /// stream. /// \return Tuple of GC stats and the runtime instance used for replaying. - static std::tuple> + static std::tuple> execFromMemoryBuffer( std::unique_ptr &&traceBuf, std::vector> &&codeBufs, const ExecuteOptions &options, - const std::function( + const std::function( const ::hermes::vm::RuntimeConfig &runtimeConfig)> &createRuntime); private: @@ -172,6 +209,12 @@ class TraceInterpreter final { const SynthTrace &trace, std::map<::hermes::SHA1, std::shared_ptr> bundles); + TraceInterpreter( + jsi::Runtime &rt, + const ExecuteOptions &options, + const SynthTrace &trace, + std::map<::hermes::SHA1, SHUnitCreator> shermesUnitCreatorFns); + static std::string exec( jsi::Runtime &rt, const ExecuteOptions &options, @@ -212,6 +255,13 @@ class TraceInterpreter final { /// HostObject's functions are called. void executeRecords(); + /// Execute the record. When \p bundles_ is not empty, the bundle file + /// (source or bytecode) with the matching source hash will be evaluated. + /// Otherwise, the shermes unit from \p shermesUnitCreatorFns_ with matching + /// source hash will be evaluated. + jsi::Value executeBeginExecJSRecord( + const SynthTrace::BeginExecJSRecord &bejsr); + /// Requires that \p valID is the proper id for \p val, and that a /// defining occurrence of \p valID occurs at the current \p defIndex. Decides /// whether the definition should be recorded, and, if so, adds the diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TracingRuntime.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TracingRuntime.h index f3d082d5..bc338c9d 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TracingRuntime.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/TracingRuntime.h @@ -5,11 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_TRACINGRUNTIME_H -#define HERMES_TRACINGRUNTIME_H - -#include "SynthTrace.h" +#pragma once +#include #include #include #include "llvh/Support/raw_ostream.h" @@ -18,12 +16,21 @@ namespace facebook { namespace hermes { namespace tracing { -class TracingRuntime : public jsi::RuntimeDecorator { +/// Prepare traceable builtins by installing Math wrappers into both properties +/// and static builtin slots. +void installTraceableBuiltinWrappers(jsi::Runtime &runtime); + +class TracingRuntime : public jsi::RuntimeDecorator +#ifdef JSI_UNSTABLE + , + public jsi::ISerialization +#endif +{ public: using RD = RuntimeDecorator; TracingRuntime( - std::unique_ptr runtime, + std::shared_ptr runtime, const ::hermes::vm::RuntimeConfig &conf, std::unique_ptr traceStream); @@ -37,6 +44,7 @@ class TracingRuntime : public jsi::RuntimeDecorator { /// @name jsi::Runtime methods. /// @{ + jsi::ICast *castInterface(const jsi::UUID &interfaceUUID) override; jsi::Value evaluateJavaScript( const std::shared_ptr &buffer, const std::string &sourceURL) override; @@ -47,6 +55,7 @@ class TracingRuntime : public jsi::RuntimeDecorator { jsi::Object global() override; jsi::Object createObject() override; + jsi::Object createObjectWithPrototype(const jsi::Value &prototype) override; jsi::Object createObject(std::shared_ptr ho) override; // Note that the NativeState methods do not need to be traced since they @@ -58,14 +67,32 @@ class TracingRuntime : public jsi::RuntimeDecorator { jsi::String createStringFromAscii(const char *str, size_t length) override; jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; + jsi::String createStringFromUtf16(const char16_t *utf16, size_t length) + override; std::string utf8(const jsi::PropNameID &) override; jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override; jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override; + jsi::PropNameID createPropNameIDFromUtf16( + const char16_t *utf16, + size_t length) override; std::string utf8(const jsi::String &) override; + std::u16string utf16(const jsi::PropNameID &) override; + std::u16string utf16(const jsi::String &) override; + + void getStringData( + const jsi::String &str, + void *ctx, + void (*cb)(void *ctx, bool ascii, const void *data, size_t num)) override; + + void getPropNameIdData( + const jsi::PropNameID &sym, + void *ctx, + void (*cb)(void *ctx, bool ascii, const void *data, size_t num)) override; + std::string symbolToString(const jsi::Symbol &) override; jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override; @@ -75,10 +102,13 @@ class TracingRuntime : public jsi::RuntimeDecorator { override; jsi::Value getProperty(const jsi::Object &obj, const jsi::PropNameID &name) override; + jsi::Value getProperty(const jsi::Object &obj, const jsi::Value &name) + override; bool hasProperty(const jsi::Object &obj, const jsi::String &name) override; bool hasProperty(const jsi::Object &obj, const jsi::PropNameID &name) override; + bool hasProperty(const jsi::Object &obj, const jsi::Value &name) override; void setPropertyValue( const jsi::Object &obj, @@ -88,6 +118,19 @@ class TracingRuntime : public jsi::RuntimeDecorator { const jsi::Object &obj, const jsi::PropNameID &name, const jsi::Value &value) override; + void setPropertyValue( + const jsi::Object &obj, + const jsi::Value &name, + const jsi::Value &value) override; + + void deleteProperty(const jsi::Object &obj, const jsi::PropNameID &name) + override; + void deleteProperty(const jsi::Object &obj, const jsi::String &name) override; + void deleteProperty(const jsi::Object &, const jsi::Value &name) override; + + void setPrototypeOf(const jsi::Object &object, const jsi::Value &prototype) + override; + jsi::Value getPrototypeOf(const jsi::Object &object) override; jsi::Array getPropertyNames(const jsi::Object &o) override; @@ -99,6 +142,22 @@ class TracingRuntime : public jsi::RuntimeDecorator { jsi::ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override; + jsi::Uint8Array createUint8Array(size_t length) override; + jsi::Uint8Array createUint8Array( + const jsi::ArrayBuffer &buffer, + size_t offset, + size_t length) override; + + jsi::ArrayBuffer buffer(const jsi::TypedArray &typedArray) override; + + jsi::Value createError(const jsi::String &msg) override; + jsi::Value createEvalError(const jsi::String &msg) override; + jsi::Value createRangeError(const jsi::String &msg) override; + jsi::Value createReferenceError(const jsi::String &msg) override; + jsi::Value createSyntaxError(const jsi::String &msg) override; + jsi::Value createTypeError(const jsi::String &msg) override; + jsi::Value createURIError(const jsi::String &msg) override; + size_t size(const jsi::Array &arr) override; size_t size(const jsi::ArrayBuffer &buf) override; @@ -111,6 +170,9 @@ class TracingRuntime : public jsi::RuntimeDecorator { size_t i, const jsi::Value &value) override; + size_t push(const jsi::Array &arr, const jsi::Value *elements, size_t count) + override; + jsi::Function createFunctionFromHostFunction( const jsi::PropNameID &name, unsigned int paramCount, @@ -130,8 +192,21 @@ class TracingRuntime : public jsi::RuntimeDecorator { void setExternalMemoryPressure(const jsi::Object &obj, size_t amount) override; + std::shared_ptr tryGetMutableBuffer( + const jsi::ArrayBuffer &buffer) override; /// @} +#ifdef JSI_UNSTABLE + std::shared_ptr serialize(const jsi::Value &value) override; + jsi::Value deserialize( + const std::shared_ptr &serialized) override; + std::unique_ptr serializeWithTransfer( + const jsi::Value &value, + const jsi::Array &transferList) override; + jsi::Array deserializeWithTransfer( + std::unique_ptr &serialized) override; +#endif + void addMarker(const std::string &marker); SynthTrace &trace() { @@ -172,7 +247,7 @@ class TracingRuntime : public jsi::RuntimeDecorator { SynthTrace::TimeSinceStart getTimeSinceStart() const; - std::unique_ptr runtime_; + std::shared_ptr runtime_; SynthTrace trace_; std::deque savedFunctions; const SynthTrace::TimePoint startTime_{std::chrono::steady_clock::now()}; @@ -209,7 +284,7 @@ class TracingHermesRuntime final : public TracingRuntime { /// \p rollbackAction is invoked if the runtime is destructed prior to /// completion of tracing. It may or may not invoked if completion failed. TracingHermesRuntime( - std::unique_ptr runtime, + std::shared_ptr runtime, const ::hermes::vm::RuntimeConfig &runtimeConfig, std::unique_ptr traceStream, std::function commitAction, @@ -257,7 +332,7 @@ class TracingHermesRuntime final : public TracingRuntime { /// The return value of \p traceCompletionCallback indicates whether the /// invocation completed successfully. std::unique_ptr makeTracingHermesRuntime( - std::unique_ptr hermesRuntime, + std::shared_ptr hermesRuntime, const ::hermes::vm::RuntimeConfig &runtimeConfig, const std::string &traceScratchPath, const std::string &traceResultPath, @@ -268,7 +343,7 @@ std::unique_ptr makeTracingHermesRuntime( /// The \p forReplay parameter indicates whether the runtime is being used /// in trace replay. (Its behavior can differ slightly in that case.) std::unique_ptr makeTracingHermesRuntime( - std::unique_ptr hermesRuntime, + std::shared_ptr hermesRuntime, const ::hermes::vm::RuntimeConfig &runtimeConfig, std::unique_ptr traceStream, bool forReplay = false); @@ -276,5 +351,3 @@ std::unique_ptr makeTracingHermesRuntime( } // namespace tracing } // namespace hermes } // namespace facebook - -#endif // HERMES_TRACINGRUNTIME_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPAgent.h index e2243259..8169f152 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPAgent.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPAgent.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_CDPAGENT_H -#define HERMES_CDP_CDPAGENT_H +#pragma once #include #include @@ -29,7 +28,8 @@ class CDPDebugAPI; /// Public-facing wrapper for internal CDP state that can be preserved across /// reloads. -struct HERMES_EXPORT State { +class HERMES_EXPORT State { + public: /// Incomplete type that stores the actual state. struct Private; @@ -103,8 +103,9 @@ class HERMES_EXPORT CDPAgent { /// tasks enqueued during destruction. ~CDPAgent(); - /// Process a CDP command encoded in \p json. This can be called from - /// arbitrary threads. + /// This function can be called from arbitrary threads. It processes a CDP + /// command encoded in \p json as UTF-8 in accordance with RFC-8259. See: + // https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/public/devtools_protocol/#wire-format_strings-and-binary-values void handleCommand(std::string json); /// Enable the Runtime domain without processing a CDP command or sending a @@ -128,5 +129,3 @@ class HERMES_EXPORT CDPAgent { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_CDPAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPDebugAPI.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPDebugAPI.h index 9809ec9a..9c8b256b 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPDebugAPI.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CDPDebugAPI.h @@ -5,12 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_CDPDEBUGAPI_H -#define HERMES_CDP_CDPDEBUGAPI_H +#pragma once #include - -#include "ConsoleMessage.h" +#include +#include namespace facebook { namespace hermes { @@ -37,7 +36,12 @@ class HERMES_EXPORT CDPDebugAPI { /// Gets the AsyncDebuggerAPI associated with this instance. debugger::AsyncDebuggerAPI &asyncDebuggerAPI() { - return *asyncDebuggerAPI_; + return asyncDebuggerAPI_; + } + + /// Gets the DebuggerDomainCoordinator associated with this instance. + DebuggerDomainCoordinator &debuggerDomainCoordinator() { + return debuggerDomainCoordinator_; } /// Adds a console message to the current CDPDebugAPI instance, @@ -53,14 +57,17 @@ class HERMES_EXPORT CDPDebugAPI { CDPDebugAPI(HermesRuntime &runtime, size_t maxCachedMessages); - HermesRuntime &runtime_; - std::unique_ptr asyncDebuggerAPI_; + /// Member order matters for destruction: asyncDebuggerAPI_ must be destroyed + /// first, as its destructor flushes the queue of pending tasks, which may + /// reference other objects (like consoleMessageDispatcher_ and + /// debuggerDomainCoordinator_). ConsoleMessageStorage consoleMessageStorage_; ConsoleMessageDispatcher consoleMessageDispatcher_; + HermesRuntime &runtime_; + DebuggerDomainCoordinator debuggerDomainCoordinator_; + debugger::AsyncDebuggerAPI asyncDebuggerAPI_; }; } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_CDPDEBUGAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CallbackOStream.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CallbackOStream.h index b8a4eb3b..bdf7914f 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CallbackOStream.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/CallbackOStream.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_CALLBACKOSTREAM_H -#define HERMES_CDP_CALLBACKOSTREAM_H +#pragma once #include #include @@ -21,7 +20,8 @@ namespace cdp { /// Subclass of \c std::ostream where flushing is implemented through a /// callback. Writes are collected in a buffer. When filled, the buffer's /// contents are emptied out and sent to a callback. -struct CallbackOStream : public std::ostream { +class CallbackOStream : public std::ostream { + public: /// Signature of callback called to flush buffer contents. Accepts the buffer /// as a string. Returns a boolean indicating whether flushing succeeded. /// Callback failure will be translated to stream failure. If the callback @@ -45,7 +45,8 @@ struct CallbackOStream : public std::ostream { private: /// \c std::streambuf sub-class backed by a std::string buffer and /// implementing overflow by calling a callback. - struct StreamBuf : public std::streambuf { + class StreamBuf : public std::streambuf { + public: /// Construct a new streambuf. Parameters are the same as those of /// \c CallbackOStream . StreamBuf(size_t sz, Fn cb); @@ -86,5 +87,3 @@ struct CallbackOStream : public std::ostream { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_CALLBACKOSTREAM_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ConsoleMessage.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ConsoleMessage.h index 906dbb9a..88577592 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ConsoleMessage.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ConsoleMessage.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_CDPCONSOLEMESSAGESTORAGE_H -#define HERMES_CDP_CDPCONSOLEMESSAGESTORAGE_H +#pragma once #include #include @@ -134,5 +133,3 @@ class ConsoleMessageDispatcher { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_CDPCONSOLEMESSAGESTORAGE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainAgent.h index b1336e6b..5e83856d 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainAgent.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainAgent.h @@ -5,25 +5,21 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_DEBUGGERDOMAINAGENT_H -#define HERMES_CDP_DEBUGGERDOMAINAGENT_H +#pragma once -#include #include #include +#include +#include +#include #include #include -#include "DomainAgent.h" -#include "DomainState.h" - namespace facebook { namespace hermes { namespace cdp { -enum class PausedNotificationReason; - namespace m = ::facebook::hermes::cdp::message; /// Details about a single Hermes breakpoint, implied by a CDP breakpoint. @@ -89,12 +85,15 @@ class DebuggerDomainAgent : public DomainAgent { DebuggerDomainAgent( int32_t executionContextID, HermesRuntime &runtime, - debugger::AsyncDebuggerAPI &asyncDebugger, + debugger::AsyncDebuggerAPI &asyncDebuggerAPI, + DebuggerDomainCoordinator &debuggerDomainAPI, SynchronizedOutboundCallback messageCallback, std::shared_ptr objTable_, DomainState &state); ~DebuggerDomainAgent(); + // ------ CDP API (used by CDPAgent) ------ + /// Enables the Debugger domain without processing CDP message or sending a /// CDP response. It will still send CDP notifications if needed. void enable(); @@ -119,7 +118,8 @@ class DebuggerDomainAgent : public DomainAgent { /// Handles Debugger.setBlackboxedRanges request void setBlackboxedRanges(const m::debugger::SetBlackboxedRangesRequest &req); - + /// Handles Debugger.setBlackboxPatterns request + void setBlackboxPatterns(const m::debugger::SetBlackboxPatternsRequest &req); /// Handles Debugger.setPauseOnExceptions void setPauseOnExceptions( const m::debugger::SetPauseOnExceptionsRequest &req); @@ -127,12 +127,28 @@ class DebuggerDomainAgent : public DomainAgent { /// Handles Debugger.evaluateOnCallFrame void evaluateOnCallFrame(const m::debugger::EvaluateOnCallFrameRequest &req); - /// Debugger.setBreakpoint creates a CDP breakpoint that applies to exactly - /// one script (identified by script ID) that does not survive reloads. + /// @cdp Debugger.setBreakpoint creates a CDP breakpoint that applies to + /// exactly one script (identified by script ID) that does not survive + /// reloads. A `condition` equal to an empty string is treated the same as an + /// omitted condition (= unconditional breakpoint). + /// Hermes allows multiple breakpoints to be set at the same location, + /// even in the same CDP session. For comparison, V8 allows it from different + /// sessions but disallows it within a single session. void setBreakpoint(const m::debugger::SetBreakpointRequest &req); - // Debugger.setBreakpointByUrl creates a CDP breakpoint that may apply to - // multiple scripts (identified by URL), and survives reloads. + /// @cdp Debugger.setBreakpointByUrl creates a CDP breakpoint that may apply + /// to multiple scripts (identified by URL), and survives reloads. A + /// `condition` equal to an empty string is treated the same as an omitted + /// condition (= unconditional breakpoint). + /// Hermes allows multiple breakpoints to be set at the same location, + /// even in the same CDP session. For comparison, V8 allows it from different + /// sessions but disallows it within a single session. void setBreakpointByUrl(const m::debugger::SetBreakpointByUrlRequest &req); + /// @cdp Debugger.getPossibleBreakpoints returns the valid breakpoint + /// locations within a source range. The `start` and (if provided) `end` + /// locations must refer to the same script. `restrictToFunction` is not yet + /// supported and is ignored. + void getPossibleBreakpoints( + const m::debugger::GetPossibleBreakpointsRequest &req); /// Handles Debugger.removeBreakpoint void removeBreakpoint(const m::debugger::RemoveBreakpointRequest &req); /// Handles Debugger.setBreakpointsActive @@ -140,75 +156,131 @@ class DebuggerDomainAgent : public DomainAgent { void setBreakpointsActive( const m::debugger::SetBreakpointsActiveRequest &req); - private: - /// Handle an event originating from the runtime. - void handleDebuggerEvent( - HermesRuntime &runtime, - debugger::AsyncDebuggerAPI &asyncDebugger, - debugger::DebuggerEventType event); + // ------ Coordinator API (used by DebuggerDomainCoordinator) ------ + + void processScript(const debugger::SourceLocation &srcLoc); + bool locationHasManualBreakpoint( + const debugger::SourceLocation &srcLoc) const; + + /// Checks whether the passed location falls within a blackboxed range + /// in blackboxedRanges_. + /// Chrome looks at full functions ("frames") to determine this. See: + /// https://source.chromium.org/chromium/chromium/src/+/318e9cfd9fbbbc70906f6a78d017a2708248dc6d:v8/src/inspector/v8-debugger-agent-impl.cc;l=984-1026 + /// We, on the other hand, look at individual lines since there's no + /// difference in practise because the current way functions are blackboxed is + /// by using ignoreList in source maps, which blackboxes full files, which + /// means also it blackboxes full functions, so there's no difference between + /// checking if a line in a function is blackboxed or if the whole function is + /// blackboxed. + /// This means that we receive one "Debugger.setBlackboxedRanges" per bundle + /// file comprised of source js files. + /// For each file appearing in the "ignoreList" in source maps, we receive the + /// start positions and end positions of the file inside the bundle file: + /// [ file 1 start position, + /// file 1 end position, + /// file 2 start position, + /// file 2 end position, + /// ... ] + bool isLocationBlackboxed(const debugger::SourceLocation &loc) const; + + void notifyPaused(PausedNotificationReason reason); + void notifyUnpaused(); + private: /// Send a Debugger.paused notification to the debug client void sendPausedNotificationToClient(PausedNotificationReason reason); /// Send a Debugger.scriptParsed notification to the debug client void sendScriptParsedNotificationToClient( const debugger::SourceLocation srcLoc); - /// Obtain the newly loaded script and send a ScriptParsed notification to the - /// debug client - void processNewLoadedScript(); - std::pair createCDPBreakpoint( CDPBreakpointDescription &&description, std::optional hermesBreakpoint = std::nullopt); - std::optional createHermesBreakpont( + std::optional createHermesBreakpoint( debugger::ScriptID scriptID, const CDPBreakpointDescription &description); + void applyBreakpointAndSendNotification( + CDPBreakpointID cdpBreakpointID, + CDPBreakpoint &cdpBreakpoint, + const debugger::SourceLocation &srcLoc); + std::optional applyBreakpoint( - CDPBreakpoint &breakpoint, + CDPBreakpoint &cdpBreakpoint, debugger::ScriptID scriptID); + /// Holds a boolean that determines if scripts without a script url + /// (e.g. anonymous scripts) should be blackboxed. + /// Same as V8: + /// https://source.chromium.org/chromium/chromium/src/+/fef5d519bab86dbd712d76bfca5be90a6e03459c:v8/src/inspector/v8-debugger-agent-impl.cc;l=997-999 + bool blackboxAnonymousScripts_ = false; + /// Optionally, holds a compiled regex pattern that is used to test if + /// script urls should be blackboxed. + /// See isLocationBlackboxed below for more details. Same as V8: + /// https://source.chromium.org/chromium/chromium/src/+/fef5d519bab86dbd712d76bfca5be90a6e03459c:v8/src/inspector/v8-debugger-agent-impl.cc;l=993-996 + /// Matching using the compiled regex should be done with + /// ::hermes::regex::searchWithBytecode. + std::optional> compiledBlackboxPatternRegex_; + + /// A vector of 1-based positions per script id indicating where blackbox + /// state changes using [from inclusive, to exclusive) pairs. + /// [ (start) ... position[0]) range is not blackboxed + /// [position[0] ... position[1]) range is blackboxed + /// [position[1] ... position[2]) range is not blackboxed ... ... + /// [position[n] ... (end) ) range is blackboxed if n is even, not + /// blackboxed if odd. + /// This is used to determine if the debugger is paused on one of these + /// blackboxed ranges, to prevent the user from stopping there in the + /// following scenarios: + /// 1. Step out- repeats stepping out until reaches a non-blackboxed range. + /// 2. Step over- stepping over to a blackboxed range meaning that + /// the next un-blackboxed range would be after all the stepping in the + /// function are done (because blackboxing is per file, meaning per function + /// as well) so we can execute step out as well in this case until we + /// step out of blackboxed ranges. + /// Comparing with v8, we don’t check if the user comes from a blackboxed + /// range, but only if a stepover got you to a blackboxed range. However + /// both results in the same thing which is stepping out until reaching a + /// non-blackboxed range. + /// 3. Step into- execute another step into. + /// Repeat this step until outside of a blackboxed range. + /// 4. Exceptions triggering the debugger pause- + /// (uncaught or if the user chooses to stop on all exceptions)- + /// ignore and continue execution + /// 5. Debugger statements- ignore and continue execution + /// 6. Explicit pause- keep stepping in until reaching a non-blackboxed range + /// 7. Manual breakpoints- allow stopping in blackboxed ranges + std::unordered_map>> + blackboxedRanges_; + bool checkDebuggerEnabled(const m::Request &req); bool checkDebuggerPaused(const m::Request &req); /// Removes any modifications this agent made to Hermes in order to enable /// debugging - void cleanUp(); + void disable(); HermesRuntime &runtime_; debugger::AsyncDebuggerAPI &asyncDebugger_; - - /// ID for the registered DebuggerEventCallback - debugger::DebuggerEventCallbackID debuggerEventCallbackId_; + DebuggerDomainCoordinator &debuggerDomainCoordinator_; /// Details of each CDP breakpoint that has been created, and not /// yet destroyed. std::unordered_map cdpBreakpoints_{}; /// CDP breakpoint IDs are assigned by the DebuggerDomainAgent. Keep track of - /// the next available ID. - CDPBreakpointID nextBreakpointID_ = 1; + /// the next available ID. Starts with 100 to avoid confusion with Hermes + /// breakpoints IDs that start with 1. + CDPBreakpointID nextBreakpointID_ = 100; DomainState &state_; - /// Whether the currently installed breakpoints actually take effect. If - /// they're supposed to be inactive, then debugger agent will automatically - /// resume execution when breakpoints are hit. - bool breakpointsActive_ = true; - /// Whether Debugger.enable was received and wasn't disabled by receiving /// Debugger.disable bool enabled_; - - /// Whether to consider the debugger as currently paused. There are some - /// debugger events such as ScriptLoaded where we don't consider the debugger - /// to be paused. - bool paused_; }; } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_DEBUGGERDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainCoordinator.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainCoordinator.h new file mode 100644 index 00000000..6e73b1f6 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DebuggerDomainCoordinator.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include + +namespace facebook { +namespace hermes { +namespace cdp { + +enum class PausedNotificationReason { kException, kOther, kStep }; + +/// Last explicit debugger step command issued by the user. +enum class UserStepRequest { + StepInto, + StepOver, + StepOut, +}; + +class DebuggerDomainAgent; + +/// Coordinates multiple DebuggerDomainAgent instances connecting to the same +/// Hermes Runtime. This class manages shared state, ensures cooperative access, +/// and eliminates race conditions when multiple agents connect concurrently. +/// +/// All methods must be called on the runtime thread unless otherwise noted. +class DebuggerDomainCoordinator { + public: + /// Constructs a DebuggerDomainCoordinator for use with the provided + /// HermesRuntime. + explicit DebuggerDomainCoordinator(HermesRuntime &runtime); + ~DebuggerDomainCoordinator(); + + // Rule of Five: Non-copyable, non-movable (stable references required) + DebuggerDomainCoordinator(const DebuggerDomainCoordinator &) = delete; + DebuggerDomainCoordinator &operator=(const DebuggerDomainCoordinator &) = + delete; + DebuggerDomainCoordinator(DebuggerDomainCoordinator &&) = delete; + DebuggerDomainCoordinator &operator=(DebuggerDomainCoordinator &&) = delete; + + //===--------------------------------------------------------------------===// + // Agent management + //===--------------------------------------------------------------------===// + + /// Registers a new agent with the coordinator. We will call the agent's + /// methods to notify it of debugger events and query state set by the + /// debugging client (like blackboxed ranges). The agent MUST call + /// disableAgent() before being destroyed. + void enableAgent( + debugger::AsyncDebuggerAPI &asyncDebugger, + DebuggerDomainAgent &agent); + /// Unregisters an agent from the coordinator. + void disableAgent( + debugger::AsyncDebuggerAPI &asyncDebugger, + DebuggerDomainAgent &agent); + + //===--------------------------------------------------------------------===// + // Debugger control + //===--------------------------------------------------------------------===// + + /// Used to handle Debugger.pause requests + void pause(); + /// Used to handle Debugger.stepInto, Debugger.stepOver, + /// and debugger.stepOut requests + void stepFromPaused( + debugger::AsyncDebuggerAPI &asyncDebugger, + UserStepRequest stepRequest); + /// Globally enables or disables pausing at breakpoints. + void setBreakpointsActive(bool active); + /// Returns whether the debugger is currently paused. + bool isPaused(debugger::AsyncDebuggerAPI &asyncDebugger) const; + /// Used to handle Debugger.resume requests + void resume(debugger::AsyncDebuggerAPI &asyncDebugger); + + private: + /// Handle an event originating from the runtime. + void handleDebuggerEvent( + HermesRuntime &runtime, + debugger::AsyncDebuggerAPI &asyncDebugger, + debugger::DebuggerEventType event); + + /// Called when the runtime is paused. + void setPaused(PausedNotificationReason pausedNotificationReason); + + /// Called when the runtime is resumed. + void setUnpaused(); + + /// Obtain the newly loaded script and send a ScriptParsed notification to the + /// enabled agents + void processNewLoadedScript(); + + /// Checks whether the location of the top frame of the call stack is + /// blackboxed in ALL of the enabled agents (and has no manual breakpoints). + bool isTopFrameLocationBlackboxed(); + + HermesRuntime &runtime_; + + /// The set of agents that are currently enabled. + std::vector enabledAgents_; + + /// Whether the currently installed breakpoints actually take effect. If + /// they're supposed to be inactive, then we will automatically + /// resume execution when breakpoints are hit. + bool breakpointsActive_; + + /// Whether to consider the debugger as currently paused. There are some + /// debugger events such as ScriptLoaded where we don't consider the debugger + /// to be paused. + bool paused_; + + /// Set to true when the user selects to explicitly pause execution. + /// This is set back to false when the execution is paused. + bool explicitPausePending_ = false; + + /// Last explicit step type issued by the user. + /// * This is "sticky" - we can't tell if a step command was + /// completed since a step command that does not result in further operations + /// resolves to a "resume" without "stepFinished" or debugger pause. + /// That means that this member should only be used in situations where we are + /// sure that a step command was issued in the given scenario, i.e. a + /// StepFinish event. For example, a step into command followed by a resume + /// would leave this member holding an "StepInto" even when minutes later the + /// execution stops on a breakpoint. + std::optional lastUserStepRequest_ = std::nullopt; +}; + +} // namespace cdp +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainAgent.h index 6770e829..ae74e5ee 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainAgent.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainAgent.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_DOMAINAGENT_H -#define HERMES_CDP_DOMAINAGENT_H +#pragma once #include #include @@ -106,5 +105,3 @@ class DomainAgent { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_DOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainState.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainState.h index 4c21603c..6c169084 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainState.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/DomainState.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_DOMAINSTATE_H -#define HERMES_CDP_DOMAINSTATE_H +#pragma once #include #include @@ -32,11 +31,18 @@ namespace cdp { /// Base class for data to be stored in DomainState. struct StateValue { - public: virtual ~StateValue() = default; virtual std::unique_ptr copy() const = 0; }; +/// StateValue that can be used as a boolean flag. +struct BooleanStateValue : public StateValue { + ~BooleanStateValue() override = default; + std::unique_ptr copy() const override; + + bool value{false}; +}; + /// StateValue that can be used as a dictionary. Used as the main storage value /// of DomainState so that modifications can be based on keys of the dictionary /// hierarchy. @@ -132,5 +138,3 @@ class DomainState { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_DOMAINSTATE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/HeapProfilerDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/HeapProfilerDomainAgent.h index 227214bc..6fdb22d8 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/HeapProfilerDomainAgent.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/HeapProfilerDomainAgent.h @@ -5,13 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_HEAPPROFILERDOMAINAGENT_H -#define HERMES_CDP_HEAPPROFILERDOMAINAGENT_H +#pragma once +#include #include -#include "DomainAgent.h" - namespace facebook { namespace hermes { namespace cdp { @@ -71,5 +69,3 @@ class HeapProfilerDomainAgent : public DomainAgent { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_HEAPPROFILERDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/JSONValueInterfaces.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/JSONValueInterfaces.h index 23a12ba8..9faf2d79 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/JSONValueInterfaces.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/JSONValueInterfaces.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_JSONVALUEINTERFACES_H -#define HERMES_CDP_JSONVALUEINTERFACES_H +#pragma once #include #include @@ -39,5 +38,3 @@ bool jsonValsEQ(const JSONValue *A, const JSONValue *B); } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_JSONVALUEINTERFACES_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageConverters.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageConverters.h index 7397bd1d..3489b0a9 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageConverters.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageConverters.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_MESSAGECONVERTERS_H -#define HERMES_CDP_MESSAGECONVERTERS_H +#pragma once #include #include @@ -81,5 +80,3 @@ std::unique_ptr makeProfile(const std::string &value); } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_MESSAGECONVERTERS_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageInterfaces.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageInterfaces.h index f19418f5..7dd1c5cb 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageInterfaces.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageInterfaces.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_MESSAGEINTERFACES_H -#define HERMES_CDP_MESSAGEINTERFACES_H +#pragma once #include #include @@ -71,5 +70,3 @@ struct Notification : public Serializable { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_MESSAGEINTERFACES_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypes.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypes.h index fcc86c32..682d94cb 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypes.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypes.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<> +// @generated SignedSource<<727dcfe67419625e56b2acb525424108>> #pragma once @@ -20,6 +20,7 @@ using JSONBlob = std::string; struct UnknownRequest; namespace debugger { +struct BreakLocation; using BreakpointId = std::string; struct BreakpointResolvedNotification; struct CallFrame; @@ -28,6 +29,8 @@ struct DisableRequest; struct EnableRequest; struct EvaluateOnCallFrameRequest; struct EvaluateOnCallFrameResponse; +struct GetPossibleBreakpointsRequest; +struct GetPossibleBreakpointsResponse; struct Location; struct PauseRequest; struct PausedNotification; @@ -35,8 +38,10 @@ struct RemoveBreakpointRequest; struct ResumeRequest; struct ResumedNotification; struct Scope; +using ScriptLanguage = std::string; struct ScriptParsedNotification; struct ScriptPosition; +struct SetBlackboxPatternsRequest; struct SetBlackboxedRangesRequest; struct SetBreakpointByUrlRequest; struct SetBreakpointByUrlResponse; @@ -131,9 +136,11 @@ struct RequestHandler { virtual void handle(const debugger::DisableRequest &req) = 0; virtual void handle(const debugger::EnableRequest &req) = 0; virtual void handle(const debugger::EvaluateOnCallFrameRequest &req) = 0; + virtual void handle(const debugger::GetPossibleBreakpointsRequest &req) = 0; virtual void handle(const debugger::PauseRequest &req) = 0; virtual void handle(const debugger::RemoveBreakpointRequest &req) = 0; virtual void handle(const debugger::ResumeRequest &req) = 0; + virtual void handle(const debugger::SetBlackboxPatternsRequest &req) = 0; virtual void handle(const debugger::SetBlackboxedRangesRequest &req) = 0; virtual void handle(const debugger::SetBreakpointRequest &req) = 0; virtual void handle(const debugger::SetBreakpointByUrlRequest &req) = 0; @@ -177,9 +184,11 @@ struct NoopRequestHandler : public RequestHandler { void handle(const debugger::DisableRequest &req) override {} void handle(const debugger::EnableRequest &req) override {} void handle(const debugger::EvaluateOnCallFrameRequest &req) override {} + void handle(const debugger::GetPossibleBreakpointsRequest &req) override {} void handle(const debugger::PauseRequest &req) override {} void handle(const debugger::RemoveBreakpointRequest &req) override {} void handle(const debugger::ResumeRequest &req) override {} + void handle(const debugger::SetBlackboxPatternsRequest &req) override {} void handle(const debugger::SetBlackboxedRangesRequest &req) override {} void handle(const debugger::SetBreakpointRequest &req) override {} void handle(const debugger::SetBreakpointByUrlRequest &req) override {} @@ -370,6 +379,21 @@ struct runtime::ExceptionDetails : public Serializable { std::optional executionContextId; }; +struct debugger::BreakLocation : public Serializable { + BreakLocation() = default; + BreakLocation(BreakLocation &&) = default; + BreakLocation(const BreakLocation &) = delete; + static std::unique_ptr tryMake(const JSONObject *obj); + JSONValue *toJsonVal(JSONFactory &factory) const override; + BreakLocation &operator=(const BreakLocation &) = delete; + BreakLocation &operator=(BreakLocation &&) = default; + + runtime::ScriptId scriptId{}; + long long lineNumber{}; + std::optional columnNumber; + std::optional type; +}; + struct debugger::Scope : public Serializable { Scope() = default; Scope(Scope &&) = default; @@ -623,6 +647,19 @@ struct debugger::EvaluateOnCallFrameRequest : public Request { std::optional throwOnSideEffect; }; +struct debugger::GetPossibleBreakpointsRequest : public Request { + GetPossibleBreakpointsRequest(); + static std::unique_ptr tryMake( + const JSONObject *obj); + + JSONValue *toJsonVal(JSONFactory &factory) const override; + void accept(RequestHandler &handler) const override; + + debugger::Location start{}; + std::optional end; + std::optional restrictToFunction; +}; + struct debugger::PauseRequest : public Request { PauseRequest(); static std::unique_ptr tryMake(const JSONObject *obj); @@ -652,6 +689,18 @@ struct debugger::ResumeRequest : public Request { std::optional terminateOnResume; }; +struct debugger::SetBlackboxPatternsRequest : public Request { + SetBlackboxPatternsRequest(); + static std::unique_ptr tryMake( + const JSONObject *obj); + + JSONValue *toJsonVal(JSONFactory &factory) const override; + void accept(RequestHandler &handler) const override; + + std::vector patterns; + std::optional skipAnonymous; +}; + struct debugger::SetBlackboxedRangesRequest : public Request { SetBlackboxedRangesRequest(); static std::unique_ptr tryMake( @@ -1015,6 +1064,15 @@ struct debugger::EvaluateOnCallFrameResponse : public Response { std::optional exceptionDetails; }; +struct debugger::GetPossibleBreakpointsResponse : public Response { + GetPossibleBreakpointsResponse() = default; + static std::unique_ptr tryMake( + const JSONObject *obj); + JSONValue *toJsonVal(JSONFactory &factory) const override; + + std::vector locations; +}; + struct debugger::SetBreakpointResponse : public Response { SetBreakpointResponse() = default; static std::unique_ptr tryMake(const JSONObject *obj); @@ -1181,6 +1239,7 @@ struct debugger::ScriptParsedNotification : public Notification { std::optional hasSourceURL; std::optional isModule; std::optional length; + std::optional scriptLanguage; }; struct heapProfiler::AddHeapSnapshotChunkNotification : public Notification { diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypesInlines.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypesInlines.h index fe765f93..027c9e1d 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypesInlines.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/MessageTypesInlines.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_MESSAGETYPESINLINES_H -#define HERMES_CDP_MESSAGETYPESINLINES_H +#pragma once #include #include @@ -312,5 +311,3 @@ void deleter(T *p) { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_MESSAGETYPESINLINES_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ProfilerDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ProfilerDomainAgent.h index 6c62b9c8..b074111f 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ProfilerDomainAgent.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/ProfilerDomainAgent.h @@ -5,14 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_PROFILERDOMAINAGENT_H -#define HERMES_CDP_PROFILERDOMAINAGENT_H +#pragma once +#include #include #include -#include "DomainAgent.h" - namespace facebook { namespace hermes { namespace cdp { @@ -38,5 +36,3 @@ class ProfilerDomainAgent : public DomainAgent { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_PROFILERDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectConverters.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectConverters.h index ae688884..93dfbedf 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectConverters.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectConverters.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_REMOTEOBJECTCONVERTERS_H -#define HERMES_CDP_REMOTEOBJECTCONVERTERS_H +#pragma once #include #include @@ -76,5 +75,3 @@ ExceptionDetails makeExceptionDetails( } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_REMOTEOBJECTCONVERTERS_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectsTable.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectsTable.h index 1b8fff5a..66e72164 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectsTable.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RemoteObjectsTable.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_REMOTEOBJECTSTABLE_H -#define HERMES_CDP_REMOTEOBJECTSTABLE_H +#pragma once #include #include @@ -126,5 +125,3 @@ class RemoteObjectsTable { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_REMOTEOBJECTSTABLE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RuntimeDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RuntimeDomainAgent.h index 9c8142aa..72cca3ee 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RuntimeDomainAgent.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/cdp/RuntimeDomainAgent.h @@ -5,14 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_CDP_RUNTIMEDOMAINAGENT_H -#define HERMES_CDP_RUNTIMEDOMAINAGENT_H +#pragma once #include -#include "CDPDebugAPI.h" -#include "DomainAgent.h" -#include "RemoteObjectConverters.h" +#include +#include +#include namespace facebook { namespace hermes { @@ -29,7 +28,7 @@ class RuntimeDomainAgent : public DomainAgent { RuntimeDomainAgent( int32_t executionContextID, HermesRuntime &runtime, - debugger::AsyncDebuggerAPI &asyncDebuggerAPI, + const debugger::AsyncDebuggerAPI &asyncDebuggerAPI, SynchronizedOutboundCallback messageCallback, std::shared_ptr objTable, ConsoleMessageStorage &consoleMessageStorage, @@ -115,7 +114,7 @@ class RuntimeDomainAgent : public DomainAgent { const ObjectSerializationOptions &serializationOptions); HermesRuntime &runtime_; - debugger::AsyncDebuggerAPI &asyncDebuggerAPI_; + const debugger::AsyncDebuggerAPI &asyncDebuggerAPI_; ConsoleMessageStorage &consoleMessageStorage_; ConsoleMessageDispatcher &consoleMessageDispatcher_; @@ -137,5 +136,3 @@ class RuntimeDomainAgent : public DomainAgent { } // namespace cdp } // namespace hermes } // namespace facebook - -#endif // HERMES_CDP_RUNTIMEDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Dummy.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Dummy.h new file mode 100644 index 00000000..af9cf2e7 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Dummy.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace hermes { + +/// Dummy extension for testing precompiled extension system. +/// Does nothing - just validates the multi-extension architecture. +/// +/// \param runtime The JSI runtime to install into. +/// \param extensions The precompiled extensions object containing setup +/// functions. +void installDummy(jsi::Runtime &runtime, jsi::Object &extensions); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TimerStats.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Extensions.h similarity index 59% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TimerStats.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Extensions.h index 6b3e84ec..de18a16b 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TimerStats.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Extensions.h @@ -9,15 +9,12 @@ #include -#include - namespace facebook { namespace hermes { -/// Creates and returns a Runtime that computes the time spent in invocations to -/// the Hermes VM. -std::unique_ptr makeTimedRuntime( - std::unique_ptr hermesRuntime); +/// Install all JSI extensions (TextEncoder, etc.) into the runtime. +/// The extensions object is passed in from the caller who loaded the bytecode. +void installExtensions(jsi::Runtime &rt, jsi::Object extensions); } // namespace hermes } // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/ExtensionsBytecode.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/ExtensionsBytecode.h new file mode 100644 index 00000000..fcb74829 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/ExtensionsBytecode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "llvh/ADT/ArrayRef.h" + +#include + +namespace facebook { +namespace hermes { + +/// Returns the precompiled bytecode for JSI extensions. +/// This bytecode, when executed, returns an object with setup functions +/// keyed by extension name. +llvh::ArrayRef getExtensionsBytecode(); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Intrinsics.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Intrinsics.h new file mode 100644 index 00000000..e65bc7a8 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Intrinsics.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "jsi/jsi.h" + +namespace facebook { +namespace hermes { + +/// Holds references to built-in JavaScript constructors captured at extension +/// initialization time. This ensures extensions use the original intrinsics +/// even if user code replaces the global properties. +struct ExtensionIntrinsics { + jsi::Function typeError; + jsi::Function rangeError; + jsi::Function uint8Array; + + /// Capture intrinsics from the runtime. Must be called before any user code + /// executes. + explicit ExtensionIntrinsics(jsi::Runtime &rt); +}; + +/// Capture and store intrinsics from the runtime. Must be called before any +/// user code executes. +void captureIntrinsics(jsi::Runtime &rt); + +/// Retrieve the stored intrinsics. Throws if captureIntrinsics was not called. +const ExtensionIntrinsics &getIntrinsics(jsi::Runtime &rt); + +/// Throw a TypeError with the given message. Uses the intrinsic TypeError +/// constructor captured at initialization time. +[[noreturn]] void throwTypeError(jsi::Runtime &rt, const char *message); + +/// Throw a RangeError with the given message. Uses the intrinsic RangeError +/// constructor captured at initialization time. +[[noreturn]] void throwRangeError(jsi::Runtime &rt, const char *message); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/JSIUtils.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/JSIUtils.h new file mode 100644 index 00000000..5412b50f --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/JSIUtils.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "Intrinsics.h" + +#include "jsi/jsi.h" +#include "llvh/ADT/Optional.h" + +#include +#include +#include +#include + +namespace facebook { +namespace hermes { + +/// Result of getTypedArrayBuffer - the destination buffer info. +struct TypedArrayBufferInfo { + uint8_t *data; + size_t byteLength; +}; + +/// MutableBuffer implementation that owns a std::string of UTF-8 data. +class UTF8Buffer : public jsi::MutableBuffer { + public: + explicit UTF8Buffer(std::string &&data) : data_(std::move(data)) {} + + size_t size() const override { + return data_.size(); + } + + uint8_t *data() override { + return reinterpret_cast(data_.data()); + } + + private: + std::string data_; +}; + +/// Try to convert a jsi::Value to a valid unsigned integer of type T. +/// Returns llvh::None if the value is not a number, is NaN, negative, +/// not an integer, exceeds MAX_SAFE_INTEGER, or exceeds the range of T. +template +inline llvh::Optional valueToUnsigned(const jsi::Value &val) { + static_assert(std::is_unsigned::value, "T must be an unsigned type"); + if (!val.isNumber()) { + return llvh::None; + } + double d = val.asNumber(); + // Check non-negative and within MAX_SAFE_INTEGER (2^53 - 1), which is exactly + // representable as a double. This ensures d could be an exact integer. + constexpr double maxSafeInteger = 9007199254740991.0; // 2^53 - 1 + if (!(d >= 0 && d <= maxSafeInteger)) { + return llvh::None; + } + // Cast to uint64_t and verify no fractional part was lost. + auto u = static_cast(d); + if (static_cast(u) != d) { + return llvh::None; + } + if (u > std::numeric_limits::max()) { + return llvh::None; + } + return static_cast(u); +} + +/// Extract buffer info from a TypedArray-like object. +/// We use duck-typing (checking for buffer/byteOffset/byteLength) rather than +/// instanceof Uint8Array. This is more permissive than the spec requires, but +/// handles cross-realm Uint8Arrays and is consistent with JSI's portable +/// design. The original VM implementation used internal type checks. +/// Throws JSError if the object is not a valid TypedArray or is detached. +/// \param errorMessage The error message to use if the object is invalid. +/// \param detachedErrorMessage The error message to use if the buffer is +/// detached. +TypedArrayBufferInfo getTypedArrayBuffer( + jsi::Runtime &rt, + const jsi::Value &val, + const char *errorMessage, + const char *detachedErrorMessage); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/TextEncoder.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/TextEncoder.h new file mode 100644 index 00000000..465ebda6 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/TextEncoder.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace hermes { + +/// Install the TextEncoder constructor on the global object. +/// This creates a proper TextEncoder class conforming to the WHATWG Encoding +/// Standard with: +/// - constructor (callable with 'new TextEncoder()') +/// - encoding getter (always returns "utf-8") +/// - encode(string) method returning Uint8Array +/// - encodeInto(string, Uint8Array) method returning {read, written} +/// +/// \param runtime The JSI runtime to install into. +/// \param extensions The precompiled extensions object containing setup +/// functions. +void installTextEncoder(jsi::Runtime &runtime, jsi::Object &extensions); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Worker.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Worker.h new file mode 100644 index 00000000..691723e0 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/Worker.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#ifdef JSI_UNSTABLE +#ifndef HERMES_EXTENSIONS_WORKER_H +#define HERMES_EXTENSIONS_WORKER_H + +#include + +namespace facebook { +namespace hermes { + +/// Install the Worker constructor on the global object. +/// \param runtime The JSI runtime to install into. +/// \param extensions The precompiled extensions object containing setup +/// functions. +void installWorker(jsi::Runtime &rt, jsi::Object &extensions); + +} // namespace hermes +} // namespace facebook + +#endif // HERMES_EXTENSIONS_WORKER_H +#endif // JSI_UNSTABLE diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/ContribDummy.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/ContribDummy.h new file mode 100644 index 00000000..d6c115ec --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/ContribDummy.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace hermes { + +/// ContribDummy extension - example community contribution. +/// Does nothing - serves as a template for contrib extensions. +/// +/// \param runtime The JSI runtime to install into. +/// \param extensions The precompiled extensions object containing setup +/// functions. +void installContribDummy(jsi::Runtime &runtime, jsi::Object &extensions); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/ContribExtensions.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/ContribExtensions.h new file mode 100644 index 00000000..37638601 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/ContribExtensions.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace hermes { + +/// Install all contrib (community-contributed) extensions into the runtime. +/// +/// \param runtime The JSI runtime to install into. +/// \param extensions The precompiled extensions object containing setup +/// functions. +void installContribExtensions(jsi::Runtime &runtime, jsi::Object &extensions); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/TextDecoder.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/TextDecoder.h new file mode 100644 index 00000000..63f0ad07 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/TextDecoder.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace hermes { + +/// Install the TextDecoder constructor on the global object. +/// This creates a proper TextDecoder class conforming to the WHATWG Encoding +/// Standard with: +/// - constructor (callable with 'new TextDecoder(label, options)') +/// - encoding getter (returns the encoding name) +/// - fatal getter (returns the fatal flag) +/// - ignoreBOM getter (returns the ignoreBOM flag) +/// - decode(input, options) method returning a string +/// +/// \param runtime The JSI runtime to install into. +/// \param extensions The precompiled extensions object containing setup +/// functions. +void installTextDecoder(jsi::Runtime &runtime, jsi::Object &extensions); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/TextDecoderUtils.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/TextDecoderUtils.h new file mode 100644 index 00000000..4dd5e87b --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/extensions/contrib/TextDecoderUtils.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "llvh/ADT/Optional.h" +#include "llvh/ADT/StringRef.h" + +#include +#include + +namespace facebook { +namespace hermes { + +// Encoding types supported by TextDecoder. isSingleByteEncoding and +// kSingleByteEncodings depend on the default enum values. +enum class TextDecoderEncoding : uint8_t { + UTF8, + UTF16LE, + UTF16BE, + IBM866, + ISO_8859_2, + ISO_8859_3, + ISO_8859_4, + ISO_8859_5, + ISO_8859_6, + ISO_8859_7, + ISO_8859_8, + ISO_8859_8_I, + ISO_8859_10, + ISO_8859_13, + ISO_8859_14, + ISO_8859_15, + ISO_8859_16, + KOI8_R, + KOI8_U, + Macintosh, + Windows874, + Windows1250, + Windows1251, + Windows1252, + Windows1253, + Windows1254, + Windows1255, + Windows1256, + Windows1257, + Windows1258, + XMacCyrillic, + _count, +}; + +enum class DecodeError { + None = 0, + InvalidSequence, // Invalid byte sequence (fatal mode) + InvalidSurrogate, // Invalid surrogate (fatal mode) + OddByteCount, // Odd byte count in UTF-16 (fatal mode) +}; + +// First single-byte encoding value. +static constexpr uint8_t kFirstSingleByteEncoding = + static_cast(TextDecoderEncoding::IBM866); + +// Number of single-byte encoding values +static constexpr uint8_t kNumSingleByteEncodings = + (uint8_t)TextDecoderEncoding::_count - kFirstSingleByteEncoding; + +inline bool isSingleByteEncoding(TextDecoderEncoding enc) { + return static_cast(enc) >= kFirstSingleByteEncoding; +} + +// Array of pointers to single-byte encoding tables, indexed by +// (TextDecoderEncoding - kFirstSingleByteEncoding). +extern const std::array + kSingleByteEncodings; + +// Parse the encoding label and return the corresponding encoding type. +// Returns llvh::None if the encoding is not supported. +llvh::Optional parseEncodingLabel(llvh::StringRef label); + +// Get the canonical encoding name for the given encoding type. +const char *getEncodingName(TextDecoderEncoding encoding); + +DecodeError decodeUTF8( + const uint8_t *bytes, + size_t length, + bool fatal, + bool ignoreBOM, + bool stream, + bool bomSeen, + std::u16string *decoded, + uint8_t outPendingBytes[4], + size_t *outPendingCount, + bool *outBOMSeen); + +DecodeError decodeUTF16( + const uint8_t *bytes, + size_t length, + bool fatal, + bool ignoreBOM, + bool bigEndian, + bool stream, + bool bomSeen, + std::u16string *decoded, + uint8_t outPendingBytes[4], + size_t *outPendingCount, + bool *outBOMSeen); + +} // namespace hermes +} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes.h index 0d6d70fc..1a66be93 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_HERMES_H -#define HERMES_HERMES_H +#pragma once #include #include @@ -17,227 +16,201 @@ #include #include +#include +#include #include #include -#include "js_native_api.h" - struct HermesTestHelper; +struct SHUnit; +struct SHRuntime; namespace hermes { - namespace vm { - class GCExecTrace; - class Runtime; - } // namespace vm +namespace vm { +class GCExecTrace; +class Runtime; +class SerializedValue; +} // namespace vm } // namespace hermes namespace facebook { - namespace jsi { - - class ThreadSafeRuntime; - - } - - namespace hermes { - - namespace debugger { - class Debugger; - } - - class HermesRuntimeImpl; - -/// Represents a Hermes JS runtime. - class HERMES_EXPORT HermesRuntime : public jsi::Runtime { - public: - - napi_status createNapiEnv(napi_env *env); - - static bool isHermesBytecode(const uint8_t *data, size_t len); - // Returns the supported bytecode version. - static uint32_t getBytecodeVersion(); - // (EXPERIMENTAL) Issues madvise calls for portions of the given - // bytecode file that will likely be used when loading the bytecode - // file and running its global function. - static void prefetchHermesBytecode(const uint8_t *data, size_t len); - // Returns whether the data is valid HBC with more extensive checks than - // isHermesBytecode and returns why it isn't in errorMessage (if nonnull) - // if not. - static bool hermesBytecodeSanityCheck( - const uint8_t *data, - size_t len, - std::string *errorMessage = nullptr); - static void setFatalHandler(void (*handler)(const std::string &)); - - // Assuming that \p data is valid HBC bytecode data, returns a pointer to the - // first element of the epilogue, data append to the end of the bytecode - // stream. Return pair contain ptr to data and header. - static std::pair getBytecodeEpilogue( - const uint8_t *data, - size_t len); - - /// Enable sampling profiler. - /// Starts a separate thread that polls VM state with \p meanHzFreq frequency. - /// Any subsequent call to \c enableSamplingProfiler() is ignored until - /// next call to \c disableSamplingProfiler() - static void enableSamplingProfiler(double meanHzFreq = 100); - - /// Disable the sampling profiler - static void disableSamplingProfiler(); - - /// Dump sampled stack trace to the given file name. - static void dumpSampledTraceToFile(const std::string &fileName); - - /// Dump sampled stack trace to the given stream. - static void dumpSampledTraceToStream(std::ostream &stream); - - /// Serialize the sampled stack to the format expected by DevTools' - /// Profiler.stop return type. - void sampledTraceToStreamInDevToolsFormat(std::ostream &stream); - - /// Return the executed JavaScript function info. - /// This information holds the segmentID, Virtualoffset and sourceURL. - /// This information is needed specifically to be able to symbolicate non-CJS - /// bundles correctly. This API will be simplified later to simply return a - /// segmentID and virtualOffset, when we are able to only support CJS bundles. - static std::unordered_map> - getExecutedFunctions(); - - /// \return whether code coverage profiler is enabled or not. - static bool isCodeCoverageProfilerEnabled(); - - /// Enable code coverage profiler. - static void enableCodeCoverageProfiler(); - - /// Disable code coverage profiler. - static void disableCodeCoverageProfiler(); - - // The base class declares most of the interesting methods. This - // just declares new methods which are specific to HermesRuntime. - // The actual implementations of the pure virtual methods are - // provided by a class internal to the .cpp file, which is created - // by the factory. - - /// Load a new segment into the Runtime. - /// The \param context must be a valid RequireContext retrieved from JS - /// using `require.context`. - void loadSegment( - std::unique_ptr buffer, - const jsi::Value &context); - - /// Gets a guaranteed unique id for an Object (or, respectively, String - /// or PropNameId), which is assigned at allocation time and is - /// static throughout that object's (or string's, or PropNameID's) - /// lifetime. - uint64_t getUniqueID(const jsi::Object &o) const; - uint64_t getUniqueID(const jsi::BigInt &s) const; - uint64_t getUniqueID(const jsi::String &s) const; - uint64_t getUniqueID(const jsi::PropNameID &pni) const; - uint64_t getUniqueID(const jsi::Symbol &sym) const; - - /// Same as the other \c getUniqueID, except it can return 0 for some values. - /// 0 means there is no ID associated with the value. - uint64_t getUniqueID(const jsi::Value &val) const; - - /// From an ID retrieved from \p getUniqueID, go back to the object. - /// NOTE: This is much slower in general than the reverse operation, and takes - /// up more memory. Don't use this unless it's absolutely necessary. - /// \return a jsi::Object if a matching object is found, else returns null. - jsi::Value getObjectForID(uint64_t id); - - /// Get a structure representing the execution history (currently just of - /// GC, but will be generalized as necessary), to aid in debugging - /// non-deterministic execution. - const ::hermes::vm::GCExecTrace &getGCExecTrace() const; - - /// Get IO tracking (aka HBC page access) info as a JSON string. - /// See hermes::vm::Runtime::getIOTrackingInfoJSON() for conditions - /// needed for there to be useful output. - std::string getIOTrackingInfoJSON(); - -#ifdef HERMESVM_PROFILER_BB - /// Write the trace to the given stream. - void dumpBasicBlockProfileTrace(std::ostream &os) const; -#endif +namespace jsi { -#ifdef HERMESVM_PROFILER_OPCODE - /// Write the opcode stats to the given stream. - void dumpOpcodeStats(std::ostream &os) const; -#endif +class ThreadSafeRuntime; - /// \return a reference to the Debugger for this Runtime. - debugger::Debugger &getDebugger(); +} -#ifdef HERMES_ENABLE_DEBUGGER - - struct DebugFlags { - // Looking for the .lazy flag? It's no longer necessary. - // Source is evaluated lazily by default. See - // RuntimeConfig::CompilationMode. - }; +namespace hermes { - /// Evaluate the given code in an unoptimized form, - /// used for debugging. - void debugJavaScript( - const std::string &src, - const std::string &sourceURL, - const DebugFlags &debugFlags); +namespace debugger { +class Debugger; +} + +class HermesRuntime; +/// The Hermes Root API interface. This is the entry point to create the Hermes +/// runtime and to access Hermes-specific methods that do not rely on a runtime +/// instance. +class HERMES_EXPORT IHermesRootAPI : public jsi::ICast { + public: + static constexpr jsi::UUID uuid{ + 0xb654d898, + 0xdfad, + 0x11ef, + 0x859a, + 0x325096b39f47}; + + // Returns an instance of Hermes Runtime. + virtual std::unique_ptr makeHermesRuntime( + const ::hermes::vm::RuntimeConfig &runtimeConfig) = 0; + + virtual bool isHermesBytecode(const uint8_t *data, size_t len) = 0; + + // Returns the supported bytecode version. + virtual uint32_t getBytecodeVersion() = 0; + + // (EXPERIMENTAL) Issues madvise calls for portions of the given + // bytecode file that will likely be used when loading the bytecode + // file and running its global function. + virtual void prefetchHermesBytecode(const uint8_t *data, size_t len) = 0; + + // Returns whether the data is valid HBC with more extensive checks than + // isHermesBytecode and returns why it isn't in errorMessage (if nonnull) + // if not. + virtual bool hermesBytecodeSanityCheck( + const uint8_t *data, + size_t len, + std::string *errorMessage = nullptr) = 0; + + /// Sets a global fatal handler that is shared across all active Hermes + /// runtimes. Setting fatal handler in multiple places will override the + /// previous fatal handler set by this functionality. + /// The fatal handler must not throw exceptions, as Hermes is compiled without + /// exceptions. + virtual void setFatalHandler(void (*handler)(const std::string &)) = 0; + + // Assuming that \p data is valid HBC bytecode data, returns a pointer to the + // first element of the epilogue, data append to the end of the bytecode + // stream. Return pair contain ptr to data and header. + virtual std::pair getBytecodeEpilogue( + const uint8_t *data, + size_t len) = 0; + + /// Enable sampling profiler. + /// Starts a separate thread that polls VM state with \p meanHzFreq frequency. + /// Any subsequent call to \c enableSamplingProfiler() is ignored until + /// next call to \c disableSamplingProfiler() + virtual void enableSamplingProfiler(double meanHzFreq = 100) = 0; + + /// Disable the sampling profiler + virtual void disableSamplingProfiler() = 0; + + /// Dump sampled stack trace to the given file name. + virtual void dumpSampledTraceToFile(const std::string &fileName) = 0; + + /// Dump sampled stack trace to the given stream. + virtual void dumpSampledTraceToStream(std::ostream &stream) = 0; + + /// Return the executed JavaScript function info. + /// This information holds the segmentID, Virtualoffset and sourceURL. + /// This information is needed specifically to be able to symbolicate non-CJS + /// bundles correctly. This API will be simplified later to simply return a + /// segmentID and virtualOffset, when we are able to only support CJS bundles. + virtual std::unordered_map> + getExecutedFunctions() = 0; + + /// \return whether code coverage profiler is enabled or not. + virtual bool isCodeCoverageProfilerEnabled() = 0; + + /// Enable code coverage profiler. + virtual void enableCodeCoverageProfiler() = 0; + + /// Disable code coverage profiler. + virtual void disableCodeCoverageProfiler() = 0; + + protected: + /// The destructor is protected as delete calls on interfaces must not occur. + /// It is also non-virtual to simplify the v-table. + ~IHermesRootAPI() {} +}; + +/// The setFatalHandler functionality has global effects, which may cause +/// unintended or surprising behavior for users of this API. For this reason, it +/// is not recommended and the functionality is provided by the optional +/// interface ISetFatalHandler. +class HERMES_EXPORT ISetFatalHandler : public jsi::ICast { + public: + static constexpr jsi::UUID uuid{ + 0xda98a610, + 0x09cb, + 0x11f0, + 0x87bf, + 0x325096b39f47}; + /// Sets a global fatal handler that is shared across all active Hermes + /// runtimes. Setting fatal handler in multiple places will override the + /// previous fatal handler set by this functionality. + /// The fatal handler must not throw exceptions, as Hermes is compiled without + /// exceptions. + virtual void setFatalHandler(void (*handler)(const std::string &)) = 0; + + protected: + ~ISetFatalHandler() = default; +}; + +/// Interface for methods that are exposed for test purposes. +class HERMES_EXPORT IHermesTestHelpers : public jsi::ICast { + public: + static constexpr jsi::UUID uuid{ + 0x664e489a, + 0xf941, + 0x11ef, + 0xa44c, + 0x325096b39f47}; + + virtual size_t rootsListLengthForTests() const = 0; + + protected: + ~IHermesTestHelpers() = default; +}; + +#ifdef JSI_UNSTABLE +// Interface for methods that are exposed for tracing purposes. +class IHermesTracingHelpers : public jsi::ICast { + public: + static constexpr jsi::UUID uuid{ + 0x74ac2c4e, + 0xc660, + 0x11f0, + 0x8de9, + 0x0242ac120002}; + + // Returns the secret for obtaining the underlying SerializedValue object + virtual const ::hermes::vm::SerializedValue *getHermesSerializedValue( + const jsi::Serialized &serialized) const = 0; + virtual const std::shared_ptr makeSerialized( + ::hermes::vm::SerializedValue &value) const = 0; + + protected: + ~IHermesTracingHelpers() = default; +}; #endif - /// Register this runtime and thread for sampling profiler. Before using the - /// runtime on another thread, invoke this function again from the new thread - /// to make the sampling profiler target the new thread (and forget the old - /// thread). - void registerForProfiling(); - /// Unregister this runtime for sampling profiler. - void unregisterForProfiling(); - - /// Define methods to interrupt JS execution and set time limits. - /// All JS compiled to bytecode via prepareJS, or evaluateJS, will support - /// interruption and time limit monitoring if the runtime is configured with - /// AsyncBreakCheckInEval. If JS prepared in other ways is executed, care must - /// be taken to ensure that it is compiled in a mode that supports it (i.e., - /// the emitted code contains async break checks). - - /// Asynchronously terminates the current execution. This can be called on - /// any thread. - void asyncTriggerTimeout(); - - /// Register this runtime for execution time limit monitoring, with a time - /// limit of \p timeoutInMs milliseconds. - /// See compilation notes above. - void watchTimeLimit(uint32_t timeoutInMs); - /// Unregister this runtime for execution time limit monitoring. - void unwatchTimeLimit(); - - /// Same as \c evaluate JavaScript but with a source map, which will be - /// applied to exception traces and debug information. - /// - /// This is an experimental Hermes-specific API. In the future it may be - /// renamed, moved or combined with another API, but the provided - /// functionality will continue to be available in some form. - jsi::Value evaluateJavaScriptWithSourceMap( - const std::shared_ptr &buffer, - const std::shared_ptr &sourceMapBuf, - const std::string &sourceURL); - - /// Returns the underlying low level Hermes VM runtime instance. - /// This function is considered unsafe and unstable. - /// Direct use of a vm::Runtime should be avoided as the lower level APIs are - /// unsafe and they can change without notice. - ::hermes::vm::Runtime *getVMRuntimeUnsafe() const; - - private: - // Only HermesRuntimeImpl can subclass this. - HermesRuntime() = default; - friend class HermesRuntimeImpl; - - friend struct ::HermesTestHelper; - size_t rootsListLengthForTests() const; - - // Do not add any members here. This ensures that there are no - // object size inconsistencies. All data should be in the impl - // class in the .cpp file. - }; +class HermesRuntime : public jsi::Runtime, + public IHermes, + public IHermesSHUnit { + public: + /// Similar to jsi::Runtime, HermesRuntime is treated as an object, rather + /// than a pure interface. This is to prevent breaking usages of + /// HermesRuntime prior to the introduction of jsi::IRuntime, IHermes, and + /// other interfaces. + ~HermesRuntime() override = default; + + using jsi::Runtime::castInterface; +}; + +/// Returns a pointer to an object that can be cast into IHermesRootAPI, which +/// can be used to create a Hermes runtime and to access global Hermes-specific +/// methods. This object has static lifetime. +HERMES_EXPORT jsi::ICast *makeHermesRootAPI(); /// Return a RuntimeConfig that is more suited for running untrusted JS than /// the default config. Disables some language features and may trade off some @@ -248,16 +221,22 @@ namespace facebook { /// conf.withArrayBuffer(true); /// ... /// auto runtime = makeHermesRuntime(conf.build()); - HERMES_EXPORT ::hermes::vm::RuntimeConfig hardenedHermesRuntimeConfig(); - - HERMES_EXPORT std::unique_ptr makeHermesRuntime( - const ::hermes::vm::RuntimeConfig &runtimeConfig = - ::hermes::vm::RuntimeConfig()); - HERMES_EXPORT std::unique_ptr - makeThreadSafeHermesRuntime( - const ::hermes::vm::RuntimeConfig &runtimeConfig = - ::hermes::vm::RuntimeConfig()); - } // namespace hermes +HERMES_EXPORT ::hermes::vm::RuntimeConfig hardenedHermesRuntimeConfig(); + +HERMES_EXPORT std::unique_ptr makeHermesRuntime( + const ::hermes::vm::RuntimeConfig &runtimeConfig = + ::hermes::vm::RuntimeConfig()); + +/// Create a HermesRuntime for the given config without throwing any exceptions. +/// This is safe to be called from code that is compiled without exceptions. +/// Returns nullptr on failure. +HERMES_EXPORT std::unique_ptr makeHermesRuntimeNoThrow( + const ::hermes::vm::RuntimeConfig &runtimeConfig = + ::hermes::vm::RuntimeConfig()) noexcept; + +HERMES_EXPORT std::unique_ptr +makeThreadSafeHermesRuntime( + const ::hermes::vm::RuntimeConfig &runtimeConfig = + ::hermes::vm::RuntimeConfig()); +} // namespace hermes } // namespace facebook - -#endif \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_api.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_api.h deleted file mode 100644 index f0e616f8..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_api.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_HERMES_API_H -#define HERMES_HERMES_API_H - -#include "js_runtime.h" - -EXTERN_C_START - -typedef struct hermes_local_connection_s *hermes_local_connection; -typedef struct hermes_remote_connection_s *hermes_remote_connection; - -//============================================================================= -// jsr_runtime -//============================================================================= - -JSR_API hermes_dump_crash_data(jsr_runtime runtime, int32_t fd); -JSR_API hermes_sampling_profiler_enable(); -JSR_API hermes_sampling_profiler_disable(); -JSR_API hermes_sampling_profiler_add(jsr_runtime runtime); -JSR_API hermes_sampling_profiler_remove(jsr_runtime runtime); -JSR_API hermes_sampling_profiler_dump_to_file(const char *filename); - -//============================================================================= -// jsr_config -//============================================================================= - -JSR_API hermes_config_enable_default_crash_handler( - jsr_config config, - bool value); - -//============================================================================= -// Setting inspector singleton -//============================================================================= - -typedef int32_t(NAPI_CDECL *hermes_inspector_add_page_cb)( - const char *title, - const char *vm, - void *connectFunc); - -typedef void(NAPI_CDECL *hermes_inspector_remove_page_cb)(int32_t page_id); - -JSR_API hermes_set_inspector( - hermes_inspector_add_page_cb add_page_cb, - hermes_inspector_remove_page_cb remove_page_cb); - -//============================================================================= -// Local and remote inspector connections. -// Local is defined in Hermes VM, Remote is defined by inspector outside of VM. -//============================================================================= - -typedef void(NAPI_CDECL *hermes_remote_connection_send_message_cb)( - hermes_remote_connection remote_connection, - const char *message); - -typedef void(NAPI_CDECL *hermes_remote_connection_disconnect_cb)( - hermes_remote_connection remote_connection); - -JSR_API hermes_create_local_connection( - void *connect_func, - hermes_remote_connection remote_connection, - hermes_remote_connection_send_message_cb on_send_message_cb, - hermes_remote_connection_disconnect_cb on_disconnect_cb, - jsr_data_delete_cb on_delete_cb, - void *deleter_data, - hermes_local_connection *local_connection); - -JSR_API hermes_delete_local_connection( - hermes_local_connection local_connection); - -JSR_API hermes_local_connection_send_message( - hermes_local_connection local_connection, - const char *message); - -JSR_API hermes_local_connection_disconnect( - hermes_local_connection local_connection); - -EXTERN_C_END - -#endif // !HERMES_HERMES_API_H \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_tracing.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_tracing.h index 470e82d9..6e30607a 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_tracing.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/hermes_tracing.h @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifndef HERMES_HERMES_TRACING_H -#define HERMES_HERMES_TRACING_H +#pragma once #include @@ -30,8 +29,8 @@ namespace hermes { /// The return value of \p traceCompletionCallback indicates whether the /// invocation completed successfully. If \p traceCompletionCallback is null, it /// also assumes as if the callback is successful. -std::unique_ptr makeTracingHermesRuntime( - std::unique_ptr hermesRuntime, +std::shared_ptr makeTracingHermesRuntime( + std::shared_ptr hermesRuntime, const ::hermes::vm::RuntimeConfig &runtimeConfig, const std::string &traceScratchPath, const std::string &traceResultPath, @@ -43,13 +42,11 @@ std::unique_ptr makeTracingHermesRuntime( /// \p traceStream the stream to write trace to. /// \p forReplay indicates whether the runtime is being used in trace replay and /// tracing. -std::unique_ptr makeTracingHermesRuntime( - std::unique_ptr hermesRuntime, +std::shared_ptr makeTracingHermesRuntime( + std::shared_ptr hermesRuntime, const ::hermes::vm::RuntimeConfig &runtimeConfig, std::unique_ptr traceStream, bool forReplay = false); } // namespace hermes } // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/RuntimeAdapter.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/RuntimeAdapter.h deleted file mode 100644 index 64396f2c..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/RuntimeAdapter.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include - -#ifndef INSPECTOR_EXPORT -#ifdef _MSC_VER -#ifdef CREATE_SHARED_LIBRARY -#define INSPECTOR_EXPORT __declspec(dllexport) -#else -#define INSPECTOR_EXPORT -#endif // CREATE_SHARED_LIBRARY -#else // _MSC_VER -#define INSPECTOR_EXPORT __attribute__((visibility("default"))) -#endif // _MSC_VER -#endif // !defined(INSPECTOR_EXPORT) - -namespace facebook { -namespace hermes { -namespace inspector_modern { - -/** - * RuntimeAdapter encapsulates a HermesRuntime object. The underlying Hermes - * runtime object should stay alive for at least as long as the RuntimeAdapter - * is alive. - */ -class INSPECTOR_EXPORT RuntimeAdapter { - public: - virtual ~RuntimeAdapter() = 0; - - /// getRuntime should return the runtime encapsulated by this adapter. The - /// CDP Handler will only invoke this function from the runtime thread. - virtual HermesRuntime &getRuntime() = 0; - - /// \p tickleJs is a method that subclasses can choose to override to make - /// the inspector more responsive. If overridden, it should call the - /// \p __tickleJs JavaScript function. Calling JavaScript functions must be - /// done on the runtime thread, and \p tickleJs() may be invoked from an - /// arbitrary thread. Thus, the call to \p __tickleJs should occur with - /// appropriate locking (e.g. via a thread-safe runtime instance, or by - /// enqueuing the call on to a dedicated JS thread). - /// - /// This makes the inspector more responsive because it gives the inspector - /// the ability to force the process to enter the Hermes interpreter loop - /// soon. This is important because the inspector can only do a number of - /// important operations (like manipulating breakpoints) within the context of - /// a Hermes interperter loop. - /// - /// The default implementation does nothing. - virtual void tickleJs(); -}; - -/** - * SharedRuntimeAdapter is a simple implementation of RuntimeAdapter that - * uses shared_ptr to hold on to the runtime. It's generally only used in tests, - * since it does not implement tickleJs. - */ -class INSPECTOR_EXPORT SharedRuntimeAdapter : public RuntimeAdapter { - public: - SharedRuntimeAdapter(std::shared_ptr runtime); - ~SharedRuntimeAdapter() override; - - HermesRuntime &getRuntime() override; - - private: - std::shared_ptr runtime_; -}; - -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/CDPHandler.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/CDPHandler.h deleted file mode 100644 index 01fe26eb..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/CDPHandler.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// using include guards instead of #pragma once due to compile issues -// with MSVC and BUCK -#ifndef HERMES_INSPECTOR_CDPHANDLER_H -#define HERMES_INSPECTOR_CDPHANDLER_H - -#include -#include -#include -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { - -using CDPMessageCallbackFunction = std::function; -using OnUnregisterFunction = std::function; - -class CDPHandlerImpl; - -struct State; - -/// Utility struct to configure the initial state of the CDP session. -struct INSPECTOR_EXPORT CDPHandlerSessionConfig { - bool isRuntimeDomainEnabled{false}; -}; - -/// Configuration for the execution context managed by the CDPHandler. -struct INSPECTOR_EXPORT CDPHandlerExecutionContextDescription { - int32_t id{}; - std::string origin; - std::string name; - std::optional auxData; - bool shouldSendNotifications{}; -}; - -/// CDPHandler processes CDP messages between the client and the debugger. -/// It performs no networking or connection logic itself. -/// The CDP Handler is invoked from multiple threads. The locking strategy is -/// to acquire the lock at each entry point into the class, and hold it until -/// the entry function has returned. In practice, these functions fall into 2 -/// categories: public functions invoked by the creator of this instance, and -/// callbacks invoked by the runtime to report events. -/// Once the lock is held, most members are safe to use from any thread, with -/// the notable exception of the runtime (and debugger retrieved from the -/// runtime). Most runtime methods must only be invoked when running on the -/// runtime thread, which occurs in the CDP Handler constructor/destructor, and -/// callbacks from the runtime thread (e.g. host functions, instrumentation -/// callbacks, and pause callback). -class INSPECTOR_EXPORT CDPHandler { - /// Hide the constructor so users can only construct via static create - /// methods. - CDPHandler( - std::unique_ptr adapter, - const std::string &title, - bool waitForDebugger, - bool processConsoleAPI, - std::shared_ptr state, - const CDPHandlerSessionConfig &sessionConfig, - std::optional - executionContextDescription); - - public: - /// Creating a CDPHandler enables the debugger on the provided runtime. This - /// should generally called before you start running any JS in the runtime. - /// This should also be called on the runtime thread, as methods are invoked - /// on the given \p adapter. - static std::shared_ptr create( - std::unique_ptr adapter, - bool waitForDebugger = false, - bool processConsoleAPI = true, - std::shared_ptr state = nullptr, - const CDPHandlerSessionConfig &sessionConfig = {}, - std::optional - executionContextDescription = std::nullopt); - /// Temporarily kept to allow React Native build to still work - static std::shared_ptr create( - std::unique_ptr adapter, - const std::string &title, - bool waitForDebugger = false, - bool processConsoleAPI = true, - std::shared_ptr state = nullptr, - const CDPHandlerSessionConfig &sessionConfig = {}, - std::optional - executionContextDescription = std::nullopt); - ~CDPHandler(); - - /// getTitle returns the name of the friendly name of the runtime that's shown - /// to users in the CDP frontend (e.g. Chrome DevTools). - std::string getTitle() const; - - /// Provide a callback to receive replies and notifications from the debugger, - /// and optionally provide a function to be called during - /// unregisterCallbacks(). - /// \param msgCallback Function to receive replies and notifications from the - /// debugger - /// \param onDisconnect Function that will be invoked upon calling - /// unregisterCallbacks - /// \return true if there wasn't a previously registered callback - bool registerCallbacks( - CDPMessageCallbackFunction msgCallback, - OnUnregisterFunction onUnregister); - - /// Unregister any previously registered callbacks. - /// \return true if there were previously registered callbacks - bool unregisterCallbacks(); - - /// Process a JSON-encoded Chrome DevTools Protocol request. - void handle(std::string str); - - /// Extract state to be persisted across reloads. - std::unique_ptr getState(); - - private: - std::shared_ptr impl_; - const std::string title_; -}; - -/// Public-facing wrapper for internal CDP state that can be preserved across -/// reloads. -struct INSPECTOR_EXPORT State { - /// Incomplete type that stores the actual state. - struct Private; - - /// Create a new wrapper with the provided \p privateState. - explicit State(std::unique_ptr privateState); - ~State(); - - /// Get the wrapped state. - Private &get() { - return *privateState_.get(); - } - - private: - /// Pointer to the actual stored state, hidden from users of this wrapper. - std::unique_ptr privateState_; -}; - -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook - -#endif // HERMES_INSPECTOR_CDPHandler_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/CallbackOStream.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/CallbackOStream.h deleted file mode 100644 index a9831555..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/CallbackOStream.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { - -/// Subclass of \c std::ostream where flushing is implemented through a -/// callback. Writes are collected in a buffer. When filled, the buffer's -/// contents are emptied out and sent to a callback. -struct CallbackOStream : public std::ostream { - /// Signature of callback called to flush buffer contents. Accepts the buffer - /// as a string. Returns a boolean indicating whether flushing succeeded. - /// Callback failure will be translated to stream failure. If the callback - /// throws an exception it will be swallowed and translated into stream - /// failure. - using Fn = std::function; - - /// Construct a new stream. - /// - /// \p sz The size of the buffer -- how large it can get before it must be - /// flushed. Must be non-zero. - /// \p cb The callback function. - CallbackOStream(size_t sz, Fn cb); - - /// This class is neither movable nor copyable. - CallbackOStream(CallbackOStream &&that) = delete; - CallbackOStream &operator=(CallbackOStream &&that) = delete; - CallbackOStream(const CallbackOStream &that) = delete; - CallbackOStream &operator=(const CallbackOStream &that) = delete; - - private: - /// \c std::streambuf sub-class backed by a std::string buffer and - /// implementing overflow by calling a callback. - struct StreamBuf : public std::streambuf { - /// Construct a new streambuf. Parameters are the same as those of - /// \c CallbackOStream . - StreamBuf(size_t sz, Fn cb); - - /// Destruction will flush any remaining buffer contents. - ~StreamBuf() override; - - /// StreamBufs are not copyable, to avoid the flush callback receiving - /// the contents of multiple streams. - StreamBuf(const StreamBuf &) = delete; - StreamBuf &operator=(const StreamBuf &) = delete; - - protected: - /// std::streambuf overrides - int_type overflow(int_type ch) override; - int sync() override; - - private: - /// The size of the backing buffer. Fixed for an instance of the streambuf. - size_t sz_; - - /// The backing buffer that writes will go to until full. - std::unique_ptr buf_; - - /// The function called when buf_ has been filled. - Fn cb_; - - /// Clears the backing buffer. - void reset(); - - /// Clears the backing buffer and returns it contents in a string. - std::string take(); - }; - - StreamBuf sbuf_; -}; - -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/JSONValueInterfaces.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/JSONValueInterfaces.h deleted file mode 100644 index 26331381..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/JSONValueInterfaces.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { -using namespace ::hermes::parser; - -/// Convert a string to a JSONValue. Will return nullopt if parsing is not -/// successful. -std::optional parseStr( - const std::string &str, - JSONFactory &factory); - -/// Convert a string to a JSON object. Will return nullopt if parsing is not -/// successful, or the resulting JSON value is not an object. -std::optional parseStrAsJsonObj( - const std::string &str, - JSONFactory &factory); - -/// Convert a JSONValue to a string. -std::string jsonValToStr(const JSONValue *v); - -/// Check if two JSONValues are equal. -bool jsonValsEQ(const JSONValue *A, const JSONValue *B); - -}; // namespace chrome -}; // namespace inspector_modern -}; // namespace hermes -}; // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageConverters.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageConverters.h deleted file mode 100644 index fd26c9ed..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageConverters.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { -namespace message { - -template -void setChromeLocation( - T &chromeLoc, - const facebook::hermes::debugger::SourceLocation &hermesLoc) { - if (hermesLoc.line != facebook::hermes::debugger::kInvalidLocation) { - chromeLoc.lineNumber = hermesLoc.line - 1; - } - - if (hermesLoc.column != facebook::hermes::debugger::kInvalidLocation) { - chromeLoc.columnNumber = hermesLoc.column - 1; - } -} - -/// ErrorCode magic numbers match JSC's (see InspectorBackendDispatcher.cpp) -enum class ErrorCode { - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - ServerError = -32000 -}; - -ErrorResponse -makeErrorResponse(int id, ErrorCode code, const std::string &message); - -OkResponse makeOkResponse(int id); - -namespace debugger { - -Location makeLocation(const facebook::hermes::debugger::SourceLocation &loc); - -} // namespace debugger - -namespace runtime { - -CallFrame makeCallFrame(const facebook::hermes::debugger::CallFrameInfo &info); - -std::vector makeCallFrames( - const facebook::hermes::debugger::StackTrace &stackTrace); - -ExceptionDetails makeExceptionDetails( - const facebook::hermes::debugger::ExceptionDetails &details); - -} // namespace runtime - -namespace heapProfiler { - -std::unique_ptr makeSamplingHeapProfile( - const std::string &value); - -} // namespace heapProfiler - -namespace profiler { - -std::unique_ptr makeProfile(const std::string &value); - -} // namespace profiler - -} // namespace message -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageInterfaces.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageInterfaces.h deleted file mode 100644 index 01e369e2..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageInterfaces.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { -namespace message { -using namespace ::hermes::parser; - -struct RequestHandler; - -/// Serializable is an interface for objects that can be serialized to and from -/// JSON. -struct Serializable { - virtual ~Serializable() = default; - virtual JSONValue *toJsonVal(JSONFactory &factory) const = 0; - - std::string toJsonStr() const; -}; - -/// Requests are sent from the debugger to the target. -struct Request : public Serializable { - using ParseResult = std::variant, std::string>; - static std::unique_ptr fromJson(const std::string &str); - - Request() = default; - explicit Request(std::string method) : method(method) {} - - // accept dispatches to the appropriate handler method in RequestHandler based - // on the type of the request. - virtual void accept(RequestHandler &handler) const = 0; - - long long id = 0; - std::string method; -}; - -/// Responses are sent from the target to the debugger in response to a Request. -struct Response : public Serializable { - Response() = default; - - long long id = 0; -}; - -/// Notifications are sent from the target to the debugger. This is used to -/// notify the debugger about events that occur in the target, e.g. stopping -/// at a breakpoint. -struct Notification : public Serializable { - Notification() = default; - explicit Notification(std::string method) : method(method) {} - - std::string method; -}; - -} // namespace message -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageTypes.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageTypes.h deleted file mode 100644 index e039758f..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageTypes.h +++ /dev/null @@ -1,1183 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<<3ebea508f76e06269045891097f89eb5>> - -#pragma once - -#include -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { -namespace message { - -template -void deleter(T *p); -using JSONBlob = std::string; -struct UnknownRequest; - -namespace debugger { -using BreakpointId = std::string; -struct BreakpointResolvedNotification; -struct CallFrame; -using CallFrameId = std::string; -struct DisableRequest; -struct EnableRequest; -struct EvaluateOnCallFrameRequest; -struct EvaluateOnCallFrameResponse; -struct Location; -struct PauseRequest; -struct PausedNotification; -struct RemoveBreakpointRequest; -struct ResumeRequest; -struct ResumedNotification; -struct Scope; -struct ScriptParsedNotification; -struct SetBreakpointByUrlRequest; -struct SetBreakpointByUrlResponse; -struct SetBreakpointRequest; -struct SetBreakpointResponse; -struct SetBreakpointsActiveRequest; -struct SetInstrumentationBreakpointRequest; -struct SetInstrumentationBreakpointResponse; -struct SetPauseOnExceptionsRequest; -struct StepIntoRequest; -struct StepOutRequest; -struct StepOverRequest; -} // namespace debugger - -namespace runtime { -struct CallArgument; -struct CallFrame; -struct CallFunctionOnRequest; -struct CallFunctionOnResponse; -struct CompileScriptRequest; -struct CompileScriptResponse; -struct ConsoleAPICalledNotification; -struct CustomPreview; -struct DisableRequest; -struct EnableRequest; -struct EntryPreview; -struct EvaluateRequest; -struct EvaluateResponse; -struct ExceptionDetails; -struct ExecutionContextCreatedNotification; -struct ExecutionContextDescription; -using ExecutionContextId = long long; -struct GetHeapUsageRequest; -struct GetHeapUsageResponse; -struct GetPropertiesRequest; -struct GetPropertiesResponse; -struct GlobalLexicalScopeNamesRequest; -struct GlobalLexicalScopeNamesResponse; -struct InternalPropertyDescriptor; -struct ObjectPreview; -struct PropertyDescriptor; -struct PropertyPreview; -struct RemoteObject; -using RemoteObjectId = std::string; -struct RunIfWaitingForDebuggerRequest; -using ScriptId = std::string; -struct StackTrace; -using Timestamp = double; -using UnserializableValue = std::string; -} // namespace runtime - -namespace heapProfiler { -struct AddHeapSnapshotChunkNotification; -struct CollectGarbageRequest; -struct GetHeapObjectIdRequest; -struct GetHeapObjectIdResponse; -struct GetObjectByHeapObjectIdRequest; -struct GetObjectByHeapObjectIdResponse; -using HeapSnapshotObjectId = std::string; -struct HeapStatsUpdateNotification; -struct LastSeenObjectIdNotification; -struct ReportHeapSnapshotProgressNotification; -struct SamplingHeapProfile; -struct SamplingHeapProfileNode; -struct SamplingHeapProfileSample; -struct StartSamplingRequest; -struct StartTrackingHeapObjectsRequest; -struct StopSamplingRequest; -struct StopSamplingResponse; -struct StopTrackingHeapObjectsRequest; -struct TakeHeapSnapshotRequest; -} // namespace heapProfiler - -namespace profiler { -struct PositionTickInfo; -struct Profile; -struct ProfileNode; -struct StartRequest; -struct StopRequest; -struct StopResponse; -} // namespace profiler - -/// RequestHandler handles requests via the visitor pattern. -struct RequestHandler { - virtual ~RequestHandler() = default; - - virtual void handle(const UnknownRequest &req) = 0; - virtual void handle(const debugger::DisableRequest &req) = 0; - virtual void handle(const debugger::EnableRequest &req) = 0; - virtual void handle(const debugger::EvaluateOnCallFrameRequest &req) = 0; - virtual void handle(const debugger::PauseRequest &req) = 0; - virtual void handle(const debugger::RemoveBreakpointRequest &req) = 0; - virtual void handle(const debugger::ResumeRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointByUrlRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointsActiveRequest &req) = 0; - virtual void handle( - const debugger::SetInstrumentationBreakpointRequest &req) = 0; - virtual void handle(const debugger::SetPauseOnExceptionsRequest &req) = 0; - virtual void handle(const debugger::StepIntoRequest &req) = 0; - virtual void handle(const debugger::StepOutRequest &req) = 0; - virtual void handle(const debugger::StepOverRequest &req) = 0; - virtual void handle(const heapProfiler::CollectGarbageRequest &req) = 0; - virtual void handle(const heapProfiler::GetHeapObjectIdRequest &req) = 0; - virtual void handle( - const heapProfiler::GetObjectByHeapObjectIdRequest &req) = 0; - virtual void handle(const heapProfiler::StartSamplingRequest &req) = 0; - virtual void handle( - const heapProfiler::StartTrackingHeapObjectsRequest &req) = 0; - virtual void handle(const heapProfiler::StopSamplingRequest &req) = 0; - virtual void handle( - const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0; - virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; - virtual void handle(const profiler::StartRequest &req) = 0; - virtual void handle(const profiler::StopRequest &req) = 0; - virtual void handle(const runtime::CallFunctionOnRequest &req) = 0; - virtual void handle(const runtime::CompileScriptRequest &req) = 0; - virtual void handle(const runtime::DisableRequest &req) = 0; - virtual void handle(const runtime::EnableRequest &req) = 0; - virtual void handle(const runtime::EvaluateRequest &req) = 0; - virtual void handle(const runtime::GetHeapUsageRequest &req) = 0; - virtual void handle(const runtime::GetPropertiesRequest &req) = 0; - virtual void handle(const runtime::GlobalLexicalScopeNamesRequest &req) = 0; - virtual void handle(const runtime::RunIfWaitingForDebuggerRequest &req) = 0; -}; - -/// NoopRequestHandler can be subclassed to only handle some requests. -struct NoopRequestHandler : public RequestHandler { - void handle(const UnknownRequest &req) override {} - void handle(const debugger::DisableRequest &req) override {} - void handle(const debugger::EnableRequest &req) override {} - void handle(const debugger::EvaluateOnCallFrameRequest &req) override {} - void handle(const debugger::PauseRequest &req) override {} - void handle(const debugger::RemoveBreakpointRequest &req) override {} - void handle(const debugger::ResumeRequest &req) override {} - void handle(const debugger::SetBreakpointRequest &req) override {} - void handle(const debugger::SetBreakpointByUrlRequest &req) override {} - void handle(const debugger::SetBreakpointsActiveRequest &req) override {} - void handle( - const debugger::SetInstrumentationBreakpointRequest &req) override {} - void handle(const debugger::SetPauseOnExceptionsRequest &req) override {} - void handle(const debugger::StepIntoRequest &req) override {} - void handle(const debugger::StepOutRequest &req) override {} - void handle(const debugger::StepOverRequest &req) override {} - void handle(const heapProfiler::CollectGarbageRequest &req) override {} - void handle(const heapProfiler::GetHeapObjectIdRequest &req) override {} - void handle( - const heapProfiler::GetObjectByHeapObjectIdRequest &req) override {} - void handle(const heapProfiler::StartSamplingRequest &req) override {} - void handle( - const heapProfiler::StartTrackingHeapObjectsRequest &req) override {} - void handle(const heapProfiler::StopSamplingRequest &req) override {} - void handle( - const heapProfiler::StopTrackingHeapObjectsRequest &req) override {} - void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} - void handle(const profiler::StartRequest &req) override {} - void handle(const profiler::StopRequest &req) override {} - void handle(const runtime::CallFunctionOnRequest &req) override {} - void handle(const runtime::CompileScriptRequest &req) override {} - void handle(const runtime::DisableRequest &req) override {} - void handle(const runtime::EnableRequest &req) override {} - void handle(const runtime::EvaluateRequest &req) override {} - void handle(const runtime::GetHeapUsageRequest &req) override {} - void handle(const runtime::GetPropertiesRequest &req) override {} - void handle(const runtime::GlobalLexicalScopeNamesRequest &req) override {} - void handle(const runtime::RunIfWaitingForDebuggerRequest &req) override {} -}; - -/// Types -struct debugger::Location : public Serializable { - Location() = default; - Location(Location &&) = default; - Location(const Location &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - Location &operator=(const Location &) = delete; - Location &operator=(Location &&) = default; - - runtime::ScriptId scriptId{}; - long long lineNumber{}; - std::optional columnNumber; -}; - -struct runtime::PropertyPreview : public Serializable { - PropertyPreview() = default; - PropertyPreview(PropertyPreview &&) = default; - PropertyPreview(const PropertyPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - PropertyPreview &operator=(const PropertyPreview &) = delete; - PropertyPreview &operator=(PropertyPreview &&) = default; - - std::string name; - std::string type; - std::optional value; - std::unique_ptr< - runtime::ObjectPreview, - std::function> - valuePreview{nullptr, deleter}; - std::optional subtype; -}; - -struct runtime::EntryPreview : public Serializable { - EntryPreview() = default; - EntryPreview(EntryPreview &&) = default; - EntryPreview(const EntryPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - EntryPreview &operator=(const EntryPreview &) = delete; - EntryPreview &operator=(EntryPreview &&) = default; - - std::unique_ptr< - runtime::ObjectPreview, - std::function> - key{nullptr, deleter}; - std::unique_ptr< - runtime::ObjectPreview, - std::function> - value{nullptr, deleter}; -}; - -struct runtime::ObjectPreview : public Serializable { - ObjectPreview() = default; - ObjectPreview(ObjectPreview &&) = default; - ObjectPreview(const ObjectPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ObjectPreview &operator=(const ObjectPreview &) = delete; - ObjectPreview &operator=(ObjectPreview &&) = default; - - std::string type; - std::optional subtype; - std::optional description; - bool overflow{}; - std::vector properties; - std::optional> entries; -}; - -struct runtime::CustomPreview : public Serializable { - CustomPreview() = default; - CustomPreview(CustomPreview &&) = default; - CustomPreview(const CustomPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CustomPreview &operator=(const CustomPreview &) = delete; - CustomPreview &operator=(CustomPreview &&) = default; - - std::string header; - std::optional bodyGetterId; -}; - -struct runtime::RemoteObject : public Serializable { - RemoteObject() = default; - RemoteObject(RemoteObject &&) = default; - RemoteObject(const RemoteObject &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - RemoteObject &operator=(const RemoteObject &) = delete; - RemoteObject &operator=(RemoteObject &&) = default; - - std::string type; - std::optional subtype; - std::optional className; - std::optional value; - std::optional unserializableValue; - std::optional description; - std::optional objectId; - std::optional preview; - std::optional customPreview; -}; - -struct runtime::CallFrame : public Serializable { - CallFrame() = default; - CallFrame(CallFrame &&) = default; - CallFrame(const CallFrame &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CallFrame &operator=(const CallFrame &) = delete; - CallFrame &operator=(CallFrame &&) = default; - - std::string functionName; - runtime::ScriptId scriptId{}; - std::string url; - long long lineNumber{}; - long long columnNumber{}; -}; - -struct runtime::StackTrace : public Serializable { - StackTrace() = default; - StackTrace(StackTrace &&) = default; - StackTrace(const StackTrace &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - StackTrace &operator=(const StackTrace &) = delete; - StackTrace &operator=(StackTrace &&) = default; - - std::optional description; - std::vector callFrames; - std::unique_ptr parent; -}; - -struct runtime::ExceptionDetails : public Serializable { - ExceptionDetails() = default; - ExceptionDetails(ExceptionDetails &&) = default; - ExceptionDetails(const ExceptionDetails &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ExceptionDetails &operator=(const ExceptionDetails &) = delete; - ExceptionDetails &operator=(ExceptionDetails &&) = default; - - long long exceptionId{}; - std::string text; - long long lineNumber{}; - long long columnNumber{}; - std::optional scriptId; - std::optional url; - std::optional stackTrace; - std::optional exception; - std::optional executionContextId; -}; - -struct debugger::Scope : public Serializable { - Scope() = default; - Scope(Scope &&) = default; - Scope(const Scope &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - Scope &operator=(const Scope &) = delete; - Scope &operator=(Scope &&) = default; - - std::string type; - runtime::RemoteObject object{}; - std::optional name; - std::optional startLocation; - std::optional endLocation; -}; - -struct debugger::CallFrame : public Serializable { - CallFrame() = default; - CallFrame(CallFrame &&) = default; - CallFrame(const CallFrame &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CallFrame &operator=(const CallFrame &) = delete; - CallFrame &operator=(CallFrame &&) = default; - - debugger::CallFrameId callFrameId{}; - std::string functionName; - std::optional functionLocation; - debugger::Location location{}; - std::string url; - std::vector scopeChain; - runtime::RemoteObject thisObj{}; - std::optional returnValue; -}; - -struct heapProfiler::SamplingHeapProfileNode : public Serializable { - SamplingHeapProfileNode() = default; - SamplingHeapProfileNode(SamplingHeapProfileNode &&) = default; - SamplingHeapProfileNode(const SamplingHeapProfileNode &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - SamplingHeapProfileNode &operator=(const SamplingHeapProfileNode &) = delete; - SamplingHeapProfileNode &operator=(SamplingHeapProfileNode &&) = default; - - runtime::CallFrame callFrame{}; - double selfSize{}; - long long id{}; - std::vector children; -}; - -struct heapProfiler::SamplingHeapProfileSample : public Serializable { - SamplingHeapProfileSample() = default; - SamplingHeapProfileSample(SamplingHeapProfileSample &&) = default; - SamplingHeapProfileSample(const SamplingHeapProfileSample &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - SamplingHeapProfileSample &operator=(const SamplingHeapProfileSample &) = - delete; - SamplingHeapProfileSample &operator=(SamplingHeapProfileSample &&) = default; - - double size{}; - long long nodeId{}; - double ordinal{}; -}; - -struct heapProfiler::SamplingHeapProfile : public Serializable { - SamplingHeapProfile() = default; - SamplingHeapProfile(SamplingHeapProfile &&) = default; - SamplingHeapProfile(const SamplingHeapProfile &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - SamplingHeapProfile &operator=(const SamplingHeapProfile &) = delete; - SamplingHeapProfile &operator=(SamplingHeapProfile &&) = default; - - heapProfiler::SamplingHeapProfileNode head{}; - std::vector samples; -}; - -struct profiler::PositionTickInfo : public Serializable { - PositionTickInfo() = default; - PositionTickInfo(PositionTickInfo &&) = default; - PositionTickInfo(const PositionTickInfo &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - PositionTickInfo &operator=(const PositionTickInfo &) = delete; - PositionTickInfo &operator=(PositionTickInfo &&) = default; - - long long line{}; - long long ticks{}; -}; - -struct profiler::ProfileNode : public Serializable { - ProfileNode() = default; - ProfileNode(ProfileNode &&) = default; - ProfileNode(const ProfileNode &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ProfileNode &operator=(const ProfileNode &) = delete; - ProfileNode &operator=(ProfileNode &&) = default; - - long long id{}; - runtime::CallFrame callFrame{}; - std::optional hitCount; - std::optional> children; - std::optional deoptReason; - std::optional> positionTicks; -}; - -struct profiler::Profile : public Serializable { - Profile() = default; - Profile(Profile &&) = default; - Profile(const Profile &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - Profile &operator=(const Profile &) = delete; - Profile &operator=(Profile &&) = default; - - std::vector nodes; - double startTime{}; - double endTime{}; - std::optional> samples; - std::optional> timeDeltas; -}; - -struct runtime::CallArgument : public Serializable { - CallArgument() = default; - CallArgument(CallArgument &&) = default; - CallArgument(const CallArgument &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CallArgument &operator=(const CallArgument &) = delete; - CallArgument &operator=(CallArgument &&) = default; - - std::optional value; - std::optional unserializableValue; - std::optional objectId; -}; - -struct runtime::ExecutionContextDescription : public Serializable { - ExecutionContextDescription() = default; - ExecutionContextDescription(ExecutionContextDescription &&) = default; - ExecutionContextDescription(const ExecutionContextDescription &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ExecutionContextDescription &operator=(const ExecutionContextDescription &) = - delete; - ExecutionContextDescription &operator=(ExecutionContextDescription &&) = - default; - - runtime::ExecutionContextId id{}; - std::string origin; - std::string name; - std::optional auxData; -}; - -struct runtime::PropertyDescriptor : public Serializable { - PropertyDescriptor() = default; - PropertyDescriptor(PropertyDescriptor &&) = default; - PropertyDescriptor(const PropertyDescriptor &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - PropertyDescriptor &operator=(const PropertyDescriptor &) = delete; - PropertyDescriptor &operator=(PropertyDescriptor &&) = default; - - std::string name; - std::optional value; - std::optional writable; - std::optional get; - std::optional set; - bool configurable{}; - bool enumerable{}; - std::optional wasThrown; - std::optional isOwn; - std::optional symbol; -}; - -struct runtime::InternalPropertyDescriptor : public Serializable { - InternalPropertyDescriptor() = default; - InternalPropertyDescriptor(InternalPropertyDescriptor &&) = default; - InternalPropertyDescriptor(const InternalPropertyDescriptor &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - InternalPropertyDescriptor &operator=(const InternalPropertyDescriptor &) = - delete; - InternalPropertyDescriptor &operator=(InternalPropertyDescriptor &&) = - default; - - std::string name; - std::optional value; -}; - -/// Requests -struct UnknownRequest : public Request { - UnknownRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional params; -}; - -struct debugger::DisableRequest : public Request { - DisableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::EnableRequest : public Request { - EnableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::EvaluateOnCallFrameRequest : public Request { - EvaluateOnCallFrameRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - debugger::CallFrameId callFrameId{}; - std::string expression; - std::optional objectGroup; - std::optional includeCommandLineAPI; - std::optional silent; - std::optional returnByValue; - std::optional generatePreview; - std::optional throwOnSideEffect; -}; - -struct debugger::PauseRequest : public Request { - PauseRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::RemoveBreakpointRequest : public Request { - RemoveBreakpointRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - debugger::BreakpointId breakpointId{}; -}; - -struct debugger::ResumeRequest : public Request { - ResumeRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional terminateOnResume; -}; - -struct debugger::SetBreakpointRequest : public Request { - SetBreakpointRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - debugger::Location location{}; - std::optional condition; -}; - -struct debugger::SetBreakpointByUrlRequest : public Request { - SetBreakpointByUrlRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - long long lineNumber{}; - std::optional url; - std::optional urlRegex; - std::optional scriptHash; - std::optional columnNumber; - std::optional condition; -}; - -struct debugger::SetBreakpointsActiveRequest : public Request { - SetBreakpointsActiveRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - bool active{}; -}; - -struct debugger::SetInstrumentationBreakpointRequest : public Request { - SetInstrumentationBreakpointRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string instrumentation; -}; - -struct debugger::SetPauseOnExceptionsRequest : public Request { - SetPauseOnExceptionsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string state; -}; - -struct debugger::StepIntoRequest : public Request { - StepIntoRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::StepOutRequest : public Request { - StepOutRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::StepOverRequest : public Request { - StepOverRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::CollectGarbageRequest : public Request { - CollectGarbageRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::GetHeapObjectIdRequest : public Request { - GetHeapObjectIdRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; -}; - -struct heapProfiler::GetObjectByHeapObjectIdRequest : public Request { - GetObjectByHeapObjectIdRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - heapProfiler::HeapSnapshotObjectId objectId{}; - std::optional objectGroup; -}; - -struct heapProfiler::StartSamplingRequest : public Request { - StartSamplingRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional samplingInterval; - std::optional includeObjectsCollectedByMajorGC; - std::optional includeObjectsCollectedByMinorGC; -}; - -struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { - StartTrackingHeapObjectsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional trackAllocations; -}; - -struct heapProfiler::StopSamplingRequest : public Request { - StopSamplingRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::StopTrackingHeapObjectsRequest : public Request { - StopTrackingHeapObjectsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional reportProgress; - std::optional treatGlobalObjectsAsRoots; - std::optional captureNumericValue; -}; - -struct heapProfiler::TakeHeapSnapshotRequest : public Request { - TakeHeapSnapshotRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional reportProgress; - std::optional treatGlobalObjectsAsRoots; - std::optional captureNumericValue; -}; - -struct profiler::StartRequest : public Request { - StartRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct profiler::StopRequest : public Request { - StopRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::CallFunctionOnRequest : public Request { - CallFunctionOnRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string functionDeclaration; - std::optional objectId; - std::optional> arguments; - std::optional silent; - std::optional returnByValue; - std::optional generatePreview; - std::optional userGesture; - std::optional awaitPromise; - std::optional executionContextId; - std::optional objectGroup; -}; - -struct runtime::CompileScriptRequest : public Request { - CompileScriptRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string expression; - std::string sourceURL; - bool persistScript{}; - std::optional executionContextId; -}; - -struct runtime::DisableRequest : public Request { - DisableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::EnableRequest : public Request { - EnableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::EvaluateRequest : public Request { - EvaluateRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string expression; - std::optional objectGroup; - std::optional includeCommandLineAPI; - std::optional silent; - std::optional contextId; - std::optional returnByValue; - std::optional generatePreview; - std::optional userGesture; - std::optional awaitPromise; -}; - -struct runtime::GetHeapUsageRequest : public Request { - GetHeapUsageRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::GetPropertiesRequest : public Request { - GetPropertiesRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; - std::optional ownProperties; - std::optional generatePreview; -}; - -struct runtime::GlobalLexicalScopeNamesRequest : public Request { - GlobalLexicalScopeNamesRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional executionContextId; -}; - -struct runtime::RunIfWaitingForDebuggerRequest : public Request { - RunIfWaitingForDebuggerRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -/// Responses -struct ErrorResponse : public Response { - ErrorResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - long long code; - std::string message; - std::optional data; -}; - -struct OkResponse : public Response { - OkResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; -}; - -struct debugger::EvaluateOnCallFrameResponse : public Response { - EvaluateOnCallFrameResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct debugger::SetBreakpointResponse : public Response { - SetBreakpointResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; - debugger::Location actualLocation{}; -}; - -struct debugger::SetBreakpointByUrlResponse : public Response { - SetBreakpointByUrlResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; - std::vector locations; -}; - -struct debugger::SetInstrumentationBreakpointResponse : public Response { - SetInstrumentationBreakpointResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; -}; - -struct heapProfiler::GetHeapObjectIdResponse : public Response { - GetHeapObjectIdResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - heapProfiler::HeapSnapshotObjectId heapSnapshotObjectId{}; -}; - -struct heapProfiler::GetObjectByHeapObjectIdResponse : public Response { - GetObjectByHeapObjectIdResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; -}; - -struct heapProfiler::StopSamplingResponse : public Response { - StopSamplingResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - heapProfiler::SamplingHeapProfile profile{}; -}; - -struct profiler::StopResponse : public Response { - StopResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - profiler::Profile profile{}; -}; - -struct runtime::CallFunctionOnResponse : public Response { - CallFunctionOnResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct runtime::CompileScriptResponse : public Response { - CompileScriptResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::optional scriptId; - std::optional exceptionDetails; -}; - -struct runtime::EvaluateResponse : public Response { - EvaluateResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct runtime::GetHeapUsageResponse : public Response { - GetHeapUsageResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - double usedSize{}; - double totalSize{}; -}; - -struct runtime::GetPropertiesResponse : public Response { - GetPropertiesResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector result; - std::optional> - internalProperties; - std::optional exceptionDetails; -}; - -struct runtime::GlobalLexicalScopeNamesResponse : public Response { - GlobalLexicalScopeNamesResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector names; -}; - -/// Notifications -struct debugger::BreakpointResolvedNotification : public Notification { - BreakpointResolvedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; - debugger::Location location{}; -}; - -struct debugger::PausedNotification : public Notification { - PausedNotification(); - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector callFrames; - std::string reason; - std::optional data; - std::optional> hitBreakpoints; - std::optional asyncStackTrace; -}; - -struct debugger::ResumedNotification : public Notification { - ResumedNotification(); - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; -}; - -struct debugger::ScriptParsedNotification : public Notification { - ScriptParsedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::ScriptId scriptId{}; - std::string url; - long long startLine{}; - long long startColumn{}; - long long endLine{}; - long long endColumn{}; - runtime::ExecutionContextId executionContextId{}; - std::string hash; - std::optional executionContextAuxData; - std::optional sourceMapURL; - std::optional hasSourceURL; - std::optional isModule; - std::optional length; -}; - -struct heapProfiler::AddHeapSnapshotChunkNotification : public Notification { - AddHeapSnapshotChunkNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::string chunk; -}; - -struct heapProfiler::HeapStatsUpdateNotification : public Notification { - HeapStatsUpdateNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector statsUpdate; -}; - -struct heapProfiler::LastSeenObjectIdNotification : public Notification { - LastSeenObjectIdNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - long long lastSeenObjectId{}; - double timestamp{}; -}; - -struct heapProfiler::ReportHeapSnapshotProgressNotification - : public Notification { - ReportHeapSnapshotProgressNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - long long done{}; - long long total{}; - std::optional finished; -}; - -struct runtime::ConsoleAPICalledNotification : public Notification { - ConsoleAPICalledNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::string type; - std::vector args; - runtime::ExecutionContextId executionContextId{}; - runtime::Timestamp timestamp{}; - std::optional stackTrace; -}; - -struct runtime::ExecutionContextCreatedNotification : public Notification { - ExecutionContextCreatedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::ExecutionContextDescription context{}; -}; - -} // namespace message -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageTypesInlines.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageTypesInlines.h deleted file mode 100644 index 49a4995d..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/MessageTypesInlines.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { -namespace message { - -template -using optional = std::optional; - -template -struct is_vector : std::false_type {}; - -template -struct is_vector> : std::true_type {}; - -/// valueFromJson - -/// Convert JSONValue to a Serializable type. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return T::tryMake(res); -} - -/// Convert JSONValue to a bool. -template -typename std::enable_if::value, std::unique_ptr>::type -valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->getValue()); -} - -/// Convert JSONValue to a long long. -template -typename std::enable_if::value, std::unique_ptr>:: - type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->getValue()); -} - -/// Convert JSONValue to a double. -template -typename std::enable_if::value, std::unique_ptr>:: - type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->getValue()); -} - -/// Convert JSONValue to a string. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->c_str()); -} - -/// Convert JSONValue to a vector. -template -typename std::enable_if::value, std::unique_ptr>::type -valueFromJson(const JSONValue *items) { - auto *arr = llvh::dyn_cast(items); - std::unique_ptr result = std::make_unique(); - result->reserve(arr->size()); - for (const auto &item : *arr) { - auto itemResult = valueFromJson(item); - if (!itemResult) { - return nullptr; - } - result->push_back(std::move(*itemResult)); - } - return result; -} - -/// Convert JSONValue to a JSONObject. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(JSONValue *v) { - auto *res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res); -} - -/// Pass through JSONValues. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(JSONValue *v) { - return std::make_unique(v); -} - -/// assign(lhs, obj, key) is a wrapper for: -/// -/// lhs = obj[key] -/// -/// It mainly exists so that we can choose the right version of valueFromJson -/// based on the type of lhs. - -template -bool assign(T &lhs, const JSONObject *obj, const U &key) { - JSONValue *v = obj->get(key); - if (v == nullptr) { - return false; - } - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(*convertResult); - return true; - } - return false; -} - -template -bool assign(optional &lhs, const JSONObject *obj, const U &key) { - JSONValue *v = obj->get(key); - if (v != nullptr) { - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(*convertResult); - return true; - } - return false; - } else { - lhs.reset(); - return true; - } -} - -template -bool assign(std::unique_ptr &lhs, const JSONObject *obj, const U &key) { - JSONValue *v = obj->get(key); - if (v != nullptr) { - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(convertResult); - return true; - } - return false; - } else { - lhs.reset(); - return true; - } -} - -template -bool assign( - std::unique_ptr> &lhs, - const JSONObject *obj, - const U &key) { - JSONValue *v = obj->get(key); - if (v != nullptr) { - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(convertResult); - return true; - } - return false; - } else { - lhs.reset(); - return true; - } -} - -/// valueToJson - -inline JSONValue *valueToJson(const Serializable &value, JSONFactory &factory) { - return value.toJsonVal(factory); -} - -// Convert a bool to JSONValue. -inline JSONValue *valueToJson(bool b, JSONFactory &factory) { - return factory.getBoolean(b); -} - -// Convert a long long to JSONValue. -inline JSONValue *valueToJson(long long num, JSONFactory &factory) { - return factory.getNumber(num); -} - -// Convert a double to JSONValue. -inline JSONValue *valueToJson(double num, JSONFactory &factory) { - return factory.getNumber(num); -} - -// Convert a string to JSONValue. -inline JSONValue *valueToJson(const std::string &str, JSONFactory &factory) { - return factory.getString(str); -} - -// Convert a vector to JSONValue. -template -JSONValue *valueToJson(const std::vector &items, JSONFactory &factory) { - llvh::SmallVector storage; - for (const auto &item : items) { - storage.push_back(valueToJson(item, factory)); - } - return factory.newArray(storage.size(), storage.begin(), storage.end()); -} - -// Cast a JSONObject to JSONValue. -inline JSONValue *valueToJson(JSONObject *obj, JSONFactory &factory) { - return llvh::cast(obj); -} - -// Pass through JSONValues. -inline JSONValue *valueToJson(JSONValue *v, JSONFactory &factory) { - return v; -} - -/// put(obj, key, value) is meant to be a wrapper for: -/// obj[key] = valueToJson(value); -/// However, JSONObjects are immutable, so we represent a 'put' operation as -/// pushing a new element onto a vector of JSONFactory::Props. - -using Properties = llvh::SmallVectorImpl; - -template -void put( - Properties &props, - const std::string &key, - const V &value, - JSONFactory &factory) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(value, factory); - props.push_back({jsStr, jsVal}); -} - -template -void put( - Properties &props, - const std::string &key, - const optional &optValue, - JSONFactory &factory) { - if (optValue.has_value()) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(optValue.value(), factory); - props.push_back({jsStr, jsVal}); - } -} - -template -void put( - Properties &props, - const std::string &key, - const std::unique_ptr &ptr, - JSONFactory &factory) { - if (ptr.get()) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(*ptr, factory); - props.push_back({jsStr, jsVal}); - } -} - -template -void put( - Properties &props, - const std::string &key, - const std::unique_ptr> &ptr, - JSONFactory &factory) { - if (ptr.get()) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(*ptr, factory); - props.push_back({jsStr, jsVal}); - } -} - -template -void deleter(T *p) { - delete p; -} - -} // namespace message -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/RemoteObjectConverters.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/RemoteObjectConverters.h deleted file mode 100644 index 89355dc3..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/RemoteObjectConverters.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { -namespace message { - -namespace debugger { - -CallFrame makeCallFrame( - uint32_t callFrameIndex, - const facebook::hermes::debugger::CallFrameInfo &callFrameInfo, - const facebook::hermes::debugger::LexicalInfo &lexicalInfo, - facebook::hermes::inspector_modern::chrome::RemoteObjectsTable &objTable, - jsi::Runtime &runtime, - const facebook::hermes::debugger::ProgramState &state); - -std::vector makeCallFrames( - const facebook::hermes::debugger::ProgramState &state, - facebook::hermes::inspector_modern::chrome::RemoteObjectsTable &objTable, - jsi::Runtime &runtime); - -} // namespace debugger - -namespace runtime { - -RemoteObject makeRemoteObject( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Value &value, - facebook::hermes::inspector_modern::chrome::RemoteObjectsTable &objTable, - const std::string &objectGroup, - bool byValue = false, - bool generatePreview = false); - -} // namespace runtime - -} // namespace message -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/RemoteObjectsTable.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/RemoteObjectsTable.h deleted file mode 100644 index d7a3370f..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/RemoteObjectsTable.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { - -/// Well-known object group names - -/** - * Objects created as a result of the Debugger.paused notification (e.g. scope - * objects) are placed in the "backtrace" object group. This object group is - * cleared when the VM resumes. - */ -extern const char *BacktraceObjectGroup; - -/** - * Objects that are created as a result of a console evaluation are placed in - * the "console" object group. This object group is cleared when the client - * clears the console. - */ -extern const char *ConsoleObjectGroup; - -/** - * RemoteObjectsTable manages the mapping of string object ids to scope metadata - * or actual JSI objects. The debugger vends these ids to the client so that the - * client can perform operations on the ids (e.g. enumerate properties on the - * object backed by the id). See Runtime.RemoteObjectId in the CDT docs for - * more details. - * - * Note that object handles are not ref-counted. Suppose an object foo is mapped - * to object id "objId" and is also in object group "objGroup". Then *either* of - * `releaseObject("objId")` or `releaseObjectGroup("objGroup")` will remove foo - * from the table. This matches the behavior of object groups in CDT. - */ -class RemoteObjectsTable { - public: - RemoteObjectsTable(); - ~RemoteObjectsTable(); - - RemoteObjectsTable(const RemoteObjectsTable &) = delete; - RemoteObjectsTable &operator=(const RemoteObjectsTable &) = delete; - - /** - * addScope adds the provided (frameIndex, scopeIndex) mapping to the table. - * If objectGroup is non-empty, then the scope object is also added to that - * object group for releasing via releaseObjectGroup. Returns an object id. - */ - std::string addScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup); - - /** - * addValue adds the JSI value to the table. If objectGroup is non-empty, then - * the scope object is also added to that object group for releasing via - * releaseObjectGroup. Returns an object id. - */ - std::string addValue( - ::facebook::jsi::Value value, - const std::string &objectGroup); - - /** - * Retrieves the (frameIndex, scopeIndex) associated with this object id, or - * nullptr if no mapping exists. The pointer stays valid as long as you only - * call const methods on this class. - */ - const std::pair *getScope(const std::string &objId) const; - - /** - * Retrieves the JSI value associated with this object id, or nullptr if no - * mapping exists. The pointer stays valid as long as you only call const - * methods on this class. - */ - const ::facebook::jsi::Value *getValue(const std::string &objId) const; - - /** - * Retrieves the object group that this object id is in, or empty string if it - * isn't in an object group. The returned pointer is only guaranteed to be - * valid until the next call to this class. - */ - std::string getObjectGroup(const std::string &objId) const; - - /** - * Removes the scope or JSI value backed by the provided object ID from the - * table. - */ - void releaseObject(const std::string &objId); - - /** - * Removes all objects that are part of the provided object group from the - * table. - */ - void releaseObjectGroup(const std::string &objectGroup); - - private: - void releaseObject(int64_t id); - - int64_t scopeId_ = -1; - int64_t valueId_ = 1; - - std::unordered_map> scopes_; - std::unordered_map values_; - std::unordered_map idToGroup_; - std::unordered_map> groupToIds_; -}; - -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/AsyncHermesRuntime.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/AsyncHermesRuntime.h deleted file mode 100644 index aaaf9cd0..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/AsyncHermesRuntime.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { - -/// URL assigned to scripts being executed in the absense of a caller-specified -/// URL. -constexpr auto kDefaultUrl = "url"; - -/** - * AsyncHermesRuntime is a helper class that runs JS scripts in a Hermes VM on - * a separate thread. This is useful for tests that want to test running JS - * in a multithreaded environment. - */ -class AsyncHermesRuntime { - public: - // Create a runtime. If veryLazy, configure the runtime to use completely - // lazy compilation. - AsyncHermesRuntime(bool veryLazy = false); - ~AsyncHermesRuntime(); - - std::shared_ptr runtime() { - return runtime_; - } - - /** - * stop sets the stop flag on this instance. JS scripts can get the current - * value of the stop flag by calling the global shouldStop() function. - */ - void stop(); - - /** - * start unsets the stop flag on this instance. JS scripts can get the current - * value of the stop flag by calling the global shouldStop() function. - */ - void start(); - - /** - * hasStoredValue returns whether or not a value has been stored yet - */ - bool hasStoredValue(); - - /** - * awaitStoredValue is a helper for getStoredValue that returns the value - * synchronously rather than in a future. - */ - jsi::Value awaitStoredValue( - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - /** - * tickleJsAsync evaluates '__tickleJs()' in the underlying Hermes runtime on - * a separate thread. - */ - void tickleJsAsync(); - - /** - * executeScriptAsync evaluates JS in the underlying Hermes runtime on a - * separate thread. - * - * This method should be called at most once during the lifetime of an - * AsyncHermesRuntime instance. - */ - void executeScriptAsync( - const std::string &str, - const std::string &url = kDefaultUrl, - facebook::hermes::HermesRuntime::DebugFlags flags = - facebook::hermes::HermesRuntime::DebugFlags{}); - - /** - * executeScriptSync evaluates JS in the underlying Hermes runtime on a - * separate thread. It will block the caller until execution completes. If - * this takes longer than \p timeout, an exception will be thrown. - */ - void executeScriptSync( - const std::string &script, - const std::string &url = kDefaultUrl, - facebook::hermes::HermesRuntime::DebugFlags flags = - facebook::hermes::HermesRuntime::DebugFlags{}, - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - /// Evaluates the given bytecode in the underlying Hermes runtime on a - /// separate thread. - /// \param bytecode Bytecode compiled with compileJS() API - /// \param url Corresponding source URL - void evaluateBytecodeAsync( - const std::string &bytecode, - const std::string &url = "url"); - - /** - * wait blocks until all previous executeScriptAsync calls finish. - */ - void wait( - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - /** - * returns the number of thrown exceptions. - */ - size_t getNumberOfExceptions(); - - /** - * returns the message of the last thrown exception. - */ - std::string getLastThrownExceptionMessage(); - - /** - * registers the runtime for profiling in the executor thread. - */ - void registerForProfilingInExecutor(); - - /** - * unregisters the runtime for profiling in the executor thread. - */ - void unregisterForProfilingInExecutor(); - - private: - jsi::Value shouldStop( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count); - - jsi::Value storeValue( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count); - - std::shared_ptr runtime_; - std::unique_ptr<::hermes::SerialExecutor> executor_; - std::atomic stopFlag_{}; - std::promise storedValue_; - bool hasStoredValue_{false}; - std::vector thrownExceptions_; -}; - -/// RAII-style class dealing with sampling profiler registration in tests. This -/// is especially important in tests -- if any test failure is caused by an -/// uncaught exception, stack unwinding will destroy a VM registered for -/// profiling in a thread that's not the one where registration happened, which -/// will lead to a hermes fatal error. Using this RAII class ensure that the -/// proper test failure cause is reported. -struct SamplingProfilerRAII { - explicit SamplingProfilerRAII(AsyncHermesRuntime &rt) : runtime_(rt) { - runtime_.registerForProfilingInExecutor(); - } - - ~SamplingProfilerRAII() { - runtime_.unregisterForProfilingInExecutor(); - } - - AsyncHermesRuntime &runtime_; -}; -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/SyncConnection.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/SyncConnection.h deleted file mode 100644 index d9ecc509..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/SyncConnection.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -#include "AsyncHermesRuntime.h" - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { - -class ExecutorRuntimeAdapter - : public facebook::hermes::inspector_modern::RuntimeAdapter { - public: - explicit ExecutorRuntimeAdapter(AsyncHermesRuntime &runtime) - : runtime_(runtime) {} - - virtual ~ExecutorRuntimeAdapter() override = default; - - HermesRuntime &getRuntime() override { - return *runtime_.runtime(); - } - - void tickleJs() override; - - private: - AsyncHermesRuntime &runtime_; -}; - -/** - * SyncConnection provides a synchronous interface over Connection that is - * useful in tests. - */ -class SyncConnection { - public: - explicit SyncConnection( - AsyncHermesRuntime &runtime, - bool waitForDebugger = false); - ~SyncConnection(); - - /// sends a message to the debugger - void send(const std::string &str); - - /// waits for the next message of either kind (response or notification) - /// from the debugger. returns the message. throws on timeout. - std::string waitForMessage( - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - bool registerCallbacks(); - bool unregisterCallbacks(); - - /// \return True if onUnregister was called in a previous unregisterCallbacks - /// call. A registerCallbacks call will reset the status. - bool onUnregisterWasCalled(); - - private: - /// This function is given to the CDPHandler to receive replies in the form of - /// CDP messages - void onReply(const std::string &message); - - /// This function is given to the CDPHandler to be invoked upon - /// unregisterCallbacks call - void onUnregister(); - - std::shared_ptr cdpHandler_; - - bool onUnregisterCalled_ = false; - - std::mutex mutex_; - std::condition_variable hasMessage_; - std::queue messages_; -}; - -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/TestHelpers.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/TestHelpers.h deleted file mode 100644 index 2f0e0399..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/inspector/chrome/tests/TestHelpers.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector_modern { -namespace chrome { - -using namespace ::hermes::parser; - -inline JSONValue *mustParseStr(const std::string &str, JSONFactory &factory) { - std::optional v = parseStr(str, factory); - EXPECT_TRUE(v.has_value()); - return v.value(); -} - -inline JSONObject *mustParseStrAsJsonObj( - const std::string &str, - JSONFactory &factory) { - std::optional obj = parseStrAsJsonObj(str, factory); - EXPECT_TRUE(obj.has_value()); - return obj.value(); -} - -template -T mustMake(const JSONObject *obj) { - std::unique_ptr instance = T::tryMake(obj); - EXPECT_TRUE(instance != nullptr); - return std::move(*instance); -} - -namespace message { - -inline std::unique_ptr mustGetRequestFromJson(const std::string &str) { - std::unique_ptr req = Request::fromJson(str); - EXPECT_TRUE(req != nullptr); - return req; -} - -} // namespace message - -} // namespace chrome -} // namespace inspector_modern -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/napi/node_api.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/napi/node_api.h new file mode 100644 index 00000000..46dbb02b --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/napi/node_api.h @@ -0,0 +1,265 @@ +#ifndef SRC_NODE_API_H_ +#define SRC_NODE_API_H_ + +#if defined(BUILDING_NODE_EXTENSION) && !defined(NAPI_EXTERN) +#ifdef _WIN32 +// Building native addon against node +#define NAPI_EXTERN __declspec(dllimport) +#elif defined(__wasm__) +#define NAPI_EXTERN __attribute__((__import_module__("napi"))) +#endif +#endif +#include "js_native_api.h" +#include "node_api_types.h" + +struct uv_loop_s; // Forward declaration. + +#ifdef _WIN32 +#define NAPI_MODULE_EXPORT __declspec(dllexport) +#else +#ifdef __EMSCRIPTEN__ +#define NAPI_MODULE_EXPORT \ + __attribute__((visibility("default"))) __attribute__((used)) +#else +#define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) +#endif +#endif + +#if defined(__GNUC__) +#define NAPI_NO_RETURN __attribute__((noreturn)) +#elif defined(_WIN32) +#define NAPI_NO_RETURN __declspec(noreturn) +#else +#define NAPI_NO_RETURN +#endif + +// Used by deprecated registration method napi_module_register. +typedef struct napi_module { + int nm_version; + unsigned int nm_flags; + const char* nm_filename; + napi_addon_register_func nm_register_func; + const char* nm_modname; + void* nm_priv; + void* reserved[4]; +} napi_module; + +#define NAPI_MODULE_VERSION 1 + +#define NAPI_MODULE_INITIALIZER_X(base, version) \ + NAPI_MODULE_INITIALIZER_X_HELPER(base, version) +#define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version + +#ifdef __wasm__ +#define NAPI_MODULE_INITIALIZER_BASE napi_register_wasm_v +#else +#define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v +#endif + +#define NODE_API_MODULE_GET_API_VERSION_BASE node_api_module_get_api_version_v + +#define NAPI_MODULE_INITIALIZER \ + NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, NAPI_MODULE_VERSION) + +#define NODE_API_MODULE_GET_API_VERSION \ + NAPI_MODULE_INITIALIZER_X(NODE_API_MODULE_GET_API_VERSION_BASE, \ + NAPI_MODULE_VERSION) + +#define NAPI_MODULE_INIT() \ + EXTERN_C_START \ + NAPI_MODULE_EXPORT int32_t NODE_API_MODULE_GET_API_VERSION(void) { \ + return NAPI_VERSION; \ + } \ + NAPI_MODULE_EXPORT napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ + napi_value exports); \ + EXTERN_C_END \ + napi_value NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports) + +#define NAPI_MODULE(modname, regfunc) \ + NAPI_MODULE_INIT() { return regfunc(env, exports); } + +// Deprecated. Use NAPI_MODULE. +#define NAPI_MODULE_X(modname, regfunc, priv, flags) \ + NAPI_MODULE(modname, regfunc) + +EXTERN_C_START + +// Deprecated. Replaced by symbol-based registration defined by NAPI_MODULE +// and NAPI_MODULE_INIT macros. +NAPI_EXTERN void NAPI_CDECL +napi_module_register(napi_module* mod); + +NAPI_EXTERN NAPI_NO_RETURN void NAPI_CDECL +napi_fatal_error(const char* location, + size_t location_len, + const char* message, + size_t message_len); + +// Methods for custom handling of async operations +NAPI_EXTERN napi_status NAPI_CDECL +napi_async_init(napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_context* result); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_async_destroy(napi_env env, napi_async_context async_context); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_make_callback(napi_env env, + napi_async_context async_context, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); + +// Methods to provide node::Buffer functionality with napi types +NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer(napi_env env, + size_t length, + void** data, + napi_value* result); +#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED +NAPI_EXTERN napi_status NAPI_CDECL +napi_create_external_buffer(napi_env env, + size_t length, + void* data, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_value* result); +#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED + +#if NAPI_VERSION >= 10 + +NAPI_EXTERN napi_status NAPI_CDECL +node_api_create_buffer_from_arraybuffer(napi_env env, + napi_value arraybuffer, + size_t byte_offset, + size_t byte_length, + napi_value* result); +#endif // NAPI_VERSION >= 10 + +NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env, + size_t length, + const void* data, + void** result_data, + napi_value* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_is_buffer(napi_env env, + napi_value value, + bool* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_get_buffer_info(napi_env env, + napi_value value, + void** data, + size_t* length); + +// Methods to manage simple async operations +NAPI_EXTERN napi_status NAPI_CDECL +napi_create_async_work(napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_delete_async_work(napi_env env, + napi_async_work work); +NAPI_EXTERN napi_status NAPI_CDECL napi_queue_async_work(node_api_basic_env env, + napi_async_work work); +NAPI_EXTERN napi_status NAPI_CDECL +napi_cancel_async_work(node_api_basic_env env, napi_async_work work); + +// version management +NAPI_EXTERN napi_status NAPI_CDECL napi_get_node_version( + node_api_basic_env env, const napi_node_version** version); + +#if NAPI_VERSION >= 2 + +// Return the current libuv event loop for a given environment +NAPI_EXTERN napi_status NAPI_CDECL +napi_get_uv_event_loop(node_api_basic_env env, struct uv_loop_s** loop); + +#endif // NAPI_VERSION >= 2 + +#if NAPI_VERSION >= 3 + +NAPI_EXTERN napi_status NAPI_CDECL napi_fatal_exception(napi_env env, + napi_value err); + +NAPI_EXTERN napi_status NAPI_CDECL napi_add_env_cleanup_hook( + node_api_basic_env env, napi_cleanup_hook fun, void* arg); + +NAPI_EXTERN napi_status NAPI_CDECL napi_remove_env_cleanup_hook( + node_api_basic_env env, napi_cleanup_hook fun, void* arg); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_open_callback_scope(napi_env env, + napi_value resource_object, + napi_async_context context, + napi_callback_scope* result); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_close_callback_scope(napi_env env, napi_callback_scope scope); + +#endif // NAPI_VERSION >= 3 + +#if NAPI_VERSION >= 4 + +// Calling into JS from other threads +NAPI_EXTERN napi_status NAPI_CDECL +napi_create_threadsafe_function(napi_env env, + napi_value func, + napi_value async_resource, + napi_value async_resource_name, + size_t max_queue_size, + size_t initial_thread_count, + void* thread_finalize_data, + napi_finalize thread_finalize_cb, + void* context, + napi_threadsafe_function_call_js call_js_cb, + napi_threadsafe_function* result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_get_threadsafe_function_context( + napi_threadsafe_function func, void** result); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_call_threadsafe_function(napi_threadsafe_function func, + void* data, + napi_threadsafe_function_call_mode is_blocking); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_acquire_threadsafe_function(napi_threadsafe_function func); + +NAPI_EXTERN napi_status NAPI_CDECL napi_release_threadsafe_function( + napi_threadsafe_function func, napi_threadsafe_function_release_mode mode); + +NAPI_EXTERN napi_status NAPI_CDECL napi_unref_threadsafe_function( + node_api_basic_env env, napi_threadsafe_function func); + +NAPI_EXTERN napi_status NAPI_CDECL napi_ref_threadsafe_function( + node_api_basic_env env, napi_threadsafe_function func); + +#endif // NAPI_VERSION >= 4 + +#if NAPI_VERSION >= 8 + +NAPI_EXTERN napi_status NAPI_CDECL +napi_add_async_cleanup_hook(node_api_basic_env env, + napi_async_cleanup_hook hook, + void* arg, + napi_async_cleanup_hook_handle* remove_handle); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle); + +#endif // NAPI_VERSION >= 8 + +#if NAPI_VERSION >= 9 + +NAPI_EXTERN napi_status NAPI_CDECL +node_api_get_module_file_name(node_api_basic_env env, const char** result); + +#endif // NAPI_VERSION >= 9 + +EXTERN_C_END + +#endif // SRC_NODE_API_H_ diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/napi/node_api_types.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/napi/node_api_types.h new file mode 100644 index 00000000..2580f15c --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/napi/node_api_types.h @@ -0,0 +1,56 @@ +#ifndef SRC_NODE_API_TYPES_H_ +#define SRC_NODE_API_TYPES_H_ + +#include "js_native_api_types.h" + +typedef napi_value(NAPI_CDECL* napi_addon_register_func)(napi_env env, + napi_value exports); +typedef int32_t(NAPI_CDECL* node_api_addon_get_api_version_func)(void); + +typedef struct napi_callback_scope__* napi_callback_scope; +typedef struct napi_async_context__* napi_async_context; +typedef struct napi_async_work__* napi_async_work; + +#if NAPI_VERSION >= 3 +typedef void(NAPI_CDECL* napi_cleanup_hook)(void* arg); +#endif // NAPI_VERSION >= 3 + +#if NAPI_VERSION >= 4 +typedef struct napi_threadsafe_function__* napi_threadsafe_function; +#endif // NAPI_VERSION >= 4 + +#if NAPI_VERSION >= 4 +typedef enum { + napi_tsfn_release, + napi_tsfn_abort +} napi_threadsafe_function_release_mode; + +typedef enum { + napi_tsfn_nonblocking, + napi_tsfn_blocking +} napi_threadsafe_function_call_mode; +#endif // NAPI_VERSION >= 4 + +typedef void(NAPI_CDECL* napi_async_execute_callback)(napi_env env, void* data); +typedef void(NAPI_CDECL* napi_async_complete_callback)(napi_env env, + napi_status status, + void* data); +#if NAPI_VERSION >= 4 +typedef void(NAPI_CDECL* napi_threadsafe_function_call_js)( + napi_env env, napi_value js_callback, void* context, void* data); +#endif // NAPI_VERSION >= 4 + +typedef struct { + uint32_t major; + uint32_t minor; + uint32_t patch; + const char* release; +} napi_node_version; + +#if NAPI_VERSION >= 8 +typedef struct napi_async_cleanup_hook_handle__* napi_async_cleanup_hook_handle; +typedef void(NAPI_CDECL* napi_async_cleanup_hook)( + napi_async_cleanup_hook_handle handle, void* data); +#endif // NAPI_VERSION >= 8 + +#endif // SRC_NODE_API_TYPES_H_ diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/synthtest/tests/TestFunctions.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/synthtest/tests/TestFunctions.h deleted file mode 100644 index 48099473..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/hermes/synthtest/tests/TestFunctions.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_API_SYNTHTEST_TESTS_TESTFUNCTIONS -#define HERMES_API_SYNTHTEST_TESTS_TESTFUNCTIONS - -#define FOREACH_TEST(F) \ - F(callbacksCallJSFunction) \ - F(globalReturnObject) \ - F(getPropertyNames) \ - F(hostCallsJS) \ - F(hostCallsJSCallsHost) \ - F(hostCallsJSWithThis) \ - F(hostFunctionCachesObject) \ - F(hostFunctionCreatesObjects) \ - F(hostFunctionMutatesGlobalObject) \ - F(hostFunctionMutatesObject) \ - F(hostFunctionNameAndParams) \ - F(hostFunctionReturn) \ - F(hostFunctionReturnArgument) \ - F(hostFunctionReturnThis) \ - F(hostGlobalObject) \ - F(nativePropertyNames) \ - F(nativeSetsConstant) \ - F(parseGCConfig) \ - F(partialTraceHostFunction) \ - F(partialTraceHostObjectGet) \ - F(partialTraceHostObjectSet) \ - F(surrogatePairString) - -#define TEST_FUNC_FORWARD_DECL(name) \ - const char *name##Trace(); \ - const char *name##Source(); - -namespace facebook { -namespace hermes { -namespace synthtest { - -// Forward decls for all of the functions used. -FOREACH_TEST(TEST_FUNC_FORWARD_DECL) - -} // namespace synthtest -} // namespace hermes -} // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/HermesABIHelpers.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/HermesABIHelpers.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/HermesABIHelpers.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/HermesABIHelpers.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/HermesABIRuntimeWrapper.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/HermesABIRuntimeWrapper.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/HermesABIRuntimeWrapper.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/HermesABIRuntimeWrapper.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/hermes_abi.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/hermes_abi.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/hermes_abi.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/hermes_abi.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/hermes_vtable.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/hermes_vtable.h similarity index 90% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/hermes_vtable.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/hermes_vtable.h index 5adeeb36..5b2645d5 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_abi/hermes_vtable.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_abi/hermes_vtable.h @@ -20,8 +20,7 @@ __declspec(dllexport) #else // _MSC_VER __attribute__((visibility("default"))) #endif // _MSC_VER -const struct HermesABIVTable * -get_hermes_abi_vtable(); +const struct HermesABIVTable *get_hermes_abi_vtable(); #ifdef __cplusplus } diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/HermesSandboxRuntime.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/HermesSandboxRuntime.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/HermesSandboxRuntime.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/HermesSandboxRuntime.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_compiled.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_compiled.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_compiled.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_compiled.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled-impl.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled-impl.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled-impl.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled-impl.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_dbg_compiled.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled-impl.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled-impl.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled-impl.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled-impl.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/hermes_sandbox_impl_opt_compiled.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/wasm-rt-fb.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/wasm-rt-fb.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/wasm-rt-fb.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/wasm-rt-fb.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/wasm-rt-impl.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/wasm-rt-impl.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/wasm-rt-impl.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/wasm-rt-impl.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/wasm-rt.h b/test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/wasm-rt.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes_sandbox/external/wasm-rt.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/hermes_sandbox/external/wasm-rt.h diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/JSIDynamic.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/JSIDynamic.h index a96cc281..d022b639 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/JSIDynamic.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/JSIDynamic.h @@ -20,7 +20,7 @@ facebook::jsi::Value valueFromDynamic( folly::dynamic dynamicFromValue( facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, - std::function filterObjectKeys = nullptr); + const std::function& filterObjectKeys = nullptr); } // namespace jsi } // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/decorator.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/decorator.h index c0d3cc6d..f101c183 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/decorator.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/decorator.h @@ -112,6 +112,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { return plain_; } + ICast* castInterface(const UUID& interfaceUUID) override { + return plain().castInterface(interfaceUUID); + } + Value evaluateJavaScript( const std::shared_ptr& buffer, const std::string& sourceURL) override { @@ -137,10 +141,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { } std::string description() override { return plain().description(); - }; + } bool isInspectable() override { return plain().isInspectable(); - }; + } Instrumentation& instrumentation() override { return *this; } @@ -156,41 +160,45 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { return plain_.cloneSymbol(pv); - }; + } Runtime::PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override { return plain_.cloneBigInt(pv); - }; + } Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { return plain_.cloneString(pv); - }; + } Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { return plain_.cloneObject(pv); - }; + } Runtime::PointerValue* clonePropNameID( const Runtime::PointerValue* pv) override { return plain_.clonePropNameID(pv); - }; + } PropNameID createPropNameIDFromAscii(const char* str, size_t length) override { return plain_.createPropNameIDFromAscii(str, length); - }; + } PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) override { return plain_.createPropNameIDFromUtf8(utf8, length); - }; + } PropNameID createPropNameIDFromString(const String& str) override { return plain_.createPropNameIDFromString(str); - }; + } + PropNameID createPropNameIDFromUtf16(const char16_t* utf16, size_t length) + override { + return plain_.createPropNameIDFromUtf16(utf16, length); + } PropNameID createPropNameIDFromSymbol(const Symbol& sym) override { return plain_.createPropNameIDFromSymbol(sym); - }; + } std::string utf8(const PropNameID& id) override { return plain_.utf8(id); - }; + } bool compare(const PropNameID& a, const PropNameID& b) override { return plain_.compare(a, b); - }; + } std::string symbolToString(const Symbol& sym) override { return plain_.symbolToString(sym); @@ -217,10 +225,13 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { String createStringFromAscii(const char* str, size_t length) override { return plain_.createStringFromAscii(str, length); - }; + } String createStringFromUtf8(const uint8_t* utf8, size_t length) override { return plain_.createStringFromUtf8(utf8, length); - }; + } + String createStringFromUtf16(const char16_t* utf16, size_t length) override { + return plain_.createStringFromUtf16(utf16, length); + } std::string utf8(const String& s) override { return plain_.utf8(s); } @@ -232,25 +243,44 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { return plain_.utf16(sym); } + void getStringData( + const jsi::String& str, + void* ctx, + void ( + *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { + plain_.getStringData(str, ctx, cb); + } + + void getPropNameIdData( + const jsi::PropNameID& sym, + void* ctx, + void ( + *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { + plain_.getPropNameIdData(sym, ctx, cb); + } + + Object createObjectWithPrototype(const Value& prototype) override { + return plain_.createObjectWithPrototype(prototype); + } + Object createObject() override { return plain_.createObject(); - }; + } Object createObject(std::shared_ptr ho) override { return plain_.createObject( std::make_shared(*this, std::move(ho))); - }; + } std::shared_ptr getHostObject(const jsi::Object& o) override { std::shared_ptr dho = plain_.getHostObject(o); return static_cast(*dho).plainHO_; - }; - -// HostFunctionType& getHostFunction(const jsi::Function& f) override { -// HostFunctionType& dhf = plain_.getHostFunction(f); -// // This will fail if a cpp file including this header is not compiled -// // with RTTI. -// return dhf.target()->plainHF_; -// }; + } + HostFunctionType& getHostFunction(const jsi::Function& f) override { + HostFunctionType& dhf = plain_.getHostFunction(f); + // This will fail if a cpp file including this header is not compiled + // with RTTI. + return dhf.target()->plainHF_; + } bool hasNativeState(const Object& o) override { return plain_.hasNativeState(o); @@ -267,78 +297,121 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { plain_.setExternalMemoryPressure(obj, amt); } + void setPrototypeOf(const Object& object, const Value& prototype) override { + plain_.setPrototypeOf(object, prototype); + } + + Value getPrototypeOf(const Object& object) override { + return plain_.getPrototypeOf(object); + } + Value getProperty(const Object& o, const PropNameID& name) override { return plain_.getProperty(o, name); - }; + } Value getProperty(const Object& o, const String& name) override { return plain_.getProperty(o, name); - }; + } + Value getProperty(const Object& o, const Value& name) override { + return plain_.getProperty(o, name); + } bool hasProperty(const Object& o, const PropNameID& name) override { return plain_.hasProperty(o, name); - }; + } bool hasProperty(const Object& o, const String& name) override { return plain_.hasProperty(o, name); - }; + } + bool hasProperty(const Object& o, const Value& name) override { + return plain_.hasProperty(o, name); + } void setPropertyValue( const Object& o, const PropNameID& name, const Value& value) override { plain_.setPropertyValue(o, name, value); - }; + } void setPropertyValue(const Object& o, const String& name, const Value& value) override { plain_.setPropertyValue(o, name, value); - }; + } + void setPropertyValue(const Object& o, const Value& name, const Value& value) + override { + plain_.setPropertyValue(o, name, value); + } + + void deleteProperty(const Object& object, const PropNameID& name) override { + plain_.deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const String& name) override { + plain_.deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const Value& name) override { + plain_.deleteProperty(object, name); + } bool isArray(const Object& o) const override { return plain_.isArray(o); - }; + } bool isArrayBuffer(const Object& o) const override { return plain_.isArrayBuffer(o); - }; + } + bool isTypedArray(const Object& o) const override { + return plain_.isTypedArray(o); + } + bool isUint8Array(const Object& o) const override { + return plain_.isUint8Array(o); + } bool isFunction(const Object& o) const override { return plain_.isFunction(o); - }; + } bool isHostObject(const jsi::Object& o) const override { return plain_.isHostObject(o); - }; + } bool isHostFunction(const jsi::Function& f) const override { return plain_.isHostFunction(f); - }; + } Array getPropertyNames(const Object& o) override { return plain_.getPropertyNames(o); - }; + } WeakObject createWeakObject(const Object& o) override { return plain_.createWeakObject(o); - }; + } Value lockWeakObject(const WeakObject& wo) override { return plain_.lockWeakObject(wo); - }; + } Array createArray(size_t length) override { return plain_.createArray(length); - }; + } ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override { return plain_.createArrayBuffer(std::move(buffer)); - }; + } size_t size(const Array& a) override { return plain_.size(a); - }; + } size_t size(const ArrayBuffer& ab) override { return plain_.size(ab); - }; + } uint8_t* data(const ArrayBuffer& ab) override { return plain_.data(ab); - }; + } + bool detached(const ArrayBuffer& ab) override { + return plain_.detached(ab); + } Value getValueAtIndex(const Array& a, size_t i) override { return plain_.getValueAtIndex(a, i); - }; + } void setValueAtIndexImpl(const Array& a, size_t i, const Value& value) override { plain_.setValueAtIndexImpl(a, i, value); - }; + } + + size_t push(const Array& a, const Value* elements, size_t count) override { + return plain_.push(a, elements, count); + } Function createFunctionFromHostFunction( const PropNameID& name, @@ -346,18 +419,58 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { HostFunctionType func) override { return plain_.createFunctionFromHostFunction( name, paramCount, DecoratedHostFunction(*this, std::move(func))); - }; + } Value call( const Function& f, const Value& jsThis, const Value* args, size_t count) override { return plain_.call(f, jsThis, args, count); - }; + } Value callAsConstructor(const Function& f, const Value* args, size_t count) override { return plain_.callAsConstructor(f, args, count); - }; + } + + void setRuntimeDataImpl( + const UUID& uuid, + const void* data, + void (*deleter)(const void* data)) override { + return plain_.setRuntimeDataImpl(uuid, data, deleter); + } + + const void* getRuntimeDataImpl(const UUID& uuid) override { + return plain_.getRuntimeDataImpl(uuid); + } + + std::shared_ptr tryGetMutableBuffer( + const jsi::ArrayBuffer& arrayBuffer) override { + return plain_.tryGetMutableBuffer(arrayBuffer); + } + + ArrayBuffer buffer(const TypedArray& typedArray) override { + return plain_.buffer(typedArray); + } + size_t byteOffset(const TypedArray& typedArray) override { + return plain_.byteOffset(typedArray); + } + size_t byteLength(const TypedArray& typedArray) override { + return plain_.byteLength(typedArray); + } + size_t length(const TypedArray& typedArray) override { + return plain_.length(typedArray); + } + + Uint8Array createUint8Array(size_t length) override { + return plain_.createUint8Array(length); + } + + Uint8Array createUint8Array( + const ArrayBuffer& buffer, + size_t offset, + size_t length) override { + return plain_.createUint8Array(buffer, offset, length); + } // Private data for managing scopes. Runtime::ScopeState* pushScope() override { @@ -369,20 +482,20 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { bool strictEquals(const Symbol& a, const Symbol& b) const override { return plain_.strictEquals(a, b); - }; + } bool strictEquals(const BigInt& a, const BigInt& b) const override { return plain_.strictEquals(a, b); - }; + } bool strictEquals(const String& a, const String& b) const override { return plain_.strictEquals(a, b); - }; + } bool strictEquals(const Object& a, const Object& b) const override { return plain_.strictEquals(a, b); - }; + } bool instanceOf(const Object& o, const Function& f) override { return plain_.instanceOf(o, f); - }; + } // jsi::Instrumentation methods @@ -445,6 +558,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { .writeBasicBlockProfileTraceToFile(fileName); } + void dumpOpcodeStats(std::ostream& os) const override { + const_cast(plain()).instrumentation().dumpOpcodeStats(os); + } + /// Dump external profiler symbols to the given file name. void dumpProfilerSymbolsToFile(const std::string& fileName) const override { const_cast(plain()).instrumentation().dumpProfilerSymbolsToFile( @@ -542,6 +659,11 @@ class WithRuntimeDecorator : public RuntimeDecorator { // the derived class. WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {} + ICast* castInterface(const UUID& interfaceUUID) override { + Around around{with_}; + return RD::castInterface(interfaceUUID); + } + Value evaluateJavaScript( const std::shared_ptr& buffer, const std::string& sourceURL) override { @@ -574,11 +696,11 @@ class WithRuntimeDecorator : public RuntimeDecorator { std::string description() override { Around around{with_}; return RD::description(); - }; + } bool isInspectable() override { Around around{with_}; return RD::isInspectable(); - }; + } // The jsi:: prefix is necessary because MSVC compiler complains C2247: // Instrumentation is not accessible because RuntimeDecorator uses private @@ -593,90 +715,99 @@ class WithRuntimeDecorator : public RuntimeDecorator { Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { Around around{with_}; return RD::cloneSymbol(pv); - }; + } Runtime::PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override { Around around{with_}; return RD::cloneBigInt(pv); - }; + } Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { Around around{with_}; return RD::cloneString(pv); - }; + } Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { Around around{with_}; return RD::cloneObject(pv); - }; + } Runtime::PointerValue* clonePropNameID( const Runtime::PointerValue* pv) override { Around around{with_}; return RD::clonePropNameID(pv); - }; + } PropNameID createPropNameIDFromAscii(const char* str, size_t length) override { Around around{with_}; return RD::createPropNameIDFromAscii(str, length); - }; + } PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) override { Around around{with_}; return RD::createPropNameIDFromUtf8(utf8, length); - }; + } + PropNameID createPropNameIDFromUtf16(const char16_t* utf16, size_t length) + override { + Around around{with_}; + return RD::createPropNameIDFromUtf16(utf16, length); + } PropNameID createPropNameIDFromString(const String& str) override { Around around{with_}; return RD::createPropNameIDFromString(str); - }; + } PropNameID createPropNameIDFromSymbol(const Symbol& sym) override { Around around{with_}; return RD::createPropNameIDFromSymbol(sym); - }; + } std::string utf8(const PropNameID& id) override { Around around{with_}; return RD::utf8(id); - }; + } bool compare(const PropNameID& a, const PropNameID& b) override { Around around{with_}; return RD::compare(a, b); - }; + } std::string symbolToString(const Symbol& sym) override { Around around{with_}; return RD::symbolToString(sym); - }; + } BigInt createBigIntFromInt64(int64_t i) override { Around around{with_}; return RD::createBigIntFromInt64(i); - }; + } BigInt createBigIntFromUint64(uint64_t i) override { Around around{with_}; return RD::createBigIntFromUint64(i); - }; + } bool bigintIsInt64(const BigInt& bi) override { Around around{with_}; return RD::bigintIsInt64(bi); - }; + } bool bigintIsUint64(const BigInt& bi) override { Around around{with_}; return RD::bigintIsUint64(bi); - }; + } uint64_t truncate(const BigInt& bi) override { Around around{with_}; return RD::truncate(bi); - }; + } String bigintToString(const BigInt& bi, int i) override { Around around{with_}; return RD::bigintToString(bi, i); - }; + } String createStringFromAscii(const char* str, size_t length) override { Around around{with_}; return RD::createStringFromAscii(str, length); - }; + } String createStringFromUtf8(const uint8_t* utf8, size_t length) override { Around around{with_}; return RD::createStringFromUtf8(utf8, length); - }; + } + String createStringFromUtf16(const char16_t* utf16, size_t length) override { + Around around{with_}; + return RD::createStringFromUtf16(utf16, length); + } std::string utf8(const String& s) override { Around around{with_}; return RD::utf8(s); @@ -691,134 +822,211 @@ class WithRuntimeDecorator : public RuntimeDecorator { return RD::utf16(sym); } + void getStringData( + const jsi::String& str, + void* ctx, + void ( + *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { + Around around{with_}; + RD::getStringData(str, ctx, cb); + } + + void getPropNameIdData( + const jsi::PropNameID& sym, + void* ctx, + void ( + *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { + Around around{with_}; + RD::getPropNameIdData(sym, ctx, cb); + } + Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override { Around around{with_}; return RD::createValueFromJsonUtf8(json, length); - }; + } + + Object createObjectWithPrototype(const Value& prototype) override { + Around around{with_}; + return RD::createObjectWithPrototype(prototype); + } Object createObject() override { Around around{with_}; return RD::createObject(); - }; + } Object createObject(std::shared_ptr ho) override { Around around{with_}; return RD::createObject(std::move(ho)); - }; + } std::shared_ptr getHostObject(const jsi::Object& o) override { Around around{with_}; return RD::getHostObject(o); - }; + } HostFunctionType& getHostFunction(const jsi::Function& f) override { Around around{with_}; return RD::getHostFunction(f); - }; + } bool hasNativeState(const Object& o) override { Around around{with_}; return RD::hasNativeState(o); - }; + } std::shared_ptr getNativeState(const Object& o) override { Around around{with_}; return RD::getNativeState(o); - }; + } void setNativeState(const Object& o, std::shared_ptr state) override { Around around{with_}; RD::setNativeState(o, state); - }; + } + + void setPrototypeOf(const Object& object, const Value& prototype) override { + Around around{with_}; + RD::setPrototypeOf(object, prototype); + } + + Value getPrototypeOf(const Object& object) override { + Around around{with_}; + return RD::getPrototypeOf(object); + } Value getProperty(const Object& o, const PropNameID& name) override { Around around{with_}; return RD::getProperty(o, name); - }; + } Value getProperty(const Object& o, const String& name) override { Around around{with_}; return RD::getProperty(o, name); - }; + } + Value getProperty(const Object& o, const Value& name) override { + Around around{with_}; + return RD::getProperty(o, name); + } bool hasProperty(const Object& o, const PropNameID& name) override { Around around{with_}; return RD::hasProperty(o, name); - }; + } bool hasProperty(const Object& o, const String& name) override { Around around{with_}; return RD::hasProperty(o, name); - }; + } + bool hasProperty(const Object& o, const Value& name) override { + Around around{with_}; + return RD::hasProperty(o, name); + } void setPropertyValue( const Object& o, const PropNameID& name, const Value& value) override { Around around{with_}; RD::setPropertyValue(o, name, value); - }; + } void setPropertyValue(const Object& o, const String& name, const Value& value) override { Around around{with_}; RD::setPropertyValue(o, name, value); - }; + } + void setPropertyValue(const Object& o, const Value& name, const Value& value) + override { + Around around{with_}; + RD::setPropertyValue(o, name, value); + } + + void deleteProperty(const Object& object, const PropNameID& name) override { + Around around{with_}; + RD::deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const String& name) override { + Around around{with_}; + RD::deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const Value& name) override { + Around around{with_}; + RD::deleteProperty(object, name); + } bool isArray(const Object& o) const override { Around around{with_}; return RD::isArray(o); - }; + } bool isArrayBuffer(const Object& o) const override { Around around{with_}; return RD::isArrayBuffer(o); - }; + } + bool isTypedArray(const Object& o) const override { + Around around{with_}; + return RD::isTypedArray(o); + } + bool isUint8Array(const Object& o) const override { + Around around{with_}; + return RD::isUint8Array(o); + } bool isFunction(const Object& o) const override { Around around{with_}; return RD::isFunction(o); - }; + } bool isHostObject(const jsi::Object& o) const override { Around around{with_}; return RD::isHostObject(o); - }; + } bool isHostFunction(const jsi::Function& f) const override { Around around{with_}; return RD::isHostFunction(f); - }; + } Array getPropertyNames(const Object& o) override { Around around{with_}; return RD::getPropertyNames(o); - }; + } WeakObject createWeakObject(const Object& o) override { Around around{with_}; return RD::createWeakObject(o); - }; + } Value lockWeakObject(const WeakObject& wo) override { Around around{with_}; return RD::lockWeakObject(wo); - }; + } Array createArray(size_t length) override { Around around{with_}; return RD::createArray(length); - }; + } ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override { return RD::createArrayBuffer(std::move(buffer)); - }; + } size_t size(const Array& a) override { Around around{with_}; return RD::size(a); - }; + } size_t size(const ArrayBuffer& ab) override { Around around{with_}; return RD::size(ab); - }; + } uint8_t* data(const ArrayBuffer& ab) override { Around around{with_}; return RD::data(ab); - }; + } + bool detached(const ArrayBuffer& ab) override { + Around around{with_}; + return RD::detached(ab); + } Value getValueAtIndex(const Array& a, size_t i) override { Around around{with_}; return RD::getValueAtIndex(a, i); - }; + } void setValueAtIndexImpl(const Array& a, size_t i, const Value& value) override { Around around{with_}; RD::setValueAtIndexImpl(a, i, value); - }; + } + size_t push(const Array& a, const Value* elements, size_t count) override { + Around around{with_}; + return RD::push(a, elements, count); + } Function createFunctionFromHostFunction( const PropNameID& name, @@ -827,7 +1035,7 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; return RD::createFunctionFromHostFunction( name, paramCount, std::move(func)); - }; + } Value call( const Function& f, const Value& jsThis, @@ -835,12 +1043,47 @@ class WithRuntimeDecorator : public RuntimeDecorator { size_t count) override { Around around{with_}; return RD::call(f, jsThis, args, count); - }; + } Value callAsConstructor(const Function& f, const Value* args, size_t count) override { Around around{with_}; return RD::callAsConstructor(f, args, count); - }; + } + + std::shared_ptr tryGetMutableBuffer( + const jsi::ArrayBuffer& arrayBuffer) override { + Around around{with_}; + return RD::tryGetMutableBuffer(arrayBuffer); + } + + ArrayBuffer buffer(const TypedArray& typedArray) override { + Around around{with_}; + return RD::buffer(typedArray); + } + size_t byteOffset(const TypedArray& typedArray) override { + Around around{with_}; + return RD::byteOffset(typedArray); + } + size_t byteLength(const TypedArray& typedArray) override { + Around around{with_}; + return RD::byteLength(typedArray); + } + size_t length(const TypedArray& typedArray) override { + Around around{with_}; + return RD::length(typedArray); + } + + Uint8Array createUint8Array(size_t length) override { + Around around{with_}; + return RD::createUint8Array(length); + } + Uint8Array createUint8Array( + const ArrayBuffer& buffer, + size_t offset, + size_t length) override { + Around around{with_}; + return RD::createUint8Array(buffer, offset, length); + } // Private data for managing scopes. Runtime::ScopeState* pushScope() override { @@ -855,31 +1098,44 @@ class WithRuntimeDecorator : public RuntimeDecorator { bool strictEquals(const Symbol& a, const Symbol& b) const override { Around around{with_}; return RD::strictEquals(a, b); - }; + } bool strictEquals(const BigInt& a, const BigInt& b) const override { Around around{with_}; return RD::strictEquals(a, b); - }; + } bool strictEquals(const String& a, const String& b) const override { Around around{with_}; return RD::strictEquals(a, b); - }; + } bool strictEquals(const Object& a, const Object& b) const override { Around around{with_}; return RD::strictEquals(a, b); - }; + } bool instanceOf(const Object& o, const Function& f) override { Around around{with_}; return RD::instanceOf(o, f); - }; + } void setExternalMemoryPressure(const jsi::Object& obj, size_t amount) override { Around around{with_}; RD::setExternalMemoryPressure(obj, amount); - }; + } + + void setRuntimeDataImpl( + const UUID& uuid, + const void* data, + void (*deleter)(const void* data)) override { + Around around{with_}; + RD::setRuntimeDataImpl(uuid, data, deleter); + } + + const void* getRuntimeDataImpl(const UUID& uuid) override { + Around around{with_}; + return RD::getRuntimeDataImpl(uuid); + } private: // Wrap an RAII type around With& to guarantee after always happens. diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/hermes.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/hermes-interfaces.h similarity index 74% rename from test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/hermes.h rename to test-app/runtime/src/main/cpp/napi/hermes/include/jsi/hermes-interfaces.h index 364a645a..4655aa8c 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/hermes.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/hermes-interfaces.h @@ -26,6 +26,59 @@ namespace debugger { class Debugger; } +/// IEventLoopControl is defined by the integrator to allow the Runtime to +/// schedule some task to be run when convenient, and to keep track of "Task +/// sources". After it is set to a Runtime, the integrator must ensure that the +/// `IEventLoopControl` instance outlives the Runtime. The IEventLoopControl +/// methods may be called by the Runtime from any thread, so they must be +/// thread-safe and cannot perform any VM operations. +struct IEventLoopControl { + /// `scheduleTask` is a function used by the caller (the Runtime) to schedule + /// some \p task. The scheduled task may perform VM operations. Thus, the + /// integrator must only run the tasks when it had exclusive access to the + /// Runtime. + virtual void scheduleTask(const std::function& task) = 0; + /// Used by the caller (the Runtime) to register a new source that can + /// schedule new work via `scheduleTask`. This method return an uint64 + /// identifying the source registered. As an example, a WebWorker instance may + /// schedule a message-processing task via `scheduleTask`, and thus is + /// considered as a "task queue source". The Runtime may register each Worker + /// using this method. This is useful for the integrator to keep track of + /// active "sources". + virtual uint64_t registerTaskQueueSource() = 0; + /// Used by the caller to unregister a source when it is not allowed to invoke + /// `scheduleTasks` anymore. The source is identified by \p sourceId, which is + /// provided when the source was originally register in + /// `registerTaskQueueSource`. As an example, after WebWorker instance is + /// terminated, it will not schedule more tasks. The Runtime may unregister + /// the Worker instance, and the integrator may exit the event-loop if there + /// are no more active sources. + virtual void unregisterTaskQueueSource(uint64_t sourceId) = 0; + + protected: + ~IEventLoopControl() = default; +}; + +/// Interface for setting the IEventLoopControl in the Runtime. +struct JSI_EXPORT ISetEventLoopControl : public jsi::ICast { + public: + static constexpr jsi::UUID uuid{ + 0x7b6902e6, + 0xfd38, + 0x11f0, + 0x8de9, + 0x0242ac120002}; + + /// Configures the eventloop control mechanism using \p eventLoopControl. + virtual void setEventLoopControl(IEventLoopControl* eventLoopControl) = 0; + /// Retrieves the IEventLoopControl if it was set previously. Otherwise, + /// return nullptr. + virtual IEventLoopControl* getEventLoopControl() = 0; + + protected: + ~ISetEventLoopControl() = default; +}; + /// Interface for Hermes-specific runtime methods.The actual implementations of /// the pure virtual methods are provided by Hermes API. class JSI_EXPORT IHermes : public jsi::ICast { diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/instrumentation.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/instrumentation.h index 726858cc..4a88951f 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/instrumentation.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/instrumentation.h @@ -121,6 +121,9 @@ class JSI_EXPORT Instrumentation { virtual void writeBasicBlockProfileTraceToFile( const std::string& fileName) const = 0; + /// Write the opcode stats to the given stream. + virtual void dumpOpcodeStats(std::ostream& os) const = 0; + /// Dump external profiler symbols to the given file name. virtual void dumpProfilerSymbolsToFile(const std::string& fileName) const = 0; }; diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi-inl.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi-inl.h index 111a4702..6a29f647 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi-inl.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi-inl.h @@ -11,48 +11,48 @@ namespace facebook { namespace jsi { namespace detail { -inline Value toValue(Runtime&, std::nullptr_t) { +inline Value toValue(IRuntime&, std::nullptr_t) { return Value::null(); } -inline Value toValue(Runtime&, bool b) { +inline Value toValue(IRuntime&, bool b) { return Value(b); } -inline Value toValue(Runtime&, double d) { +inline Value toValue(IRuntime&, double d) { return Value(d); } -inline Value toValue(Runtime&, float f) { +inline Value toValue(IRuntime&, float f) { return Value(static_cast(f)); } -inline Value toValue(Runtime&, int i) { +inline Value toValue(IRuntime&, int i) { return Value(i); } -inline Value toValue(Runtime& runtime, const char* str) { +inline Value toValue(IRuntime& runtime, const char* str) { return String::createFromAscii(runtime, str); } -inline Value toValue(Runtime& runtime, const std::string& str) { +inline Value toValue(IRuntime& runtime, const std::string& str) { return String::createFromUtf8(runtime, str); } template -inline Value toValue(Runtime& runtime, const T& other) { +inline Value toValue(IRuntime& runtime, const T& other) { static_assert( std::is_base_of::value, "This type cannot be converted to Value"); return Value(runtime, other); } -inline Value toValue(Runtime& runtime, const Value& value) { +inline Value toValue(IRuntime& runtime, const Value& value) { return Value(runtime, value); } -inline Value&& toValue(Runtime&, Value&& value) { +inline Value&& toValue(IRuntime&, Value&& value) { return std::move(value); } -inline PropNameID toPropNameID(Runtime& runtime, const char* name) { +inline PropNameID toPropNameID(IRuntime& runtime, const char* name) { return PropNameID::forAscii(runtime, name); } -inline PropNameID toPropNameID(Runtime& runtime, const std::string& name) { +inline PropNameID toPropNameID(IRuntime& runtime, const std::string& name) { return PropNameID::forUtf8(runtime, name); } -inline PropNameID&& toPropNameID(Runtime&, PropNameID&& name) { +inline PropNameID&& toPropNameID(IRuntime&, PropNameID&& name) { return std::move(name); } @@ -84,59 +84,110 @@ inline const Runtime::PointerValue* Runtime::getPointerValue( return value.data_.pointer.ptr_; } -inline Value Object::getProperty(Runtime& runtime, const char* name) const { +inline void Runtime::setRuntimeData( + const UUID& dataUUID, + const std::shared_ptr& data) { + auto* dataPtr = new std::shared_ptr(data); + setRuntimeDataImpl(dataUUID, dataPtr, [](const void* data) { + delete (const std::shared_ptr*)data; + }); +} + +inline std::shared_ptr Runtime::getRuntimeData(const UUID& dataUUID) { + auto* data = (const std::shared_ptr*)getRuntimeDataImpl(dataUUID); + return data ? *data : nullptr; +} + +Value Object::getPrototype(IRuntime& runtime) const { + return runtime.getPrototypeOf(*this); +} + +inline Value Object::getProperty(IRuntime& runtime, const char* name) const { return getProperty(runtime, String::createFromAscii(runtime, name)); } -inline Value Object::getProperty(Runtime& runtime, const String& name) const { +inline Value Object::getProperty(IRuntime& runtime, const String& name) const { return runtime.getProperty(*this, name); } -inline Value Object::getProperty(Runtime& runtime, const PropNameID& name) +inline Value Object::getProperty(IRuntime& runtime, const PropNameID& name) const { return runtime.getProperty(*this, name); } -inline bool Object::hasProperty(Runtime& runtime, const char* name) const { +inline Value Object::getProperty(IRuntime& runtime, const Value& name) const { + return runtime.getProperty(*this, name); +} + +inline bool Object::hasProperty(IRuntime& runtime, const char* name) const { return hasProperty(runtime, String::createFromAscii(runtime, name)); } -inline bool Object::hasProperty(Runtime& runtime, const String& name) const { +inline bool Object::hasProperty(IRuntime& runtime, const String& name) const { return runtime.hasProperty(*this, name); } -inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name) +inline bool Object::hasProperty(IRuntime& runtime, const PropNameID& name) const { return runtime.hasProperty(*this, name); } +inline bool Object::hasProperty(IRuntime& runtime, const Value& name) const { + return runtime.hasProperty(*this, name); +} + template -void Object::setProperty(Runtime& runtime, const char* name, T&& value) const { +void Object::setProperty(IRuntime& runtime, const char* name, T&& value) const { setProperty( runtime, String::createFromAscii(runtime, name), std::forward(value)); } template -void Object::setProperty(Runtime& runtime, const String& name, T&& value) +void Object::setProperty(IRuntime& runtime, const String& name, T&& value) + const { + setPropertyValue( + runtime, name, detail::toValue(runtime, std::forward(value))); +} + +template +void Object::setProperty(IRuntime& runtime, const PropNameID& name, T&& value) const { setPropertyValue( runtime, name, detail::toValue(runtime, std::forward(value))); } template -void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value) +void Object::setProperty(IRuntime& runtime, const Value& name, T&& value) const { setPropertyValue( runtime, name, detail::toValue(runtime, std::forward(value))); } -inline Array Object::getArray(Runtime& runtime) const& { +inline void Object::deleteProperty(IRuntime& runtime, const char* name) const { + deleteProperty(runtime, String::createFromAscii(runtime, name)); +} + +inline void Object::deleteProperty(IRuntime& runtime, const String& name) + const { + runtime.deleteProperty(*this, name); +} + +inline void Object::deleteProperty(IRuntime& runtime, const PropNameID& name) + const { + runtime.deleteProperty(*this, name); +} + +inline void Object::deleteProperty(IRuntime& runtime, const Value& name) const { + runtime.deleteProperty(*this, name); +} + +inline Array Object::getArray(IRuntime& runtime) const& { assert(runtime.isArray(*this)); (void)runtime; // when assert is disabled we need to mark this as used return Array(runtime.cloneObject(ptr_)); } -inline Array Object::getArray(Runtime& runtime) && { +inline Array Object::getArray(IRuntime& runtime) && { assert(runtime.isArray(*this)); (void)runtime; // when assert is disabled we need to mark this as used Runtime::PointerValue* value = ptr_; @@ -144,13 +195,13 @@ inline Array Object::getArray(Runtime& runtime) && { return Array(value); } -inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const& { +inline ArrayBuffer Object::getArrayBuffer(IRuntime& runtime) const& { assert(runtime.isArrayBuffer(*this)); (void)runtime; // when assert is disabled we need to mark this as used return ArrayBuffer(runtime.cloneObject(ptr_)); } -inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && { +inline ArrayBuffer Object::getArrayBuffer(IRuntime& runtime) && { assert(runtime.isArrayBuffer(*this)); (void)runtime; // when assert is disabled we need to mark this as used Runtime::PointerValue* value = ptr_; @@ -158,12 +209,22 @@ inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && { return ArrayBuffer(value); } -inline Function Object::getFunction(Runtime& runtime) const& { +inline TypedArray Object::getTypedArray(IRuntime& runtime) const& { + assert(runtime.isTypedArray(*this)); + return TypedArray(runtime.cloneObject(ptr_)); +} + +inline Uint8Array Object::getUint8Array(IRuntime& runtime) const& { + assert(runtime.isUint8Array(*this)); + return Uint8Array(runtime.cloneObject(ptr_)); +} + +inline Function Object::getFunction(IRuntime& runtime) const& { assert(runtime.isFunction(*this)); return Function(runtime.cloneObject(ptr_)); } -inline Function Object::getFunction(Runtime& runtime) && { +inline Function Object::getFunction(IRuntime& runtime) && { assert(runtime.isFunction(*this)); (void)runtime; // when assert is disabled we need to mark this as used Runtime::PointerValue* value = ptr_; @@ -172,24 +233,24 @@ inline Function Object::getFunction(Runtime& runtime) && { } template -inline bool Object::isHostObject(Runtime& runtime) const { +inline bool Object::isHostObject(IRuntime& runtime) const { return runtime.isHostObject(*this) && std::dynamic_pointer_cast(runtime.getHostObject(*this)); } template <> -inline bool Object::isHostObject(Runtime& runtime) const { +inline bool Object::isHostObject(IRuntime& runtime) const { return runtime.isHostObject(*this); } template -inline std::shared_ptr Object::getHostObject(Runtime& runtime) const { +inline std::shared_ptr Object::getHostObject(IRuntime& runtime) const { assert(isHostObject(runtime)); return std::static_pointer_cast(runtime.getHostObject(*this)); } template -inline std::shared_ptr Object::asHostObject(Runtime& runtime) const { +inline std::shared_ptr Object::asHostObject(IRuntime& runtime) const { if (!isHostObject(runtime)) { detail::throwOrDie( "Object is not a HostObject of desired type"); @@ -199,59 +260,59 @@ inline std::shared_ptr Object::asHostObject(Runtime& runtime) const { template <> inline std::shared_ptr Object::getHostObject( - Runtime& runtime) const { + IRuntime& runtime) const { assert(runtime.isHostObject(*this)); return runtime.getHostObject(*this); } template -inline bool Object::hasNativeState(Runtime& runtime) const { +inline bool Object::hasNativeState(IRuntime& runtime) const { return runtime.hasNativeState(*this) && std::dynamic_pointer_cast(runtime.getNativeState(*this)); } template <> -inline bool Object::hasNativeState(Runtime& runtime) const { +inline bool Object::hasNativeState(IRuntime& runtime) const { return runtime.hasNativeState(*this); } template -inline std::shared_ptr Object::getNativeState(Runtime& runtime) const { +inline std::shared_ptr Object::getNativeState(IRuntime& runtime) const { assert(hasNativeState(runtime)); return std::static_pointer_cast(runtime.getNativeState(*this)); } inline void Object::setNativeState( - Runtime& runtime, + IRuntime& runtime, std::shared_ptr state) const { runtime.setNativeState(*this, state); } -inline void Object::setExternalMemoryPressure(Runtime& runtime, size_t amt) +inline void Object::setExternalMemoryPressure(IRuntime& runtime, size_t amt) const { runtime.setExternalMemoryPressure(*this, amt); } -inline Array Object::getPropertyNames(Runtime& runtime) const { +inline Array Object::getPropertyNames(IRuntime& runtime) const { return runtime.getPropertyNames(*this); } -inline Value WeakObject::lock(Runtime& runtime) const { +inline Value WeakObject::lock(IRuntime& runtime) const { return runtime.lockWeakObject(*this); } template -void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value) const { +void Array::setValueAtIndex(IRuntime& runtime, size_t i, T&& value) const { setValueAtIndexImpl( runtime, i, detail::toValue(runtime, std::forward(value))); } -inline Value Array::getValueAtIndex(Runtime& runtime, size_t i) const { +inline Value Array::getValueAtIndex(IRuntime& runtime, size_t i) const { return runtime.getValueAtIndex(*this, i); } inline Function Function::createFromHostFunction( - Runtime& runtime, + IRuntime& runtime, const jsi::PropNameID& name, unsigned int paramCount, jsi::HostFunctionType func) { @@ -259,18 +320,19 @@ inline Function Function::createFromHostFunction( name, paramCount, std::move(func)); } -inline Value Function::call(Runtime& runtime, const Value* args, size_t count) +inline Value Function::call(IRuntime& runtime, const Value* args, size_t count) const { return runtime.call(*this, Value::undefined(), args, count); } -inline Value Function::call(Runtime& runtime, std::initializer_list args) - const { +inline Value Function::call( + IRuntime& runtime, + std::initializer_list args) const { return call(runtime, args.begin(), args.size()); } template -inline Value Function::call(Runtime& runtime, Args&&... args) const { +inline Value Function::call(IRuntime& runtime, Args&&... args) const { // A more awesome version of this would be able to create raw values // which can be used directly without wrapping and unwrapping, but // this will do for now. @@ -278,7 +340,7 @@ inline Value Function::call(Runtime& runtime, Args&&... args) const { } inline Value Function::callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, const Value* args, size_t count) const { @@ -286,7 +348,7 @@ inline Value Function::callWithThis( } inline Value Function::callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, std::initializer_list args) const { return callWithThis(runtime, jsThis, args.begin(), args.size()); @@ -294,7 +356,7 @@ inline Value Function::callWithThis( template inline Value Function::callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, Args&&... args) const { // A more awesome version of this would be able to create raw values @@ -305,14 +367,30 @@ inline Value Function::callWithThis( } template -inline Array Array::createWithElements(Runtime& runtime, Args&&... args) { +inline Array Array::createWithElements(IRuntime& runtime, Args&&... args) { return createWithElements( runtime, {detail::toValue(runtime, std::forward(args))...}); } +template +inline size_t Array::push(IRuntime& runtime, Args&&... args) { + return push(runtime, {detail::toValue(runtime, std::forward(args))...}); +} + +inline size_t Array::push( + IRuntime& runtime, + std::initializer_list elements) { + return push(runtime, elements.begin(), elements.size()); +} + +inline size_t +Array::push(IRuntime& runtime, const Value* elements, size_t count) { + return runtime.push(*this, elements, count); +} + template inline std::vector PropNameID::names( - Runtime& runtime, + IRuntime& runtime, Args&&... args) { return names({detail::toPropNameID(runtime, std::forward(args))...}); } @@ -329,26 +407,26 @@ inline std::vector PropNameID::names( } inline Value Function::callAsConstructor( - Runtime& runtime, + IRuntime& runtime, const Value* args, size_t count) const { return runtime.callAsConstructor(*this, args, count); } inline Value Function::callAsConstructor( - Runtime& runtime, + IRuntime& runtime, std::initializer_list args) const { return callAsConstructor(runtime, args.begin(), args.size()); } template -inline Value Function::callAsConstructor(Runtime& runtime, Args&&... args) +inline Value Function::callAsConstructor(IRuntime& runtime, Args&&... args) const { return callAsConstructor( runtime, {detail::toValue(runtime, std::forward(args))...}); } -String BigInt::toString(Runtime& runtime, int radix) const { +String BigInt::toString(IRuntime& runtime, int radix) const { return runtime.bigintToString(*this, radix); } diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi.h index be48bb82..bf98028f 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/jsi.h @@ -8,6 +8,8 @@ #pragma once #include +#include +#include #include #include #include @@ -31,6 +33,107 @@ class FBJSRuntime; namespace facebook { namespace jsi { +/// UUID version 1 implementation. This should be constructed with constant +/// arguments to identify fixed UUIDs. +class JSI_EXPORT UUID { + public: + // Construct from raw parts + constexpr UUID( + uint32_t timeLow, + uint16_t timeMid, + uint16_t timeHighAndVersion, + uint16_t variantAndClockSeq, + uint64_t node) + : high( + ((uint64_t)(timeLow) << 32) | ((uint64_t)(timeMid) << 16) | + ((uint64_t)(timeHighAndVersion))), + low(((uint64_t)(variantAndClockSeq) << 48) | node) {} + + // Default constructor (zero UUID) + constexpr UUID() : high(0), low(0) {} + + constexpr UUID(const UUID&) = default; + constexpr UUID& operator=(const UUID&) = default; + + constexpr bool operator==(const UUID& other) const { + return high == other.high && low == other.low; + } + constexpr bool operator!=(const UUID& other) const { + return !(*this == other); + } + + // Ordering (for std::map, sorting, etc.) + constexpr bool operator<(const UUID& other) const { + return (high < other.high) || (high == other.high && low < other.low); + } + + // Hash support for UUID (for unordered_map compatibility) + struct Hash { + std::size_t operator()(const UUID& uuid) const noexcept { + return std::hash{}(uuid.high) ^ + (std::hash{}(uuid.low) << 1); + } + }; + + // UUID format: 8-4-4-4-12 + std::string toString() const { + std::string buffer(36, ' '); + std::snprintf( + buffer.data(), + buffer.size() + 1, + "%08x-%04x-%04x-%04x-%012llx", + getTimeLow(), + getTimeMid(), + getTimeHighAndVersion(), + getVariantAndClockSeq(), + (unsigned long long)getNode()); + return buffer; + } + + constexpr uint32_t getTimeLow() const { + return (uint32_t)(high >> 32); + } + + constexpr uint16_t getTimeMid() const { + return (uint16_t)(high >> 16); + } + + constexpr uint16_t getTimeHighAndVersion() const { + return (uint16_t)high; + } + + constexpr uint16_t getVariantAndClockSeq() const { + return (uint16_t)(low >> 48); + } + + constexpr uint64_t getNode() const { + return low & 0xFFFFFFFFFFFF; + } + + private: + uint64_t high; + uint64_t low; +}; + +/// Base interface that all JSI interfaces inherit from. Users should not try to +/// manipulate this base type directly, and should use castInterface to get the +/// appropriate subtype. +struct JSI_EXPORT ICast { + /// If the current object can be cast into the interface specified by \p + /// interfaceUUID, return a pointer to the object. Otherwise, return a null + /// pointer. + /// The returned interface has the same lifetime as the underlying object. It + /// does not need to be released when not needed. + virtual ICast* castInterface(const UUID& interfaceUUID) = 0; + + protected: + /// Interfaces are not destructible, thus the destructor is intentionally + /// protected to prevent delete calls on the interface. + /// Additionally, the destructor is non-virtual to reduce the vtable + /// complexity from inheritance. + ~ICast() = default; +}; + /// Base class for buffers of data or bytecode that need to be passed to the /// runtime. The buffer is expected to be fully immutable, so the result of /// size(), data(), and the contents of the pointer returned by data() must not @@ -80,6 +183,7 @@ class JSI_EXPORT PreparedJavaScript { virtual ~PreparedJavaScript() = 0; }; +class IRuntime; class Runtime; class Pointer; class PropNameID; @@ -96,6 +200,8 @@ class Instrumentation; class Scope; class JSIException; class JSError; +class TypedArray; +class Uint8Array; /// A function which has this type can be registered as a function /// callable from JavaScript using Function::createFromHostFunction(). @@ -119,7 +225,7 @@ class JSI_EXPORT HostObject { // object. (This may be as late as when the Runtime is shut down.) // You have no control over which thread it is called on. This will // be called from inside the GC, so it is unsafe to do any VM - // operations which require a Runtime&. Derived classes' dtors + // operations which require a IRuntime&. Derived classes' dtors // should also avoid doing anything expensive. Calling the dtor on // a jsi object is explicitly ok. If you want to do JS operations, // or any nontrivial work, you should add it to a work queue, and @@ -151,27 +257,92 @@ class JSI_EXPORT NativeState { virtual ~NativeState(); }; -/// Represents a JS runtime. Movable, but not copyable. Note that -/// this object may not be thread-aware, but cannot be used safely from -/// multiple threads at once. The application is responsible for -/// ensuring that it is used safely. This could mean using the -/// Runtime from a single thread, using a mutex, doing all work on a -/// serial queue, etc. This restriction applies to the methods of -/// this class, and any method in the API which take a Runtime& as an -/// argument. Destructors (all but ~Scope), operators, or other methods -/// which do not take Runtime& as an argument are safe to call from any -/// thread, but it is still forbidden to make write operations on a single -/// instance of any class from more than one thread. In addition, to -/// make shutdown safe, destruction of objects associated with the Runtime -/// must be destroyed before the Runtime is destroyed, or from the -/// destructor of a managed HostObject or HostFunction. Informally, this -/// means that the main source of unsafe behavior is to hold a jsi object -/// in a non-Runtime-managed object, and not clean it up before the Runtime -/// is shut down. If your lifecycle is such that avoiding this is hard, -/// you will probably need to do use your own locks. -class JSI_EXPORT Runtime { +// JSI_UNSTABLE gates features that will be released with a Hermes version in +// the future. Until released, these features may be subject to change. After +// release, these features will be moved out of JSI_UNSTABLE and become frozen. +#ifdef JSI_UNSTABLE +/// Opaque class that is used to store serialized object from a runtime. The +/// lifetime of this object is orthogonal to the original runtime object, and +/// may outlive the original object. +class JSI_EXPORT Serialized { + public: + /// Uses \p secretAddr to validate if the Serialized data is supported. If so, + /// return the pointer to the underlying serialized data. Otherwise, return a + /// nullptr. This should be used by the runtime to deserialize the data. + virtual void* getPrivate(const void* secretAddr) = 0; + virtual ~Serialized(); +}; + +/// Provides a set of APIs that allows copying objects between different +/// runtime instances. The runtimes instances must be of the same type. As an +/// example, a serialized object from Hermes runtime may only be deserialized by +/// another Hermes runtime. +class JSI_EXPORT ISerialization : public ICast { public: - virtual ~Runtime(); + static constexpr jsi::UUID uuid{ + 0xd40fe0ec, + 0xa47c, + 0x42c9, + 0x8c09, + 0x661aeab832d8}; + + /// Serializes the given Value \p value using the structured clone algorithm. + /// It returns a shared pointer of an opaque Serialized object that can be + /// deserialized multiple times. The lifetime of the Serialized object is not + /// tied to the lifetime of the original object. + virtual std::shared_ptr serialize(const Value& value) = 0; + + /// Given a Serialized object provided by \p serialized, deserialize it using + /// the structured clone algorithm into a JS value in the current runtime. + /// Returns the deserialized JS value. + virtual Value deserialize(const std::shared_ptr& serialized) = 0; + + /// Serializes the given jsi::Value \p value using the structured clone + /// algorithm. \p transferList must be a JS Array. Given the length property + /// of \p transferList, this API will transfer everything at index [0, length + /// - 1] to the serialized object. The transferred values will no longer be + /// usable in the original runtime. It returns a unique pointer of an opaque + /// Serialized object that can be deserialized once only by + /// deserializeWithTransfer. The lifetime of the Serialized object is not tied + /// to the lifetime of the original object. + virtual std::unique_ptr serializeWithTransfer( + const Value& value, + const Array& transferList) = 0; + + /// Using the structure clone algorithm, deserialize the object provided by \p + /// serialized into a JS value in the current runtime. \p serialized must be + /// created by serializeWithTransfer. If the current runtime does not support + /// the serialization scheme in \p serialized, then this method will throw and + /// \p serialized will remain unmodified. Otherwise, this will consume the + /// serialized data entirely and make the serialized objects in the current + /// runtime. Any transferred values in the serialized object will be owned by + /// the current runtime. + /// This method returns an Array containing the deserialized values, where + /// the first element is the value passed into serializeWithTransfer, + /// followed by all transferred values. + virtual Array deserializeWithTransfer( + std::unique_ptr& serialized) = 0; + + protected: + ~ISerialization() = default; +}; + +#endif // JSI_UNSTABLE + +/// An interface that provides various functionalities of the JS runtime. +/// The APIs must not be called from multiple threads concurrently. It is the +/// user's responsibility ensure thread safety when using IRuntime. +/// Users should cast their runtime to IRuntime to access these APIs. However, +/// for backward compatibility, these APIs are also accessible via the Runtime +/// object directly. +class JSI_EXPORT IRuntime : public ICast { + public: + static constexpr jsi::UUID uuid{ + 0xc2e8e22e, + 0xd7a6, + 0x11f0, + 0x8de9, + 0x0242ac120002}; /// Evaluates the given JavaScript \c buffer. \c sourceURL is used /// to annotate the stack trace if there is an exception. The @@ -265,22 +436,31 @@ class JSI_EXPORT Runtime { /// \return an interface to extract metrics from this \c Runtime. The default /// implementation of this function returns an \c Instrumentation instance /// which returns no metrics. - virtual Instrumentation& instrumentation(); - - protected: - friend class Pointer; - friend class PropNameID; - friend class Symbol; - friend class BigInt; - friend class String; - friend class Object; - friend class WeakObject; - friend class Array; - friend class ArrayBuffer; - friend class Function; - friend class Value; - friend class Scope; - friend class JSError; + virtual Instrumentation& instrumentation() = 0; + + /// Stores the pointer \p data with the \p dataUUID in the runtime. This can + /// be used to store some custom data within the runtime. When the runtime is + /// destroyed, or if an entry at an existing key is overwritten, the runtime + /// will release its ownership of the held object. + virtual void setRuntimeData( + const UUID& dataUUID, + const std::shared_ptr& data) = 0; + /// Returns the data associated with the \p uuid in the runtime. If there's no + /// data associated with the uuid, return a null pointer. + virtual std::shared_ptr getRuntimeData(const UUID& dataUUID) = 0; + + /// Stores the pointer \p data with the \p uuid in the runtime. This can be + /// used to store some custom data within the runtime. When the runtime is + /// destroyed, or if an entry at an existing key is overwritten, the runtime + /// will release its ownership by calling \p deleter. + virtual void setRuntimeDataImpl( + const UUID& dataUUID, + const void* data, + void (*deleter)(const void* data)) = 0; + + /// Returns the data associated with the \p uuid in the runtime. If there's no + /// data associated with the uuid, return a null pointer. + virtual const void* getRuntimeDataImpl(const UUID& dataUUID) = 0; // Potential optimization: avoid the cloneFoo() virtual dispatch, // and instead just fix the number of fields, and copy them, since @@ -294,11 +474,11 @@ class JSI_EXPORT Runtime { virtual ~PointerValue() = default; }; - virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneBigInt(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* cloneSymbol(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* cloneBigInt(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* cloneString(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* cloneObject(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* clonePropNameID(const IRuntime::PointerValue* pv) = 0; virtual PropNameID createPropNameIDFromAscii( const char* str, @@ -306,6 +486,9 @@ class JSI_EXPORT Runtime { virtual PropNameID createPropNameIDFromUtf8( const uint8_t* utf8, size_t length) = 0; + virtual PropNameID createPropNameIDFromUtf16( + const char16_t* utf16, + size_t length) = 0; virtual PropNameID createPropNameIDFromString(const String& str) = 0; virtual PropNameID createPropNameIDFromSymbol(const Symbol& sym) = 0; virtual std::string utf8(const PropNameID&) = 0; @@ -322,36 +505,55 @@ class JSI_EXPORT Runtime { virtual String createStringFromAscii(const char* str, size_t length) = 0; virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; + virtual String createStringFromUtf16( + const char16_t* utf16, + size_t length) = 0; virtual std::string utf8(const String&) = 0; // \return a \c Value created from a utf8-encoded JSON string. The default // implementation creates a \c String and invokes JSON.parse. - virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length); + virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length) = 0; virtual Object createObject() = 0; virtual Object createObject(std::shared_ptr ho) = 0; virtual std::shared_ptr getHostObject(const jsi::Object&) = 0; virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0; + // Creates a new Object with the custom prototype + virtual Object createObjectWithPrototype(const Value& prototype) = 0; + virtual bool hasNativeState(const jsi::Object&) = 0; virtual std::shared_ptr getNativeState(const jsi::Object&) = 0; virtual void setNativeState( const jsi::Object&, std::shared_ptr state) = 0; + virtual void setPrototypeOf(const Object& object, const Value& prototype) = 0; + virtual Value getPrototypeOf(const Object& object) = 0; + virtual Value getProperty(const Object&, const PropNameID& name) = 0; virtual Value getProperty(const Object&, const String& name) = 0; + virtual Value getProperty(const Object&, const Value& name) = 0; virtual bool hasProperty(const Object&, const PropNameID& name) = 0; virtual bool hasProperty(const Object&, const String& name) = 0; + virtual bool hasProperty(const Object&, const Value& name) = 0; virtual void setPropertyValue( const Object&, const PropNameID& name, const Value& value) = 0; virtual void setPropertyValue(const Object&, const String& name, const Value& value) = 0; + virtual void + setPropertyValue(const Object&, const Value& name, const Value& value) = 0; + + virtual void deleteProperty(const Object&, const PropNameID& name) = 0; + virtual void deleteProperty(const Object&, const String& name) = 0; + virtual void deleteProperty(const Object&, const Value& name) = 0; virtual bool isArray(const Object&) const = 0; virtual bool isArrayBuffer(const Object&) const = 0; + virtual bool isTypedArray(const Object&) const = 0; + virtual bool isUint8Array(const Object&) const = 0; virtual bool isFunction(const Object&) const = 0; virtual bool isHostObject(const jsi::Object&) const = 0; virtual bool isHostFunction(const jsi::Function&) const = 0; @@ -366,9 +568,11 @@ class JSI_EXPORT Runtime { virtual size_t size(const Array&) = 0; virtual size_t size(const ArrayBuffer&) = 0; virtual uint8_t* data(const ArrayBuffer&) = 0; + virtual bool detached(const ArrayBuffer&) = 0; virtual Value getValueAtIndex(const Array&, size_t i) = 0; virtual void setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0; + virtual size_t push(const Array&, const Value*, size_t) = 0; virtual Function createFunctionFromHostFunction( const PropNameID& name, @@ -384,8 +588,8 @@ class JSI_EXPORT Runtime { // Private data for managing scopes. struct ScopeState; - virtual ScopeState* pushScope(); - virtual void popScope(ScopeState*); + virtual ScopeState* pushScope() = 0; + virtual void popScope(ScopeState*) = 0; virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0; virtual bool strictEquals(const BigInt& a, const BigInt& b) const = 0; @@ -399,8 +603,210 @@ class JSI_EXPORT Runtime { const jsi::Object& obj, size_t amount) = 0; - virtual std::u16string utf16(const String& str); - virtual std::u16string utf16(const PropNameID& sym); + virtual std::u16string utf16(const String& str) = 0; + virtual std::u16string utf16(const PropNameID& sym) = 0; + + /// Invokes the provided callback \p cb with the String content in \p str. + /// The callback must take in three arguments: bool ascii, const void* data, + /// and size_t num, respectively. \p ascii indicates whether the \p data + /// passed to the callback should be interpreted as a pointer to a sequence of + /// \p num ASCII characters or UTF16 characters. Depending on the internal + /// representation of the string, the function may invoke the callback + /// multiple times, with a different format on each invocation. The callback + /// must not access runtime functionality, as any operation on the runtime may + /// invalidate the data pointers. + virtual void getStringData( + const jsi::String& str, + void* ctx, + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) = 0; + + /// Invokes the provided callback \p cb with the PropNameID content in \p sym. + /// The callback must take in three arguments: bool ascii, const void* data, + /// and size_t num, respectively. \p ascii indicates whether the \p data + /// passed to the callback should be interpreted as a pointer to a sequence of + /// \p num ASCII characters or UTF16 characters. Depending on the internal + /// representation of the string, the function may invoke the callback + /// multiple times, with a different format on each invocation. The callback + /// must not access runtime functionality, as any operation on the runtime may + /// invalidate the data pointers. + virtual void getPropNameIdData( + const jsi::PropNameID& sym, + void* ctx, + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) = 0; + + /// If possible, returns the MutableBuffer representing \p arrayBuffer's + /// underlying data, else return a nullptr. Importantly, the returned + /// MutableBuffer directly points to \p arrayBuffer's data instead of copying + /// the data over. The data's lifetime is valid for the lifetime of + /// MutableBuffer, which is orthogonal from \p arrayBuffer. + virtual std::shared_ptr tryGetMutableBuffer( + const jsi::ArrayBuffer& arrayBuffer) = 0; + + /// \return the underlying buffer of the \p typedArray. + virtual ArrayBuffer buffer(const TypedArray& typedArray) = 0; + /// \return the 'byteOffset' property of the \p typedArray. + virtual size_t byteOffset(const TypedArray& typedArray) = 0; + /// \return the 'byteLength' property of the \p typedArray. + virtual size_t byteLength(const TypedArray& typedArray) = 0; + /// \return the 'length; property of the \p typedArray. + virtual size_t length(const TypedArray& typedArray) = 0; + + /// Create a JS UInt8Array with length \p length. + virtual Uint8Array createUint8Array(size_t length) = 0; + /// Create a JS UInt8Array using the ArrayBuffer \p buffer starting at byte + /// offset \p offset and length \p length. + virtual Uint8Array + createUint8Array(const ArrayBuffer& buffer, size_t offset, size_t length) = 0; + + /// Create a new Error object with the message property set to \p message. + virtual Value createError(const String& msg) = 0; + /// Create a new EvalError object with the message property set to \p message. + virtual Value createEvalError(const String& msg) = 0; + /// Create a new RangeError object with the message property set to \p + /// message. + virtual Value createRangeError(const String& msg) = 0; + /// Create a new ReferenceError object with the message property set to \p + /// message. + virtual Value createReferenceError(const String& msg) = 0; + /// Create a new SyntaxError object with the message property set to \p + /// message. + virtual Value createSyntaxError(const String& msg) = 0; + /// Create a new TypeError object with the message property set to \p message. + virtual Value createTypeError(const String& msg) = 0; + /// Create a new URIError object with the message property set to \p message. + virtual Value createURIError(const String& msg) = 0; + + /// Returns the number of code units in the string, equivalent to 'length' + /// property of a JS string. + virtual size_t length(const String& str) = 0; + + protected: + virtual ~IRuntime() = default; +}; + +/// Represents a JS runtime. Movable, but not copyable. Note that +/// this object may not be thread-aware, but cannot be used safely from +/// multiple threads at once. The application is responsible for +/// ensuring that it is used safely. This could mean using the +/// Runtime from a single thread, using a mutex, doing all work on a +/// serial queue, etc. This restriction applies to the methods of +/// this class, and any method in the API which take a Runtime& as an +/// argument. Destructors (all but ~Scope), operators, or other methods +/// which do not take Runtime& as an argument are safe to call from any +/// thread, but it is still forbidden to make write operations on a single +/// instance of any class from more than one thread. In addition, to +/// make shutdown safe, destruction of objects associated with the Runtime +/// must be destroyed before the Runtime is destroyed, or from the +/// destructor of a managed HostObject or HostFunction. Informally, this +/// means that the main source of unsafe behavior is to hold a jsi object +/// in a non-Runtime-managed object, and not clean it up before the Runtime +/// is shut down. If your lifecycle is such that avoiding this is hard, +/// you will probably need to do use your own locks. +class JSI_EXPORT Runtime : public IRuntime { + public: + virtual ~Runtime() override; + + using IRuntime::getProperty; + using IRuntime::hasProperty; + using IRuntime::setPropertyValue; + ICast* castInterface(const UUID& uuid) override; + + Instrumentation& instrumentation() override; + + /// Stores the pointer \p data with the \p uuid in the runtime. This can be + /// used to store some custom data within the runtime. When the runtime is + /// destroyed, or if an entry at an existing key is overwritten, the runtime + /// will release its ownership of the held object. + void setRuntimeData(const UUID& uuid, const std::shared_ptr& data) + override; + + /// Returns the data associated with the \p uuid in the runtime. If there's no + /// data associated with the uuid, return a null pointer. + std::shared_ptr getRuntimeData(const UUID& uuid) override; + + Value getProperty(const Object&, const Value& name) override; + bool hasProperty(const Object&, const Value& name) override; + void setPropertyValue(const Object&, const Value& name, const Value& value) + override; + void deleteProperty(const Object&, const PropNameID& name) override; + void deleteProperty(const Object&, const String& name) override; + void deleteProperty(const Object&, const Value& name) override; + + void setRuntimeDataImpl( + const UUID& uuid, + const void* data, + void (*deleter)(const void* data)) override; + const void* getRuntimeDataImpl(const UUID& uuid) override; + + PropNameID createPropNameIDFromUtf16(const char16_t* utf16, size_t length) + override; + String createStringFromUtf16(const char16_t* utf16, size_t length) override; + + Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override; + + Object createObjectWithPrototype(const Value& prototype) override; + void setPrototypeOf(const Object& object, const Value& prototype) override; + Value getPrototypeOf(const Object& object) override; + + ScopeState* pushScope() override; + void popScope(ScopeState*) override; + + std::u16string utf16(const String& str) override; + std::u16string utf16(const PropNameID& sym) override; + + void getStringData( + const jsi::String& str, + void* ctx, + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) override; + void getPropNameIdData( + const jsi::PropNameID& sym, + void* ctx, + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) override; + + size_t push(const Array&, const Value*, size_t) override; + + std::shared_ptr tryGetMutableBuffer( + const jsi::ArrayBuffer& arrayBuffer) override; + + bool detached(const ArrayBuffer&) override; + + ArrayBuffer buffer(const TypedArray& typedArray) override; + size_t byteOffset(const TypedArray& typedArray) override; + size_t byteLength(const TypedArray& typedArray) override; + size_t length(const TypedArray& typedArray) override; + + bool isTypedArray(const Object&) const override; + bool isUint8Array(const Object&) const override; + Uint8Array createUint8Array(size_t length) override; + Uint8Array createUint8Array( + const ArrayBuffer& buffer, + size_t offset, + size_t length) override; + + Value createError(const String& msg) override; + Value createEvalError(const String& msg) override; + Value createRangeError(const String& msg) override; + Value createReferenceError(const String& msg) override; + Value createSyntaxError(const String& msg) override; + Value createTypeError(const String& msg) override; + Value createURIError(const String& msg) override; + + size_t length(const String& str) override; + + protected: + friend class Pointer; + friend class PropNameID; + friend class Symbol; + friend class BigInt; + friend class String; + friend class Object; + friend class WeakObject; + friend class Array; + friend class ArrayBuffer; + friend class Function; + friend class Value; + friend class Scope; + friend class JSError; // These exist so derived classes can access the private parts of // Value, Symbol, String, and Object, which are all friends of Runtime. @@ -443,7 +849,7 @@ class JSI_EXPORT PropNameID : public Pointer { public: using Pointer::Pointer; - PropNameID(Runtime& runtime, const PropNameID& other) + PropNameID(IRuntime& runtime, const PropNameID& other) : Pointer(runtime.clonePropNameID(other.ptr_)) {} PropNameID(PropNameID&& other) = default; @@ -451,66 +857,98 @@ class JSI_EXPORT PropNameID : public Pointer { /// Create a JS property name id from ascii values. The data is /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) { + static PropNameID + forAscii(IRuntime& runtime, const char* str, size_t length) { return runtime.createPropNameIDFromAscii(str, length); } /// Create a property name id from a nul-terminated C ascii name. The data is /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str) { + static PropNameID forAscii(IRuntime& runtime, const char* str) { return forAscii(runtime, str, strlen(str)); } /// Create a PropNameID from a C++ string. The string is copied. - static PropNameID forAscii(Runtime& runtime, const std::string& str) { + static PropNameID forAscii(IRuntime& runtime, const std::string& str) { return forAscii(runtime, str.c_str(), str.size()); } /// Create a PropNameID from utf8 values. The data is copied. /// Results are undefined if \p utf8 contains invalid code points. static PropNameID - forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + forUtf8(IRuntime& runtime, const uint8_t* utf8, size_t length) { return runtime.createPropNameIDFromUtf8(utf8, length); } /// Create a PropNameID from utf8-encoded octets stored in a /// std::string. The string data is transformed and copied. /// Results are undefined if \p utf8 contains invalid code points. - static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) { + static PropNameID forUtf8(IRuntime& runtime, const std::string& utf8) { return runtime.createPropNameIDFromUtf8( reinterpret_cast(utf8.data()), utf8.size()); } + /// Given a series of UTF-16 encoded code units, create a PropNameId. The + /// input may contain unpaired surrogates, which will be interpreted as a code + /// point of the same value. + static PropNameID + forUtf16(IRuntime& runtime, const char16_t* utf16, size_t length) { + return runtime.createPropNameIDFromUtf16(utf16, length); + } + + /// Given a series of UTF-16 encoded code units stored inside std::u16string, + /// create a PropNameId. The input may contain unpaired surrogates, which + /// will be interpreted as a code point of the same value. + static PropNameID forUtf16(IRuntime& runtime, const std::u16string& str) { + return runtime.createPropNameIDFromUtf16(str.data(), str.size()); + } + /// Create a PropNameID from a JS string. - static PropNameID forString(Runtime& runtime, const jsi::String& str) { + static PropNameID forString(IRuntime& runtime, const jsi::String& str) { return runtime.createPropNameIDFromString(str); } /// Create a PropNameID from a JS symbol. - static PropNameID forSymbol(Runtime& runtime, const jsi::Symbol& sym) { + static PropNameID forSymbol(IRuntime& runtime, const jsi::Symbol& sym) { return runtime.createPropNameIDFromSymbol(sym); } // Creates a vector of PropNameIDs constructed from given arguments. template - static std::vector names(Runtime& runtime, Args&&... args); + static std::vector names(IRuntime& runtime, Args&&... args); // Creates a vector of given PropNameIDs. template static std::vector names(PropNameID (&&propertyNames)[N]); /// Copies the data in a PropNameID as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { + std::string utf8(IRuntime& runtime) const { return runtime.utf8(*this); } /// Copies the data in a PropNameID as utf16 into a C++ string. - std::u16string utf16(Runtime& runtime) const { + std::u16string utf16(IRuntime& runtime) const { return runtime.utf16(*this); } + /// Invokes the user provided callback to process the content in PropNameId. + /// The callback must take in three arguments: bool ascii, const void* data, + /// and size_t num, respectively. \p ascii indicates whether the \p data + /// passed to the callback should be interpreted as a pointer to a sequence of + /// \p num ASCII characters or UTF16 characters. The function may invoke the + /// callback multiple times, with a different format on each invocation. The + /// callback must not access runtime functionality, as any operation on the + /// runtime may invalidate the data pointers. + template + void getPropNameIdData(IRuntime& runtime, CB& cb) const { + runtime.getPropNameIdData( + *this, &cb, [](void* ctx, bool ascii, const void* data, size_t num) { + (*((CB*)ctx))(ascii, data, num); + }); + } + static bool compare( - Runtime& runtime, + IRuntime& runtime, const jsi::PropNameID& a, const jsi::PropNameID& b) { return runtime.compare(a, b); @@ -533,13 +971,14 @@ class JSI_EXPORT Symbol : public Pointer { Symbol& operator=(Symbol&& other) = default; /// \return whether a and b refer to the same symbol. - static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) { + static bool + strictEquals(IRuntime& runtime, const Symbol& a, const Symbol& b) { return runtime.strictEquals(a, b); } /// Converts a Symbol into a C++ string as JS .toString would. The output /// will look like \c Symbol(description) . - std::string toString(Runtime& runtime) const { + std::string toString(IRuntime& runtime) const { return runtime.symbolToString(*this); } @@ -556,51 +995,52 @@ class JSI_EXPORT BigInt : public Pointer { BigInt& operator=(BigInt&& other) = default; /// Create a BigInt representing the signed 64-bit \p value. - static BigInt fromInt64(Runtime& runtime, int64_t value) { + static BigInt fromInt64(IRuntime& runtime, int64_t value) { return runtime.createBigIntFromInt64(value); } /// Create a BigInt representing the unsigned 64-bit \p value. - static BigInt fromUint64(Runtime& runtime, uint64_t value) { + static BigInt fromUint64(IRuntime& runtime, uint64_t value) { return runtime.createBigIntFromUint64(value); } /// \return whether a === b. - static bool strictEquals(Runtime& runtime, const BigInt& a, const BigInt& b) { + static bool + strictEquals(IRuntime& runtime, const BigInt& a, const BigInt& b) { return runtime.strictEquals(a, b); } /// \returns This bigint truncated to a signed 64-bit integer. - int64_t getInt64(Runtime& runtime) const { + int64_t getInt64(IRuntime& runtime) const { return runtime.truncate(*this); } /// \returns Whether this bigint can be losslessly converted to int64_t. - bool isInt64(Runtime& runtime) const { + bool isInt64(IRuntime& runtime) const { return runtime.bigintIsInt64(*this); } /// \returns This bigint truncated to a signed 64-bit integer. Throws a /// JSIException if the truncation is lossy. - int64_t asInt64(Runtime& runtime) const; + int64_t asInt64(IRuntime& runtime) const; /// \returns This bigint truncated to an unsigned 64-bit integer. - uint64_t getUint64(Runtime& runtime) const { + uint64_t getUint64(IRuntime& runtime) const { return runtime.truncate(*this); } /// \returns Whether this bigint can be losslessly converted to uint64_t. - bool isUint64(Runtime& runtime) const { + bool isUint64(IRuntime& runtime) const { return runtime.bigintIsUint64(*this); } /// \returns This bigint truncated to an unsigned 64-bit integer. Throws a /// JSIException if the truncation is lossy. - uint64_t asUint64(Runtime& runtime) const; + uint64_t asUint64(IRuntime& runtime) const; /// \returns this BigInt converted to a String in base \p radix. Throws a /// JSIException if radix is not in the [2, 36] range. - inline String toString(Runtime& runtime, int radix = 10) const; + inline String toString(IRuntime& runtime, int radix = 10) const; friend class Runtime; friend class Value; @@ -617,19 +1057,19 @@ class JSI_EXPORT String : public Pointer { /// Create a JS string from ascii values. The string data is /// copied. static String - createFromAscii(Runtime& runtime, const char* str, size_t length) { + createFromAscii(IRuntime& runtime, const char* str, size_t length) { return runtime.createStringFromAscii(str, length); } /// Create a JS string from a nul-terminated C ascii string. The /// string data is copied. - static String createFromAscii(Runtime& runtime, const char* str) { + static String createFromAscii(IRuntime& runtime, const char* str) { return createFromAscii(runtime, str, strlen(str)); } /// Create a JS string from a C++ string. The string data is /// copied. - static String createFromAscii(Runtime& runtime, const std::string& str) { + static String createFromAscii(IRuntime& runtime, const std::string& str) { return createFromAscii(runtime, str.c_str(), str.size()); } @@ -637,33 +1077,72 @@ class JSI_EXPORT String : public Pointer { /// transformed and copied. Results are undefined if \p utf8 contains invalid /// code points. static String - createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + createFromUtf8(IRuntime& runtime, const uint8_t* utf8, size_t length) { return runtime.createStringFromUtf8(utf8, length); } /// Create a JS string from utf8-encoded octets stored in a /// std::string. The string data is transformed and copied. Results are /// undefined if \p utf8 contains invalid code points. - static String createFromUtf8(Runtime& runtime, const std::string& utf8) { + static String createFromUtf8(IRuntime& runtime, const std::string& utf8) { return runtime.createStringFromUtf8( reinterpret_cast(utf8.data()), utf8.length()); } + /// Given a series of UTF-16 encoded code units, create a JS String. The input + /// may contain unpaired surrogates, which will be interpreted as a code point + /// of the same value. + static String + createFromUtf16(IRuntime& runtime, const char16_t* utf16, size_t length) { + return runtime.createStringFromUtf16(utf16, length); + } + + /// Given a series of UTF-16 encoded code units stored inside std::u16string, + /// create a JS String. The input may contain unpaired surrogates, which will + /// be interpreted as a code point of the same value. + static String createFromUtf16( + IRuntime& runtime, + const std::u16string& utf16) { + return runtime.createStringFromUtf16(utf16.data(), utf16.length()); + } + /// \return whether a and b contain the same characters. - static bool strictEquals(Runtime& runtime, const String& a, const String& b) { + static bool + strictEquals(IRuntime& runtime, const String& a, const String& b) { return runtime.strictEquals(a, b); } + /// \return the 'length' property of this JS string. + size_t length(IRuntime& runtime) const { + return runtime.length(*this); + } + /// Copies the data in a JS string as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { + std::string utf8(IRuntime& runtime) const { return runtime.utf8(*this); } /// Copies the data in a JS string as utf16 into a C++ string. - std::u16string utf16(Runtime& runtime) const { + std::u16string utf16(IRuntime& runtime) const { return runtime.utf16(*this); } + /// Invokes the user provided callback to process content in String. The + /// callback must take in three arguments: bool ascii, const void* data, and + /// size_t num, respectively. \p ascii indicates whether the \p data passed to + /// the callback should be interpreted as a pointer to a sequence of \p num + /// ASCII characters or UTF16 characters. The function may invoke the callback + /// multiple times, with a different format on each invocation. The callback + /// must not access runtime functionality, as any operation on the runtime may + /// invalidate the data pointers. + template + void getStringData(IRuntime& runtime, CB& cb) const { + runtime.getStringData( + *this, &cb, [](void* ctx, bool ascii, const void* data, size_t num) { + (*((CB*)ctx))(ascii, data, num); + }); + } + friend class Runtime; friend class Value; }; @@ -680,84 +1159,147 @@ class JSI_EXPORT Object : public Pointer { Object& operator=(Object&& other) = default; /// Creates a new Object instance, like '{}' in JS. - Object(Runtime& runtime) : Object(runtime.createObject()) {} + explicit Object(IRuntime& runtime) : Object(runtime.createObject()) {} static Object createFromHostObject( - Runtime& runtime, + IRuntime& runtime, std::shared_ptr ho) { return runtime.createObject(ho); } + /// Creates a new Object with the custom prototype + static Object create(IRuntime& runtime, const Value& prototype) { + return runtime.createObjectWithPrototype(prototype); + } + /// \return whether this and \c obj are the same JSObject or not. - static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) { + static bool + strictEquals(IRuntime& runtime, const Object& a, const Object& b) { return runtime.strictEquals(a, b); } /// \return the result of `this instanceOf ctor` in JS. - bool instanceOf(Runtime& rt, const Function& ctor) const { + bool instanceOf(IRuntime& rt, const Function& ctor) const { return rt.instanceOf(*this, ctor); } + /// Sets \p prototype as the prototype of the object. The prototype must be + /// either an Object or null. If the prototype was not set successfully, this + /// method will throw. + void setPrototype(IRuntime& runtime, const Value& prototype) const { + return runtime.setPrototypeOf(*this, prototype); + } + + /// \return the prototype of the object + inline Value getPrototype(IRuntime& runtime) const; + /// \return the property of the object with the given ascii name. /// If the name isn't a property on the object, returns the /// undefined value. - Value getProperty(Runtime& runtime, const char* name) const; + Value getProperty(IRuntime& runtime, const char* name) const; /// \return the property of the object with the String name. /// If the name isn't a property on the object, returns the /// undefined value. - Value getProperty(Runtime& runtime, const String& name) const; + Value getProperty(IRuntime& runtime, const String& name) const; /// \return the property of the object with the given JS PropNameID /// name. If the name isn't a property on the object, returns the /// undefined value. - Value getProperty(Runtime& runtime, const PropNameID& name) const; + Value getProperty(IRuntime& runtime, const PropNameID& name) const; + + /// \return the Property of the object with the given JS Value name. If the + /// name isn't a property on the object, returns the undefined value.This + /// attempts to convert the JS Value to convert to a property key. If the + /// conversion fails, this method may throw. + Value getProperty(IRuntime& runtime, const Value& name) const; /// \return true if and only if the object has a property with the /// given ascii name. - bool hasProperty(Runtime& runtime, const char* name) const; + bool hasProperty(IRuntime& runtime, const char* name) const; /// \return true if and only if the object has a property with the /// given String name. - bool hasProperty(Runtime& runtime, const String& name) const; + bool hasProperty(IRuntime& runtime, const String& name) const; /// \return true if and only if the object has a property with the /// given PropNameID name. - bool hasProperty(Runtime& runtime, const PropNameID& name) const; + bool hasProperty(IRuntime& runtime, const PropNameID& name) const; + + /// \return true if and only if the object has a property with the given + /// JS Value name. This attempts to convert the JS Value to convert to a + /// property key. If the conversion fails, this method may throw. + bool hasProperty(IRuntime& runtime, const Value& name) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, /// String, or Object. template - void setProperty(Runtime& runtime, const char* name, T&& value) const; + void setProperty(IRuntime& runtime, const char* name, T&& value) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, /// String, or Object. template - void setProperty(Runtime& runtime, const String& name, T&& value) const; + void setProperty(IRuntime& runtime, const String& name, T&& value) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, /// String, or Object. template - void setProperty(Runtime& runtime, const PropNameID& name, T&& value) const; + void setProperty(IRuntime& runtime, const PropNameID& name, T&& value) const; + + /// Sets the property value from a Value or anything which can be + /// used to make one: nullptr_t, bool, double, int, const char*, + /// String, or Object. This takes a JS Value as the property name, and + /// attempts to convert to a property key. If the conversion fails, this + /// method may throw. + template + void setProperty(IRuntime& runtime, const Value& name, T&& value) const; + + /// Delete the property with the given ascii name. Throws if the deletion + /// failed. + void deleteProperty(IRuntime& runtime, const char* name) const; + + /// Delete the property with the given String name. Throws if the deletion + /// failed. + void deleteProperty(IRuntime& runtime, const String& name) const; + + /// Delete the property with the given PropNameID name. Throws if the deletion + /// failed. + void deleteProperty(IRuntime& runtime, const PropNameID& name) const; + + /// Delete the property with the given Value name. Throws if the deletion + /// failed. + void deleteProperty(IRuntime& runtime, const Value& name) const; /// \return true iff JS \c Array.isArray() would return \c true. If /// so, then \c getArray() will succeed. - bool isArray(Runtime& runtime) const { + bool isArray(IRuntime& runtime) const { return runtime.isArray(*this); } /// \return true iff the Object is an ArrayBuffer. If so, then \c /// getArrayBuffer() will succeed. - bool isArrayBuffer(Runtime& runtime) const { + bool isArrayBuffer(IRuntime& runtime) const { return runtime.isArrayBuffer(*this); } + /// \return true iff the Object is a TypedArray (Uint8Array, Int32Array, + /// Float64Array, etc.). If so, then \c getTypedArray() will succeed. + bool isTypedArray(IRuntime& runtime) const { + return runtime.isTypedArray(*this); + } + + /// \return true iff the Object is an Uint8Array. If so, then \c + /// getUint8Array() will succeed + bool isUint8Array(IRuntime& runtime) const { + return runtime.isUint8Array(*this); + } + /// \return true iff the Object is callable. If so, then \c /// getFunction will succeed. - bool isFunction(Runtime& runtime) const { + bool isFunction(IRuntime& runtime) const { return runtime.isFunction(*this); } @@ -765,99 +1307,117 @@ class JSI_EXPORT Object : public Pointer { /// and the HostObject passed is of type \c T. If returns \c true then /// \c getHostObject will succeed. template - bool isHostObject(Runtime& runtime) const; + bool isHostObject(IRuntime& runtime) const; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) const&; + Array getArray(IRuntime& runtime) const&; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) &&; + Array getArray(IRuntime& runtime) &&; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will throw /// JSIException. - Array asArray(Runtime& runtime) const&; + Array asArray(IRuntime& runtime) const&; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will throw /// JSIException. - Array asArray(Runtime& runtime) &&; + Array asArray(IRuntime& runtime) &&; + + /// \return a TypedArray instance which refers to the same underlying + /// object. If \c isTypedArray() would return false, this will throw + /// JSIException. + TypedArray asTypedArray(IRuntime& runtime) const&; + + /// \return an Uint8Array instance which refers to the same underlying + /// object. If \c isUint8Array() would return false, this will throw + /// JSIException. + Uint8Array asUint8Array(IRuntime& runtime) const&; /// \return an ArrayBuffer instance which refers to the same underlying /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) const&; + ArrayBuffer getArrayBuffer(IRuntime& runtime) const&; /// \return an ArrayBuffer instance which refers to the same underlying /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) &&; + ArrayBuffer getArrayBuffer(IRuntime& runtime) &&; + + /// \return a TypedArray instance which refers to the same underlying + /// object. If \c isTypedArray() would return false, this will assert. + TypedArray getTypedArray(IRuntime& runtime) const&; + + /// \return an Uint8Array instance which refers to the same underlying + /// object. If \c isUint8Array() would return false, this will assert. + Uint8Array getUint8Array(IRuntime& runtime) const&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) const&; + Function getFunction(IRuntime& runtime) const&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) &&; + Function getFunction(IRuntime& runtime) &&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will throw /// JSIException. - Function asFunction(Runtime& runtime) const&; + Function asFunction(IRuntime& runtime) const&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will throw /// JSIException. - Function asFunction(Runtime& runtime) &&; + Function asFunction(IRuntime& runtime) &&; /// \return a shared_ptr which refers to the same underlying /// \c HostObject that was used to create this object. If \c isHostObject /// is false, this will assert. Note that this does a type check and will /// assert if the underlying HostObject isn't of type \c T template - std::shared_ptr getHostObject(Runtime& runtime) const; + std::shared_ptr getHostObject(IRuntime& runtime) const; /// \return a shared_ptr which refers to the same underlying /// \c HostObject that was used to create this object. If \c isHostObject /// is false, this will throw. template - std::shared_ptr asHostObject(Runtime& runtime) const; + std::shared_ptr asHostObject(IRuntime& runtime) const; /// \return whether this object has native state of type T previously set by /// \c setNativeState. template - bool hasNativeState(Runtime& runtime) const; + bool hasNativeState(IRuntime& runtime) const; /// \return a shared_ptr to the state previously set by \c setNativeState. /// If \c hasNativeState is false, this will assert. Note that this does a /// type check and will assert if the native state isn't of type \c T template - std::shared_ptr getNativeState(Runtime& runtime) const; + std::shared_ptr getNativeState(IRuntime& runtime) const; /// Set the internal native state property of this object, overwriting any old /// value. Creates a new shared_ptr to the object managed by \p state, which /// will live until the value at this property becomes unreachable. /// /// Throws a type error if this object is a proxy or host object. - void setNativeState(Runtime& runtime, std::shared_ptr state) + void setNativeState(IRuntime& runtime, std::shared_ptr state) const; /// \return same as \c getProperty(name).asObject(), except with /// a better exception message. - Object getPropertyAsObject(Runtime& runtime, const char* name) const; + Object getPropertyAsObject(IRuntime& runtime, const char* name) const; /// \return similar to \c /// getProperty(name).getObject().getFunction(), except it will /// throw JSIException instead of asserting if the property is /// not an object, or the object is not callable. - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + Function getPropertyAsFunction(IRuntime& runtime, const char* name) const; /// \return an Array consisting of all enumerable property names in /// the object and its prototype chain. All values in the return /// will be isString(). (This is probably not optimal, but it /// works. I only need it in one place.) - Array getPropertyNames(Runtime& runtime) const; + Array getPropertyNames(IRuntime& runtime) const; /// Inform the runtime that there is additional memory associated with a given /// JavaScript object that is not visible to the GC. This can be used if an @@ -867,23 +1427,30 @@ class JSI_EXPORT Object : public Pointer { /// calls will overwrite any previously set value. Once the object is garbage /// collected, the associated external memory will be considered freed and may /// no longer factor into GC decisions. - void setExternalMemoryPressure(Runtime& runtime, size_t amt) const; + void setExternalMemoryPressure(IRuntime& runtime, size_t amt) const; protected: void setPropertyValue( - Runtime& runtime, + IRuntime& runtime, const String& name, const Value& value) const { return runtime.setPropertyValue(*this, name, value); } void setPropertyValue( - Runtime& runtime, + IRuntime& runtime, const PropNameID& name, const Value& value) const { return runtime.setPropertyValue(*this, name, value); } + void setPropertyValue( + IRuntime& runtime, + const Value& name, + const Value& value) const { + return runtime.setPropertyValue(*this, name, value); + } + friend class Runtime; friend class Value; }; @@ -899,14 +1466,14 @@ class JSI_EXPORT WeakObject : public Pointer { WeakObject& operator=(WeakObject&& other) = default; /// Create a WeakObject from an Object. - WeakObject(Runtime& runtime, const Object& o) + WeakObject(IRuntime& runtime, const Object& o) : WeakObject(runtime.createWeakObject(o)) {} /// \return a Value representing the underlying Object if it is still valid; /// otherwise returns \c undefined. Note that this method has nothing to do /// with threads or concurrency. The name is based on std::weak_ptr::lock() /// which serves a similar purpose. - Value lock(Runtime& runtime) const; + Value lock(IRuntime& runtime) const; friend class Runtime; }; @@ -917,43 +1484,54 @@ class JSI_EXPORT Array : public Object { public: Array(Array&&) = default; /// Creates a new Array instance, with \c length undefined elements. - Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {} + Array(IRuntime& runtime, size_t length) + : Array(runtime.createArray(length)) {} Array& operator=(Array&&) = default; /// \return the size of the Array, according to its length property. /// (C++ naming convention) - size_t size(Runtime& runtime) const { + size_t size(IRuntime& runtime) const { return runtime.size(*this); } /// \return the size of the Array, according to its length property. /// (JS naming convention) - size_t length(Runtime& runtime) const { + size_t length(IRuntime& runtime) const { return size(runtime); } /// \return the property of the array at index \c i. If there is no /// such property, returns the undefined value. If \c i is out of /// range [ 0..\c length ] throws a JSIException. - Value getValueAtIndex(Runtime& runtime, size_t i) const; + Value getValueAtIndex(IRuntime& runtime, size_t i) const; /// Sets the property of the array at index \c i. The argument /// value behaves as with Object::setProperty(). If \c i is out of /// range [ 0..\c length ] throws a JSIException. template - void setValueAtIndex(Runtime& runtime, size_t i, T&& value) const; + void setValueAtIndex(IRuntime& runtime, size_t i, T&& value) const; - /// There is no current API for changing the size of an array once - /// created. We'll probably need that eventually. + /// Appends provides values to the end of the Array in the order they appear. + /// Returns the new length of the array. + template + size_t push(IRuntime& runtime, Args&&... args); + + /// Appends everything in \p elements to the end of the Array in the order + /// they appear. Returns the new length of the array. + size_t push(IRuntime& runtime, std::initializer_list elements); + + /// Appends \p count elements at \p elements to the end of the Array in the + /// order they appear. + size_t push(IRuntime& runtime, const Value* elements, size_t count); /// Creates a new Array instance from provided values template - static Array createWithElements(Runtime&, Args&&... args); + static Array createWithElements(IRuntime&, Args&&... args); /// Creates a new Array instance from initializer list. static Array createWithElements( - Runtime& runtime, + IRuntime& runtime, std::initializer_list elements); private: @@ -961,7 +1539,7 @@ class JSI_EXPORT Array : public Object { friend class Value; friend class Runtime; - void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) + void setValueAtIndexImpl(IRuntime& runtime, size_t i, const Value& value) const { return runtime.setValueAtIndexImpl(*this, i, value); } @@ -975,24 +1553,33 @@ class JSI_EXPORT ArrayBuffer : public Object { ArrayBuffer(ArrayBuffer&&) = default; ArrayBuffer& operator=(ArrayBuffer&&) = default; - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) + ArrayBuffer(IRuntime& runtime, std::shared_ptr buffer) : ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {} /// \return the size of the ArrayBuffer storage. This is not affected by /// overriding the byteLength property. /// (C++ naming convention) - size_t size(Runtime& runtime) const { + size_t size(IRuntime& runtime) const { return runtime.size(*this); } - size_t length(Runtime& runtime) const { + size_t length(IRuntime& runtime) const { return runtime.size(*this); } - uint8_t* data(Runtime& runtime) const { + uint8_t* data(IRuntime& runtime) const { return runtime.data(*this); } + std::shared_ptr tryGetMutableBuffer(IRuntime& runtime) const { + return runtime.tryGetMutableBuffer(*this); + } + + /// \return true if the ArrayBuffer is detached, false otherwise. + bool detached(IRuntime& runtime) const { + return runtime.detached(*this); + } + private: friend class Object; friend class Value; @@ -1019,7 +1606,7 @@ class JSI_EXPORT Function : public Object { /// any captured values, you are responsible for ensuring that their /// destructors are safe to call on any thread. static Function createFromHostFunction( - Runtime& runtime, + IRuntime& runtime, const jsi::PropNameID& name, unsigned int paramCount, jsi::HostFunctionType func); @@ -1030,7 +1617,7 @@ class JSI_EXPORT Function : public Object { /// \b Note: as with Function.prototype.apply, \c this may not always be /// \c undefined in the function itself. If the function is non-strict, /// \c this will be set to the global object. - Value call(Runtime& runtime, const Value* args, size_t count) const; + Value call(IRuntime& runtime, const Value* args, size_t count) const; /// Calls the function with a \c std::initializer_list of Value /// arguments. The \c this value of the JS function will not be set by the @@ -1039,7 +1626,7 @@ class JSI_EXPORT Function : public Object { /// \b Note: as with Function.prototype.apply, \c this may not always be /// \c undefined in the function itself. If the function is non-strict, /// \c this will be set to the global object. - Value call(Runtime& runtime, std::initializer_list args) const; + Value call(IRuntime& runtime, std::initializer_list args) const; /// Calls the function with any number of arguments similarly to /// Object::setProperty(). The \c this value of the JS function will not be @@ -1049,12 +1636,12 @@ class JSI_EXPORT Function : public Object { /// \c undefined in the function itself. If the function is non-strict, /// \c this will be set to the global object. template - Value call(Runtime& runtime, Args&&... args) const; + Value call(IRuntime& runtime, Args&&... args) const; /// Calls the function with \c count \c args and \c jsThis value passed /// as the \c this value. Value callWithThis( - Runtime& Runtime, + IRuntime& Runtime, const Object& jsThis, const Value* args, size_t count) const; @@ -1062,36 +1649,36 @@ class JSI_EXPORT Function : public Object { /// Calls the function with a \c std::initializer_list of Value /// arguments and \c jsThis passed as the \c this value. Value callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, std::initializer_list args) const; /// Calls the function with any number of arguments similarly to /// Object::setProperty(), and with \c jsThis passed as the \c this value. template - Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args) + Value callWithThis(IRuntime& runtime, const Object& jsThis, Args&&... args) const; /// Calls the function as a constructor with \c count \c args. Equivalent /// to calling `new Func` where `Func` is the js function reqresented by /// this. - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) + Value callAsConstructor(IRuntime& runtime, const Value* args, size_t count) const; /// Same as above `callAsConstructor`, except use an initializer_list to /// supply the arguments. - Value callAsConstructor(Runtime& runtime, std::initializer_list args) + Value callAsConstructor(IRuntime& runtime, std::initializer_list args) const; /// Same as above `callAsConstructor`, but automatically converts/wraps /// any argument with a jsi Value. template - Value callAsConstructor(Runtime& runtime, Args&&... args) const; + Value callAsConstructor(IRuntime& runtime, Args&&... args) const; /// Returns whether this was created with Function::createFromHostFunction. /// If true then you can use getHostFunction to get the underlying /// HostFunctionType. - bool isHostFunction(Runtime& runtime) const { + bool isHostFunction(IRuntime& runtime) const { return runtime.isHostFunction(*this); } @@ -1102,7 +1689,7 @@ class JSI_EXPORT Function : public Object { /// Note: The reference returned is borrowed from the JS object underlying /// \c this, and thus only lasts as long as the object underlying /// \c this does. - HostFunctionType& getHostFunction(Runtime& runtime) const { + HostFunctionType& getHostFunction(IRuntime& runtime) const { assert(isHostFunction(runtime)); return runtime.getHostFunction(*this); } @@ -1115,6 +1702,64 @@ class JSI_EXPORT Function : public Object { Function(Runtime::PointerValue* value) : Object(value) {} }; +/// Represents a JS TypedArray +class JSI_EXPORT TypedArray : public Object { + public: + TypedArray(TypedArray&&) = default; + TypedArray& operator=(TypedArray&&) = default; + + // Gets the buffer of this TypedArray + ArrayBuffer buffer(IRuntime& runtime) { + return runtime.buffer(*this); + } + + // Gets the byte offset of this TypedArray + size_t byteOffset(IRuntime& runtime) { + return runtime.byteOffset(*this); + } + + // Gets the byte length of this TypedArray + size_t byteLength(IRuntime& runtime) { + return runtime.byteLength(*this); + } + + // Gets the element length of this TypedArray + size_t length(IRuntime& runtime) { + return runtime.length(*this); + } + + private: + friend class Object; + friend class Value; + friend class Runtime; + friend class Uint8Array; + + explicit TypedArray(Runtime::PointerValue* value) : Object(value) {} +}; + +// Represents a JS Uint8Array +class JSI_EXPORT Uint8Array : public TypedArray { + public: + Uint8Array(Uint8Array&&) = default; + Uint8Array& operator=(Uint8Array&&) = default; + + Uint8Array(IRuntime& runtime, size_t length) + : Uint8Array(runtime.createUint8Array(length)) {} + Uint8Array( + IRuntime& runtime, + const ArrayBuffer& buffer, + size_t offset, + size_t length) + : Uint8Array(runtime.createUint8Array(buffer, offset, length)) {} + + private: + friend class Object; + friend class Value; + friend class Runtime; + + explicit Uint8Array(Runtime::PointerValue* value) : TypedArray(value) {} +}; + /// Represents any JS Value (undefined, null, boolean, number, symbol, /// string, or object). Movable, or explicitly copyable (has no copy /// ctor). @@ -1165,32 +1810,32 @@ class JSI_EXPORT Value { Value(Value&& other) noexcept; /// Copies a Symbol lvalue into a new JS value. - Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) { + Value(IRuntime& runtime, const Symbol& sym) : Value(SymbolKind) { new (&data_.pointer) Symbol(runtime.cloneSymbol(sym.ptr_)); } /// Copies a BigInt lvalue into a new JS value. - Value(Runtime& runtime, const BigInt& bigint) : Value(BigIntKind) { + Value(IRuntime& runtime, const BigInt& bigint) : Value(BigIntKind) { new (&data_.pointer) BigInt(runtime.cloneBigInt(bigint.ptr_)); } /// Copies a String lvalue into a new JS value. - Value(Runtime& runtime, const String& str) : Value(StringKind) { + Value(IRuntime& runtime, const String& str) : Value(StringKind) { new (&data_.pointer) String(runtime.cloneString(str.ptr_)); } /// Copies a Object lvalue into a new JS value. - Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) { + Value(IRuntime& runtime, const Object& obj) : Value(ObjectKind) { new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_)); } /// Creates a JS value from another Value lvalue. - Value(Runtime& runtime, const Value& value); + Value(IRuntime& runtime, const Value& value); /// Value(rt, "foo") will treat foo as a bool. This makes doing /// that a compile error. template - Value(Runtime&, const char*) { + Value(IRuntime&, const char*) { static_assert( !std::is_same::value, "Value cannot be constructed directly from const char*"); @@ -1209,13 +1854,13 @@ class JSI_EXPORT Value { // \return a \c Value created from a utf8-encoded JSON string. static Value - createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length) { + createFromJsonUtf8(IRuntime& runtime, const uint8_t* json, size_t length) { return runtime.createValueFromJsonUtf8(json, length); } /// \return according to the Strict Equality Comparison algorithm, see: /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison - static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); + static bool strictEquals(IRuntime& runtime, const Value& a, const Value& b); Value& operator=(Value&& other) noexcept { this->~Value(); @@ -1255,6 +1900,28 @@ class JSI_EXPORT Value { return kind_ == ObjectKind; } + /// \returns true if `Number.isInteger(value)` returns true, false otherwise. + bool isInteger() const { + // 1. If number is an integral Number, return true. + // 2. Return false. + + // The spec's definition of integral number: + // When the term integer is used in this specification, it refers to a + // mathematical value which is in the set of integers, unless otherwise + // stated. When the term integral Number is used in this specification, it + // refers to a finite Number value whose mathematical value is in the set of + // integers. + + if (!isNumber()) { + return false; + } + auto number = data_.number; + if (!std::isfinite(number)) { + return false; + } + return number == std::trunc(number); + } + /// \return the boolean value, or asserts if not a boolean. bool getBool() const { assert(isBool()); @@ -1276,14 +1943,14 @@ class JSI_EXPORT Value { double asNumber() const; /// \return the Symbol value, or asserts if not a symbol. - Symbol getSymbol(Runtime& runtime) const& { + Symbol getSymbol(IRuntime& runtime) const& { assert(isSymbol()); return Symbol(runtime.cloneSymbol(data_.pointer.ptr_)); } /// \return the Symbol value, or asserts if not a symbol. /// Can be used on rvalue references to avoid cloning more symbols. - Symbol getSymbol(Runtime&) && { + Symbol getSymbol(IRuntime&) && { assert(isSymbol()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1292,18 +1959,18 @@ class JSI_EXPORT Value { /// \return the Symbol value, or throws JSIException if not a /// symbol - Symbol asSymbol(Runtime& runtime) const&; - Symbol asSymbol(Runtime& runtime) &&; + Symbol asSymbol(IRuntime& runtime) const&; + Symbol asSymbol(IRuntime& runtime) &&; /// \return the BigInt value, or asserts if not a bigint. - BigInt getBigInt(Runtime& runtime) const& { + BigInt getBigInt(IRuntime& runtime) const& { assert(isBigInt()); return BigInt(runtime.cloneBigInt(data_.pointer.ptr_)); } /// \return the BigInt value, or asserts if not a bigint. /// Can be used on rvalue references to avoid cloning more bigints. - BigInt getBigInt(Runtime&) && { + BigInt getBigInt(IRuntime&) && { assert(isBigInt()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1312,18 +1979,18 @@ class JSI_EXPORT Value { /// \return the BigInt value, or throws JSIException if not a /// bigint - BigInt asBigInt(Runtime& runtime) const&; - BigInt asBigInt(Runtime& runtime) &&; + BigInt asBigInt(IRuntime& runtime) const&; + BigInt asBigInt(IRuntime& runtime) &&; /// \return the String value, or asserts if not a string. - String getString(Runtime& runtime) const& { + String getString(IRuntime& runtime) const& { assert(isString()); return String(runtime.cloneString(data_.pointer.ptr_)); } /// \return the String value, or asserts if not a string. /// Can be used on rvalue references to avoid cloning more strings. - String getString(Runtime&) && { + String getString(IRuntime&) && { assert(isString()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1332,18 +1999,18 @@ class JSI_EXPORT Value { /// \return the String value, or throws JSIException if not a /// string. - String asString(Runtime& runtime) const&; - String asString(Runtime& runtime) &&; + String asString(IRuntime& runtime) const&; + String asString(IRuntime& runtime) &&; /// \return the Object value, or asserts if not an object. - Object getObject(Runtime& runtime) const& { + Object getObject(IRuntime& runtime) const& { assert(isObject()); return Object(runtime.cloneObject(data_.pointer.ptr_)); } /// \return the Object value, or asserts if not an object. /// Can be used on rvalue references to avoid cloning more objects. - Object getObject(Runtime&) && { + Object getObject(IRuntime&) && { assert(isObject()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1352,11 +2019,11 @@ class JSI_EXPORT Value { /// \return the Object value, or throws JSIException if not an /// object. - Object asObject(Runtime& runtime) const&; - Object asObject(Runtime& runtime) &&; + Object asObject(IRuntime& runtime) const&; + Object asObject(IRuntime& runtime) &&; // \return a String like JS .toString() would do. - String toString(Runtime& runtime) const; + String toString(IRuntime& runtime) const; private: friend class Runtime; @@ -1430,7 +2097,7 @@ class JSI_EXPORT Value { /// locking, provided that the lock (if any) is managed with RAII helpers. class JSI_EXPORT Scope { public: - explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {} + explicit Scope(IRuntime& rt) : rt_(rt), prv_(rt.pushScope()) {} ~Scope() { rt_.popScope(prv_); } @@ -1442,13 +2109,13 @@ class JSI_EXPORT Scope { Scope& operator=(Scope&&) = delete; template - static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) { + static auto callInNewScope(IRuntime& rt, F f) -> decltype(f()) { Scope s(rt); return f(); } private: - Runtime& rt_; + IRuntime& rt_; Runtime::ScopeState* prv_; }; @@ -1488,31 +2155,55 @@ class JSI_EXPORT JSINativeException : public JSIException { class JSI_EXPORT JSError : public JSIException { public: /// Creates a JSError referring to provided \c value - JSError(Runtime& r, Value&& value); + JSError(IRuntime& r, Value&& value); /// Creates a JSError referring to new \c Error instance capturing current /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, std::string message); + JSError(IRuntime& rt, std::string message); /// Creates a JSError referring to new \c Error instance capturing current /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, const char* message) + JSError(IRuntime& rt, const char* message) : JSError(rt, std::string(message)) {} /// Creates a JSError referring to a JavaScript Object having message and /// stack properties set to provided values. - JSError(Runtime& rt, std::string message, std::string stack); + JSError(IRuntime& rt, std::string message, std::string stack); /// Creates a JSError referring to provided value and what string /// set to provided message. This argument order is a bit weird, /// but necessary to avoid ambiguity with the above. - JSError(std::string what, Runtime& rt, Value&& value); + JSError(std::string what, IRuntime& rt, Value&& value); /// Creates a JSError referring to the provided value, message and stack. This /// constructor does not take a Runtime parameter, and therefore cannot result /// in recursively invoking the JSError constructor. JSError(Value&& value, std::string message, std::string stack); + /// Creates a JSError referring to new \c EvalError instance. The error + /// message property is set to given \c message. + static JSError createEvalError(IRuntime& rt, const std::string& message); + + /// Creates a JSError referring to new \c RangeError instance. The error + /// message property is set to given \c message. + static JSError createRangeError(IRuntime& rt, const std::string& message); + + /// Creates a JSError referring to new \c ReferenceError instance. The error + /// message property is set to given \c message. + static JSError createReferenceError(IRuntime& rt, const std::string& message); + + /// Creates a JSError referring to new \c SyntaxError instance. The error + /// message property is set to given \c message. + static JSError createSyntaxError(IRuntime& rt, const std::string& message); + + /// Creates a JSError referring to new \c TypeError instance. The error + /// message property is set to given \c message. + static JSError createTypeError(IRuntime& rt, const std::string& message); + + /// Creates a JSError referring to new \c URIError instance. The error + /// message property is set to given \c message. + static JSError createURIError(IRuntime& rt, const std::string& message); + JSError(const JSError&) = default; virtual ~JSError(); @@ -1534,7 +2225,7 @@ class JSI_EXPORT JSError : public JSIException { // This initializes the value_ member and does some other // validation, so it must be called by every branch through the // constructors. - void setValue(Runtime& rt, Value&& value); + void setValue(IRuntime& rt, Value&& value); // This needs to be on the heap, because throw requires the object // be copyable, and Value is not. @@ -1543,6 +2234,32 @@ class JSI_EXPORT JSError : public JSIException { std::string stack_; }; +/// Helper function to cast the object pointed to by \p ptr into an interface +/// specified by \c U. If cast is successful, return a pointer to the object +/// as a raw pointer of \c U. Otherwise, return nullptr. +/// The returned interface same lifetime as the object referenced by \p ptr. +template +U* castInterface(T* ptr) { + if (ptr) { + return static_cast(ptr->castInterface(U::uuid)); + } + return nullptr; +} + +/// Helper function to cast the object managed by the shared_ptr \p ptr into an +/// interface specified by \c U. If the cast is successful, return a shared_ptr +/// of type \c U to the object. Otherwise, return an empty pointer. +/// The returned shared_ptr shares ownership of the object with \p ptr. +template +std::shared_ptr dynamicInterfaceCast(T&& ptr) { + auto* p = ptr->castInterface(U::uuid); + U* res = static_cast(p); + if (res) { + return std::shared_ptr(std::forward(ptr), res); + } + return nullptr; +} + } // namespace jsi } // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/test/testlib.h b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/test/testlib.h index 9f30fb2d..b56d41b8 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/test/testlib.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/jsi/test/testlib.h @@ -19,7 +19,7 @@ namespace jsi { class Runtime; -using RuntimeFactory = std::function()>; +using RuntimeFactory = std::function()>; std::vector runtimeGenerators(); @@ -42,7 +42,7 @@ class JSITestBase : public ::testing::TestWithParam { } RuntimeFactory factory; - std::unique_ptr runtime; + std::shared_ptr runtime; Runtime& rt; }; } // namespace jsi diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi.h b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi.h new file mode 100644 index 00000000..508a04e5 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi.h @@ -0,0 +1,269 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef HERMES_NAPI_HERMES_NAPI_H +#define HERMES_NAPI_HERMES_NAPI_H + +#include "hermes/napi/node_api.h" + +//=========================================================================== +// Host integration interface +//=========================================================================== + +/// Abstract interface that the host application can provide to integrate +/// with its runtime environment. This covers async work scheduling +/// (napi_create_async_work / napi_queue_async_work), thread-safe +/// function dispatch, libuv event loop access, and fatal exception +/// handling. Fields left as nullptr preserve default behavior — in +/// particular, async work and thread-safe function APIs will return +/// napi_generic_failure if the relevant callbacks are not provided. +/// +/// The host is responsible for the lifetime of this struct — it must +/// outlive the napi_env that references it. +struct hermes_napi_host { + /// Schedule \p execute to run on a worker thread. When execute + /// completes, schedule \p complete to run on the main (JS) thread. + /// \p data is passed through to both callbacks. + /// \p complete receives \p status == 0 on success, or + /// napi_cancelled if the work was cancelled. + void (*post_work)( + void *loop_data, + void *work_data, + void (*execute)(void *work_data), + void (*complete)(void *work_data, napi_status status)); + + /// Cancel a previously posted work item. The event loop should + /// attempt to cancel the work. If successful, the complete callback + /// from post_work should be called with status == napi_cancelled. + /// Returns true if cancellation was initiated, false if the work + /// has already started or completed. + bool (*cancel_work)(void *loop_data, void *work_data); + + /// Schedule \p callback to run on the main (JS) thread. + /// This is used by thread-safe functions to dispatch queued calls. + /// \p data is passed through to the callback. + void (*post_task)( + void *loop_data, + void *task_data, + void (*callback)(void *task_data)); + + /// Opaque data pointer passed as the first argument to post_work, + /// cancel_work, and post_task. Typically the host's event loop + /// instance. + void *data; + + /// If non-null, napi_get_uv_event_loop() returns this pointer. + /// The host is responsible for the lifetime — it must outlive the + /// napi_env. Embedders without libuv leave this nullptr (the + /// default), in which case napi_get_uv_event_loop() returns + /// napi_generic_failure. + struct uv_loop_s *uv_loop; + + /// If non-null, called by napi_fatal_exception() instead of aborting. + /// The host typically routes this to + /// process.emit('uncaughtException', err). + /// Receives the host data pointer, the napi_env, and the error value. + /// When null (the default), napi_fatal_exception() prints the error + /// and calls abort() via hermes_fatal(). + void (*fatal_exception)(void *data, napi_env env, napi_value err); + + /// Hold a long-lived reference on the event loop. The host should + /// keep the loop alive until a matching unref_loop call. Used by + /// threadsafe functions to model libuv-style "ref" semantics: while + /// any tsfn in an env is referenced, the env holds one ref so + /// producer threads can dispatch into JS without racing the loop's + /// shutdown. + /// + /// Contract: within a single napi_env, ref_loop / unref_loop calls + /// are paired and NEVER nested — every ref_loop is followed by a + /// matching unref_loop before the next ref_loop from that env. The + /// env coalesces all referenced tsfns into one ref/unref pair, so an + /// env contributes at most one outstanding ref at a time. + /// + /// Across distinct envs sharing the same host, multiple unmatched + /// refs may be in flight concurrently — one per env that has any + /// referenced tsfn — so the host implementation must accept + /// concurrent unmatched ref_loop calls (typically via a refcount + /// or its existing source-tracking mechanism). + /// + /// Optional — when null, TSFN ref/unref are tracked for API + /// compatibility only and have no effect on loop lifetime. + /// Must be paired with unref_loop. + void (*ref_loop)(void *loop_data); + + /// Release a long-lived loop reference previously taken by ref_loop. + /// Optional; null only if ref_loop is also null. + void (*unref_loop)(void *loop_data); +}; + +typedef hermes_napi_host hermes_napi_event_loop; + +//=========================================================================== +// Environment lifecycle (public API) +//=========================================================================== + +/// Create a new NAPI environment backed by a Hermes Runtime. +/// The \p hermes_runtime parameter is an opaque pointer to a +/// hermes::vm::Runtime instance. +/// +/// The env is owned by the Runtime and is automatically torn down and +/// freed when the Runtime is destroyed. The caller must keep the +/// Runtime alive for as long as it uses the returned pointer, and must +/// not free the env itself. +/// +/// Finalizer semantics at Runtime teardown: +/// +/// Finalizers registered via napi_wrap, napi_add_finalizer, +/// napi_create_external, etc. are guaranteed to be invoked exactly +/// once with the live env that owns the wrapped object. We never +/// invoke user finalizers with env == nullptr. +/// +/// Callbacks fire in one of two contexts, both with a non-null env: +/// +/// - Unrestricted: the wrapped object was collected while the GC +/// heap was fully functional — either during normal operation +/// (collection happens, callback is drained at the next +/// NAPI_PREAMBLE / drainJobs) or during the env's shutdown +/// phase (cleanup hooks, persistent-reference finalizers, the +/// instance-data finalizer, or thread-safe-function cleanup +/// transitively triggered a GC; the callback is drained at the +/// end of shutdown). The callback may freely allocate, call +/// into JS, and use the env as in any other context. +/// +/// - Restricted: the wrapped object was still reachable from JS +/// roots at teardown and was finalized by +/// getHeap().finalizeAll() itself. The callback is drained +/// after finalizeAll, with the GC heap in a post-mortem state. +/// Any NAPI call that would allocate or run JavaScript returns +/// napi_cannot_run_js instead of executing. A spec-conformant +/// finalizer (which only frees native resources — free(), +/// close(), delete, refcount decrement) is unaffected. A +/// finalizer that tries to call back into JS receives the +/// error code and may choose to log it. +/// +/// The optional \p host provides host integration callbacks (async work, +/// thread-safe functions, etc.); if nullptr, those APIs return +/// napi_generic_failure. +NAPI_EXTERN napi_env NAPI_CDECL +hermes_napi_create_env(void *hermes_runtime, hermes_napi_host *host = nullptr); + +/// Get the last module registered via the deprecated napi_module_register(). +/// Returns nullptr if no module has been registered. +NAPI_EXTERN const napi_module *NAPI_CDECL +hermes_napi_get_last_registered_module(); + +/// Load a NAPI addon from a shared library at \p path. +/// +/// This function: +/// 1. Opens the shared library via dlopen(). +/// 2. Looks up the `napi_register_module_v1` symbol (modern approach). +/// If not found, falls back to the deprecated `napi_module_register()` +/// approach (checking `hermes_napi_get_last_registered_module()`). +/// 3. Creates an empty `exports` object in the caller's env. +/// 4. Calls the module's init function with (env, exports). +/// 5. Stores the resulting exports object in \p result. +/// +/// The caller must provide a valid \p env with an open handle scope. +/// The module's CallbackBundles are owned by the caller's env. +/// +/// Returns napi_ok on success, or an error status on failure (with a +/// pending exception set on the env). +NAPI_EXTERN napi_status NAPI_CDECL +hermes_napi_load_module(napi_env env, const char *path, napi_value *result); + +//=========================================================================== +// Script execution +//=========================================================================== + +/// Flags for hermes_run_script(). The first field is the struct +/// size for ABI-stable extensibility — newer versions can append +/// fields without breaking older callers. +struct hermes_run_script_flags { + /// Size of this struct in bytes. Must be set to + /// sizeof(hermes_run_script_flags) by the caller. + size_t struct_size; + /// Run in strict mode. + bool strict; + /// Enable TypeScript support. + bool enable_ts; + /// Keep the compiled bytecode alive for the lifetime of the Runtime, + /// even after all references to it are gone. This lets the engine + /// avoid copying certain parts of the buffer, resulting in faster + /// load times. Do not set this for short-lived modules (e.g. REPL + /// input) — they would accumulate and never be freed. + bool persistent; +}; + +/// Compile and run JavaScript source in a single call. +/// +/// \p data and \p size describe a UTF-8 source buffer. If the last +/// byte is '\0', the source is used zero-copy (actual source length +/// is size-1). Otherwise, an internal copy is made to add the null +/// terminator required by the compiler. +/// +/// The runtime takes ownership of the buffer: \p finalize_cb is +/// called with (\p data, \p size, \p finalize_hint) when the buffer +/// is no longer needed. Pass NULL for \p finalize_cb if the buffer +/// is static or externally managed. +/// +/// \p source_url appears in stack traces (NULL → empty string). +/// \p flags controls compilation options (NULL → all defaults). +/// +/// Returns napi_ok on success, napi_pending_exception on JS or +/// compile error. +NAPI_EXTERN napi_status NAPI_CDECL hermes_run_script( + napi_env env, + const uint8_t *data, + size_t size, + void (*finalize_cb)(const uint8_t *data, size_t size, void *hint), + void *finalize_hint, + const char *source_url, + const hermes_run_script_flags *flags, + napi_value *result); + +//=========================================================================== +// Pre-compiled bytecode +//=========================================================================== + +/// Flags for hermes_run_bytecode(). The first field is the struct +/// size for ABI-stable extensibility — newer versions can append +/// fields without breaking older callers. +struct hermes_bytecode_flags { + size_t struct_size; + /// Keep the bytecode alive for the lifetime of the Runtime, even + /// after all references to it are gone. This lets the engine avoid + /// copying certain parts of the buffer, resulting in faster load + /// times. Do not set this for short-lived modules (e.g. REPL + /// input) — they would accumulate and never be freed. + bool persistent; + bool hides_epilogue; + bool funcs_are_builtins; +}; + +/// Run pre-compiled Hermes bytecode. +/// +/// \p data and \p size describe the bytecode buffer. The runtime +/// takes ownership: \p finalize_cb is called with (\p data, \p size, +/// \p finalize_hint) when the buffer is no longer needed. Pass NULL +/// for \p finalize_cb if the buffer is static or externally managed. +/// +/// \p source_url appears in stack traces (NULL → empty string). +/// \p flags controls runtime module behavior (NULL → all defaults). +/// +/// Returns napi_ok on success, napi_pending_exception on JS error, +/// or napi_generic_failure on bytecode validation failure. +NAPI_EXTERN napi_status NAPI_CDECL hermes_run_bytecode( + napi_env env, + const uint8_t *data, + size_t size, + void (*finalize_cb)(const uint8_t *data, size_t size, void *hint), + void *finalize_hint, + const char *source_url, + const hermes_bytecode_flags *flags, + napi_value *result); + +#endif // HERMES_NAPI_HERMES_NAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_compile.h b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_compile.h new file mode 100644 index 00000000..7ce9b49a --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_compile.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef HERMES_NAPI_HERMES_NAPI_COMPILE_H +#define HERMES_NAPI_HERMES_NAPI_COMPILE_H + +#include "hermes/napi/node_api.h" + +#include + +/// Flags for hermes_compile_to_bytecode(). The first field is the struct +/// size for ABI-stable extensibility — newer versions can append +/// fields without breaking older callers. +struct hermes_compile_flags { + size_t struct_size; + /// Parse in strict mode. + bool strict; + /// Run the full optimization pipeline on the compiled IR. + bool optimize; + /// Emit async break check instructions, e.g. for enforcing execution + /// time limits. + bool emit_async_break_check; + /// Enable TypeScript support. + bool enable_ts; +}; + +/// Compile JavaScript source to serialized Hermes bytecode. +/// +/// \p source and \p source_size describe the UTF-8 source buffer. +/// For best performance, include the null terminator in the buffer +/// (i.e. source[source_size - 1] == '\0'); the actual source text is +/// then source_size - 1 bytes. If the buffer does not end with '\0', +/// the function makes an internal copy to add one. +/// +/// \p source_url appears in error messages and stack traces +/// (NULL -> empty string). +/// \p flags controls compilation behavior (NULL -> all defaults). +/// +/// On success, *bytecode_out and *bytecode_size_out receive a newly +/// allocated buffer containing the serialized bytecode. Free it with +/// hermes_free_bytecode(). +/// +/// Returns napi_ok on success, napi_pending_exception on parse/compile +/// error (the exception describes the error). +napi_status hermes_compile_to_bytecode( + napi_env env, + const uint8_t *source, + size_t source_size, + const char *source_url, + const hermes_compile_flags *flags, + uint8_t **bytecode_out, + size_t *bytecode_size_out); + +/// Free bytecode allocated by hermes_compile_to_bytecode(). +void hermes_free_bytecode(uint8_t *bytecode); + +#endif // HERMES_NAPI_HERMES_NAPI_COMPILE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_impl.h b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_impl.h new file mode 100644 index 00000000..57d936d3 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_impl.h @@ -0,0 +1,606 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef HERMES_NAPI_HERMES_NAPI_IMPL_H +#define HERMES_NAPI_HERMES_NAPI_IMPL_H + +#include "hermes_napi.h" + +#include "hermes/VM/HermesValue.h" +#include "hermes/VM/Runtime.h" +#include "hermes/VM/WeakRef.h" + +#include +#include +#include +#include +#include +#include + +//=========================================================================== +// Callback info +//=========================================================================== + +/// The callback info structure passed to NAPI callbacks. +/// This is stack-allocated by the native function trampoline and holds +/// all the data that napi_get_cb_info and napi_get_new_target need. +/// +/// The \c thisArg pointer points to the 'this' value — either on the +/// register stack (for regular calls) or in a handle scope slot (for +/// constructor calls where a new 'this' object is synthesized). +/// The \c argsBase pointer always points to the 'this' slot on the +/// register stack, so that arguments can be accessed at argsBase[-(i+1)]. +struct napi_callback_info__ { + /// The NAPI environment. + napi_env env; + + /// Pointer to the 'this' PinnedHermesValue. For regular calls this + /// points into the register stack; for constructor calls it may + /// point into a handle scope slot. + const hermes::vm::PinnedHermesValue *thisArg; + + /// Pointer to the 'this' slot on the register stack. Arguments are + /// at argsBase[-(i+1)]. This always points to the register stack, + /// even for constructor calls. + const hermes::vm::PinnedHermesValue *argsBase; + + /// Number of JavaScript arguments (excluding 'this'). + unsigned argc; + + /// User data pointer from napi_create_function. + void *data; + + /// Pointer to the new.target PinnedHermesValue in the register stack. + /// Contains undefined for regular function calls. + const hermes::vm::PinnedHermesValue *newTarget; + + /// Get the i-th argument. Returns the HermesValue directly from the + /// register stack if i < argc, otherwise returns undefined. + hermes::vm::HermesValue getArg(unsigned i) const { + // Args are stored at decreasing addresses from argsBase. + // arg[i] is at argsBase[-(i+1)]. + return i < argc + ? static_cast(argsBase[-(int)(i + 1)]) + : hermes::vm::HermesValue::encodeUndefinedValue(); + } +}; + +//=========================================================================== +// Callback bundle +//=========================================================================== + +/// Data associated with each NAPI native function. Stored in a list +/// owned by napi_env__ and pointed to by the NativeFunction's context. +/// The bundle is allocated when napi_create_function is called and freed +/// when the env is destroyed. +struct CallbackBundle { + napi_env env; + napi_callback cb; + void *data; +}; + +//=========================================================================== +// Handle scope storage +//=========================================================================== + +/// A fixed-size block of PinnedHermesValue slots used for handle storage. +/// Once allocated, the block never moves in memory, so pointers to individual +/// slots (returned as napi_value) remain valid for the block's lifetime. +struct HandleBlock { + /// Number of slots per block. Chosen for cache efficiency while + /// keeping per-scope overhead low. + static constexpr size_t kSize = 64; + + hermes::vm::PinnedHermesValue slots[kSize]; +}; + +/// Descriptor for an open handle scope. Records the position in the handle +/// storage at the time the scope was opened, so that closing the scope can +/// reclaim all handles allocated since then. +struct HandleScopeDescriptor { + /// Index of the active block when this scope was opened. + size_t blockIndex; + + /// Number of used slots in that block when this scope was opened. + size_t slotIndex; + + /// Whether this is an escapable scope. + bool escapable = false; + + /// Whether napi_escape_handle has been called on this scope. + bool escapeCalled = false; + + /// For escapable scopes: pointer to the pre-allocated slot in the + /// parent scope where the escaped value will be stored. + hermes::vm::PinnedHermesValue *escapeSlot = nullptr; +}; + +//=========================================================================== +// napi_deferred__ definition +//=========================================================================== + +/// A deferred promise resolution. Stores the resolve and reject functions +/// captured from the Promise constructor's executor callback. These are +/// GC roots, marked by the env's custom roots function while the deferred +/// is active. The deferred is consumed (deleted) when +/// napi_resolve_deferred or napi_reject_deferred is called. +/// +/// Deferreds are stored in a doubly-linked list in the env for GC root +/// enumeration and cleanup. +struct napi_deferred__ { + /// The resolve function from the Promise executor. + hermes::vm::PinnedHermesValue resolve{}; + + /// The reject function from the Promise executor. + hermes::vm::PinnedHermesValue reject{}; + + /// Doubly-linked list pointers for the env's deferred list. + napi_deferred__ *prev_ = nullptr; + napi_deferred__ *next_ = nullptr; +}; + +//=========================================================================== +// napi_ref__ definition +//=========================================================================== + +/// A persistent reference to a JavaScript value that can be either strong +/// (prevents GC, refcount > 0) or weak (allows GC, refcount == 0). +/// +/// Strong references: The \c value field is marked as a GC root by the +/// env's custom roots function. The referenced value will not be collected. +/// +/// Weak references: For pointer types (objects, strings, bigints), a +/// \c WeakRef tracks GC liveness through a \c WeakRefSlot. The +/// \c value field is NOT marked as a root, so the GC may collect the +/// referent. For non-pointer types (numbers, booleans, undefined, null, +/// symbols), no GC tracking is needed — these values are always valid. +/// +/// References are stored in a doubly-linked list in the env for GC root +/// enumeration and cleanup. +/// A reference to a JS value with controllable prevent-collection semantics. +/// +/// Three states, defined by (refcount, weakSlot_): +/// +/// 1. Strong (refcount > 0, weakSlot_ == nullptr): +/// `value` holds the referenced value and is marked as a GC root. +/// +/// 2. Weak (refcount == 0, weakSlot_ != nullptr): +/// `value` is undefined. The actual value lives in the weak slot, which +/// the GC tracks — updating it if the object moves, or invalidating it +/// if collected. All value types (objects, strings, numbers, booleans, +/// symbols, etc.) go through the weak slot uniformly. +/// +/// 3. Known dead (refcount == 0, weakSlot_ == nullptr): +/// `value` is undefined. The referent was collected. Permanent state: +/// napi_reference_ref is a no-op returning 0, napi_get_reference_value +/// returns NULL. +/// +/// Transitions: +/// 1→2: napi_reference_unref to refcount 0. Value moves to a new slot. +/// 2→1: napi_reference_ref when slot is alive. Value restored from slot. +/// 2→3: napi_reference_ref when slot is dead, or env teardown. +struct napi_ref__ { + napi_env env; + + /// Reference count. >0 means strong (GC root), ==0 means weak. + uint32_t refcount; + + /// When strong (refcount > 0), contains the referenced value and is + /// marked as a GC root. When weak (refcount == 0), always undefined — + /// the actual value lives in the weakSlot_. + hermes::vm::PinnedHermesValue value{}; + + /// When weak: tracks the value and its liveness. The GC updates or + /// invalidates the slot as needed. Null for strong refs or for weak + /// refs that are known dead (referent was collected). + hermes::vm::WeakRefSlot *weakSlot_ = nullptr; + + /// Doubly-linked list pointers for the env's reference list. + napi_ref__ *prev_ = nullptr; + napi_ref__ *next_ = nullptr; + + /// Optional destructor callback. Called during GC or env teardown, + /// but NOT by napi_delete_reference (matching V8 behavior). + napi_finalize finalize_cb = nullptr; + void *finalize_data = nullptr; + void *finalize_hint = nullptr; + + /// Set to true when this reference is being cleaned up during env + /// teardown. Prevents re-entrant deletion when a finalizer calls + /// napi_delete_reference on the same reference being finalized. + bool deletionPending_ = false; + + /// Return true if this is a strong reference (refcount > 0). + bool isStrong() const { + return refcount > 0; + } + + /// Move the value into a weak slot. Called when transitioning from + /// strong to weak, or when creating a new weak reference. + void createWeakSlot(hermes::vm::Runtime &runtime) { + assert(!weakSlot_ && "weak slot already exists"); + auto shv = hermes::vm::SmallHermesValue::encodeHermesValue(value, runtime); + weakSlot_ = runtime.getHeap().allocWeakSlot(shv); + value = hermes::vm::HermesValue::encodeUndefinedValue(); + } + + /// Release the weak slot. Called when transitioning from weak to + /// strong, or when the reference is deleted. + void releaseWeakSlot() { + if (weakSlot_) { + weakSlot_->free(); + weakSlot_ = nullptr; + } + } + + /// Check if the weak reference is still valid (referent not collected). + /// Strong refs are always valid. Weak refs with no slot are known dead. + bool isWeakValid() const { + if (isStrong()) + return true; + if (!weakSlot_) + return false; // known dead + return weakSlot_->hasValue(); + } + + /// Get the referenced value from the weak slot, with a read barrier. + /// Only valid when the weak slot exists and hasValue() is true. + hermes::vm::HermesValue getWeakValue(hermes::vm::Runtime &runtime) const { + assert(weakSlot_ && "no weak slot"); + assert(weakSlot_->hasValue() && "weak ref is dead"); + return weakSlot_->getValue(runtime, runtime.getHeap()); + } +}; + +//=========================================================================== +// napi_async_cleanup_hook_handle__ definition +//=========================================================================== + +/// Handle for an async cleanup hook registered via +/// napi_add_async_cleanup_hook. Stores the user's hook function and +/// data, and is linked into the env's asyncCleanupHooks_ list. +/// During env teardown, the hook is called with this handle (so it can +/// call napi_remove_async_cleanup_hook) and the user data. +struct napi_async_cleanup_hook_handle__ { + napi_env env; + napi_async_cleanup_hook hook; + void *arg; +}; + +//=========================================================================== +// napi_env__ definition +//=========================================================================== + +/// The core environment structure for the Hermes NAPI implementation. +/// Each napi_env holds a reference to a Hermes VM Runtime and manages +/// error state, pending exceptions, handle scopes, and (in future phases) +/// persistent references and cleanup hooks. +struct napi_env__ { + /// Create a new NAPI environment backed by the given Runtime. + /// The Runtime's lifetime must exceed this env's lifetime. + /// The optional \p host provides host integration callbacks. + explicit napi_env__( + hermes::vm::Runtime &runtime, + hermes_napi_host *host = nullptr); + + /// Destructor. + ~napi_env__(); + + /// Non-copyable, non-movable. + napi_env__(const napi_env__ &) = delete; + napi_env__ &operator=(const napi_env__ &) = delete; + + //--- Handle scope management --- + + /// Allocate a new PinnedHermesValue slot in the current (topmost) handle + /// scope, store \p val in it, and return a pointer to the slot. The + /// returned pointer is valid until the enclosing handle scope is closed. + /// Returns nullptr if no handle scope is open. + napi_value addToCurrentScope(hermes::vm::HermesValue val); + + /// Mark all live handle scope slots as GC roots via \p acceptor. + void markHandleScopes(hermes::vm::RootAcceptor &acceptor); + + //--- Deferred finalization --- + + /// Queue a finalizer callback for deferred execution outside GC. + /// Called from GC finalizers (NativeState, DecoratedObject) which + /// must not call back into JS during GC sweep. + void queuePendingFinalizer(napi_finalize cb, void *data, void *hint); + + /// Drain all pending finalizer callbacks. Called at safe points + /// outside of GC (NAPI_PREAMBLE, env teardown) to execute the + /// queued callbacks. + void drainPendingFinalizers(); + + /// Drive substantive shutdown of this environment. Drains pending + /// finalizers, runs cleanup hooks, finalizes persistent references, + /// runs the instance data finalizer, and tears down deferreds and + /// thread-safe functions. Registered as a Runtime shutdown + /// callback so it runs before Runtime::~Runtime calls + /// getHeap().finalizeAll(), with a fully functional heap. + /// + /// User code invoked here (cleanup hooks, ref finalizers, etc.) + /// may transitively trigger GC, which queues wrap callbacks via + /// finalizeNapiObjectData. A final drainPendingFinalizers() at + /// the end of shutdown() runs those callbacks while the heap is + /// still fully functional, so they have unrestricted env access. + /// + /// After this returns, the env structure still exists (it must + /// outlive finalizeAll so finalizeNapiObjectData can call + /// queuePendingFinalizer for wrapped objects still alive at + /// teardown). Only callbacks queued by finalizeAll itself end + /// up in the Runtime's post-shutdown deleter, where + /// inPostShutdownDrain_ is set and NAPI calls that would + /// allocate or run JS return napi_cannot_run_js. + void shutdown(); + + //--- Reference management --- + + /// Add a reference to the env's linked list. + void addReference(napi_ref__ *ref); + + /// Remove a reference from the env's linked list. + void removeReference(napi_ref__ *ref); + + /// Mark all strong references as GC roots via \p acceptor. + void markReferences(hermes::vm::RootAcceptor &acceptor); + + //--- Deferred management --- + + /// Add a deferred to the env's linked list for GC root marking. + void addDeferred(napi_deferred__ *def); + + /// Remove a deferred from the env's linked list. + void removeDeferred(napi_deferred__ *def); + + /// Mark all active deferreds as GC roots via \p acceptor. + void markDeferreds(hermes::vm::RootAcceptor &acceptor); + + //--- Thread-safe function management --- + + /// Add a TSFN to the env's linked list for GC root marking. + void addTsfn(napi_threadsafe_function__ *tsfn); + + /// Remove a TSFN from the env's linked list. + void removeTsfn(napi_threadsafe_function__ *tsfn); + + /// Mark all active TSFNs as GC roots via \p acceptor. + void markTsfns(hermes::vm::RootAcceptor &acceptor); + + /// Acquire a tsfn loop reference. On the 0 → 1 transition, calls + /// host->ref_loop. On other increments, just bumps the counter. + void acquireTsfnLoopRef(); + + /// Release a tsfn loop reference. On the 1 → 0 transition, calls + /// host->unref_loop. On other decrements, just lowers the counter. + void releaseTsfnLoopRef(); + + /// The Hermes VM runtime this environment is bound to. + hermes::vm::Runtime &runtime; + + /// Last error information returned by napi_get_last_error_info. + napi_extended_error_info last_error{}; + + /// Whether there is a pending JavaScript exception. + bool hasPendingException = false; + + /// Storage for the pending JavaScript exception value. + /// This is a GC root — it is marked during GC via the custom roots + /// function registered in the constructor. + hermes::vm::PinnedHermesValue pendingException{}; + + /// Module API version. Defaults to NAPI_VERSION. + int32_t module_api_version = NAPI_VERSION; + + /// Number of currently open handle scopes (for validation). + int open_handle_scopes = 0; + + /// Stack of open handle scope descriptors (LIFO). + std::vector scopeStack_; + + /// Pool of handle blocks. Blocks are allocated on demand and reused + /// across scope open/close cycles to avoid repeated allocation. + /// Pointers to slots within these blocks are handed out as napi_value. + std::vector> blocks_; + + /// Index of the current (partially filled) block in blocks_. + size_t currentBlockIndex_ = 0; + + /// Number of slots used in the current block. + size_t currentSlotIndex_ = 0; + + /// Callback bundles for native functions. Each bundle is owned by the + /// env and freed when the env is destroyed. We use a deque so that + /// pointers to bundles (stored as NativeFunction context) remain stable. + std::deque callbackBundles_; + + /// Head of the doubly-linked list of persistent references (napi_ref). + /// Used for GC root marking (strong refs) and cleanup on env destruction. + napi_ref__ *refListHead_ = nullptr; + + /// Head of the doubly-linked list of active deferreds (napi_deferred). + /// Used for GC root marking of resolve/reject functions. + napi_deferred__ *deferredListHead_ = nullptr; + + /// The file name of the loaded NAPI module (set by + /// hermes_napi_load_module). Used by node_api_get_module_file_name. + std::string moduleFileName_; + + /// Registered environment cleanup hooks (LIFO order on teardown). + /// Each entry is a {function, arg} pair. Called in reverse order + /// during env destruction, before finalizers and other cleanup. + std::vector> cleanupHooks_; + + /// Registered async cleanup hooks (LIFO order on teardown). + /// Each entry is a heap-allocated handle. Called in reverse order + /// during env destruction, after sync cleanup hooks. + std::vector asyncCleanupHooks_; + + /// Per-environment instance data (set by napi_set_instance_data). + void *instanceData_ = nullptr; + + /// Finalizer callback for instance data, called on env destruction or + /// when replaced by a new napi_set_instance_data call. + napi_finalize instanceDataFinalizeCb_ = nullptr; + + /// Hint passed to the instance data finalizer. + void *instanceDataFinalizeHint_ = nullptr; + + /// Optional host integration interface provided by the host + /// application. When non-null, enables async work APIs + /// (napi_queue_async_work, napi_cancel_async_work) and thread-safe + /// functions. When null, those APIs return napi_generic_failure. + /// Owned by the host; must outlive this env. + hermes_napi_host *host_ = nullptr; + + /// Number of currently open callback scopes (for validation in + /// napi_close_callback_scope). + int open_callback_scopes = 0; + + /// Cumulative external memory hint (bytes) reported via + /// napi_adjust_external_memory. Hermes GC does not support global + /// external memory hints (its API requires a specific GCCell), so + /// this counter is maintained for reporting purposes only. + int64_t externalMemory_ = 0; + + /// Head of the doubly-linked list of active thread-safe functions. + /// Used for GC root marking of JS function values. + napi_threadsafe_function__ *tsfnListHead_ = nullptr; + + /// Number of currently referenced thread-safe functions in this env. + /// While > 0, this env holds one host loop reference via host->ref_loop. + /// Incremented by tsfn create (when is_ref) and napi_ref_tsfn; + /// decremented by napi_unref_tsfn and tsfn finalize. + /// + /// The env coalesces all referenced tsfns into a single host + /// ref_loop / unref_loop pair — see the contract on hermes_napi_host. + int activeTsfnLoopRefs_ = 0; + + /// A pending finalizer callback queued during GC for deferred execution. + struct PendingFinalizer { + napi_finalize cb; + void *data; + void *hint; + }; + + /// Queue of finalizer callbacks deferred from GC sweep. GC finalizers + /// must not call back into JS (GC reentrancy), so they queue their + /// callbacks here. The queue is drained at safe points via + /// drainPendingFinalizers(). + std::vector pendingFinalizers_; + + /// Guards pendingFinalizers_ and drainingFinalizers_. Hades GC can queue + /// finalizers from a background thread while the JS thread drains them. + std::mutex pendingFinalizersMutex_; + + /// True while drainPendingFinalizers() is executing, to prevent + /// re-entrant draining (a callback may trigger GC which queues more). + bool drainingFinalizers_ = false; + + /// True while running the final post-finalizeAll drain in the + /// post-shutdown deleter. Set by the deleter before invoking + /// drainPendingFinalizers() and never cleared (the env is deleted + /// immediately after). + /// + /// At this point Runtime::~Runtime has already called + /// getHeap().finalizeAll(), so the GC heap is in a post-mortem + /// state — allocating new JS values or triggering a collection + /// would re-finalize already-finalized cells (UB). NAPI_PREAMBLE + /// returns napi_cannot_run_js when this flag is set, so any + /// finalizer callback that calls back into JS gets a defined + /// error instead of an undefined crash. Spec-conformant + /// finalizers (which only free native resources) ignore the + /// return and complete normally. + bool inPostShutdownDrain_ = false; +}; + +//=========================================================================== +// Internal helper functions +//=========================================================================== + +/// Clear the last error state in the environment. +inline napi_status napi_clear_last_error(node_api_basic_env env) { + env->last_error.error_code = napi_ok; + env->last_error.engine_error_code = 0; + env->last_error.engine_reserved = nullptr; + env->last_error.error_message = nullptr; + return napi_ok; +} + +/// Set the last error state in the environment and return the error code. +inline napi_status napi_set_last_error( + node_api_basic_env env, + napi_status error_code, + uint32_t engine_error_code = 0, + void *engine_reserved = nullptr) { + env->last_error.error_code = error_code; + env->last_error.engine_error_code = engine_error_code; + env->last_error.engine_reserved = engine_reserved; + return error_code; +} + +//=========================================================================== +// Argument checking macros +//=========================================================================== + +/// Return napi_invalid_arg if env is null, or napi_cannot_run_js if +/// the env is in its post-shutdown drain (running queued finalizers +/// after Runtime::~Runtime's finalizeAll). At that point the GC heap +/// is in a post-mortem state; allocating new JS values or calling +/// into JS would re-finalize already-finalized cells. The flag +/// gives finalizer callbacks that try to call back into JS a defined +/// error instead of an undefined crash. Spec-conformant finalizers +/// (which only free native resources) never see this code. +#define CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return napi_invalid_arg; \ + } \ + if ((env)->inPostShutdownDrain_) \ + return napi_cannot_run_js; \ + } while (0) + +/// Return the given status if the condition is false. +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ + } while (0) + +/// Return napi_invalid_arg if the argument is null. +#define CHECK_ARG(env, arg) \ + RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) + +/// Propagate a non-ok status from a NAPI call. +#define STATUS_CALL(call) \ + do { \ + napi_status status = (call); \ + if (status != napi_ok) \ + return status; \ + } while (0) + +/// Preamble for NAPI functions that may call into JavaScript. +/// Validates env, checks for pending exceptions, and clears last error. +/// Functions that are exception-safe (handle scopes, error info, getters +/// for singletons, type checks, value extraction) should use CHECK_ENV +/// instead. +/// +/// CHECK_ENV (called first) returns napi_cannot_run_js if the env is +/// in its post-shutdown drain, so finalizer callbacks that try to call +/// back into JS get a defined error instead of triggering re-finalization. +#define NAPI_PREAMBLE(env) \ + do { \ + CHECK_ENV((env)); \ + (env)->drainPendingFinalizers(); \ + RETURN_STATUS_IF_FALSE( \ + (env), !(env)->hasPendingException, napi_pending_exception); \ + napi_clear_last_error((env)); \ + } while (0) + +#endif // HERMES_NAPI_HERMES_NAPI_IMPL_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_internal.h b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_internal.h new file mode 100644 index 00000000..35510b95 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/hermes/include/napi/hermes_napi_internal.h @@ -0,0 +1,151 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef HERMES_NAPI_HERMES_NAPI_INTERNAL_H +#define HERMES_NAPI_HERMES_NAPI_INTERNAL_H + +#include "hermes_napi_impl.h" + +#include "hermes/VM/Callable.h" +#include "hermes/VM/HandleRootOwner.h" +#include "hermes/VM/StringPrimitive.h" + +/// Helper: capture the runtime's thrown value as a NAPI pending +/// exception and return \p status. +inline napi_status captureRuntimeException(napi_env env, napi_status status) { + env->pendingException = env->runtime.getThrownValue(); + env->hasPendingException = true; + env->runtime.clearThrownValue(); + return napi_set_last_error(env, status); +} + +/// Helper: intern a UTF-8 name as a SymbolID. The name is first +/// created as a StringPrimitive (handling all UTF-8 correctly), then +/// interned via the identifier table. Returns the SymbolID via +/// \p symRes. On failure, captures the runtime exception and returns +/// the error status. Requires an active GCScope. +inline napi_status internUtf8AsSymbol( + napi_env env, + const char *utf8name, + hermes::vm::CallResult> &symRes) { + using namespace hermes::vm; + + Runtime &runtime = env->runtime; + + // Create a StringPrimitive from the UTF-8 name. + auto strRes = StringPrimitive::createEfficient( + runtime, + UTF8Ref( + reinterpret_cast(utf8name), std::strlen(utf8name))); + if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) { + return captureRuntimeException(env, napi_pending_exception); + } + + // Intern the string to get a SymbolID. + symRes = runtime.getIdentifierTable().getSymbolHandleFromPrimitive( + runtime, + PseudoHandle::create(vmcast(*strRes))); + if (LLVM_UNLIKELY(symRes == ExecutionStatus::EXCEPTION)) { + return captureRuntimeException(env, napi_pending_exception); + } + + return napi_ok; +} + +/// Linked list node for finalizer callbacks (napi_add_finalizer). +struct NapiFinalizerEntry { + void *data; + napi_finalize finalize_cb; + void *finalize_hint; + NapiFinalizerEntry *next; +}; + +/// Consolidated per-object NAPI data. One allocation backs all of +/// napi_wrap, napi_add_finalizer, and napi_type_tag_object. +struct NapiObjectData { + napi_env env; + + // -- Wrap (napi_wrap) -- + bool hasWrap = false; + void *wrapData = nullptr; + napi_finalize wrapFinalizeCb = nullptr; + void *wrapFinalizeHint = nullptr; + + // -- Type tag (napi_type_tag_object) -- + bool hasTypeTag = false; + uint64_t typeTagLower = 0; + uint64_t typeTagUpper = 0; + + // -- Finalizer chain (napi_add_finalizer) -- + NapiFinalizerEntry *finalizerChain = nullptr; +}; + +/// Read-only lookup for NapiObjectData on an object. Returns the +/// NapiObjectData pointer in *outData, or nullptr if none exists. +/// Returns napi_invalid_arg if js_object is not an object. +/// Defined in hermes_napi_wrap.cpp. +napi_status +getNapiObjectData(napi_env env, napi_value js_object, NapiObjectData **outData); + +/// Lazy-creation lookup for NapiObjectData on an object. If the +/// internal property is absent, creates a new NapiObjectData and +/// defines the property. Caller must provide an active GCScope. +/// Defined in hermes_napi_wrap.cpp. +napi_status getOrCreateNapiObjectData( + napi_env env, + hermes::vm::Handle objHandle, + NapiObjectData **outData); + +/// The trampoline function that bridges Hermes's NativeFunctionPtr +/// signature with NAPI's napi_callback signature. Defined in +/// hermes_napi_function.cpp but declared here so it can be used from +/// other translation units (e.g., hermes_napi_object.cpp for +/// napi_define_properties). +hermes::vm::CallResult napiFunctionTrampoline( + void *context, + hermes::vm::Runtime &runtime); + +/// Constructor trampoline for NAPI functions. Handles both regular calls +/// and constructor calls (creating a 'this' object when called via 'new'). +/// Defined in hermes_napi_function.cpp. +hermes::vm::CallResult napiConstructorTrampoline( + void *context, + hermes::vm::Runtime &runtime); + +/// Helper: create a NativeFunction wrapping a napi_callback for use as +/// a method, getter, or setter in napi_define_properties and +/// napi_define_class. The callback bundle is stored in the env's deque +/// for lifetime management. Requires an active GCScope. Defined in +/// hermes_napi_object.cpp. +napi_status createNapiNativeFunction( + napi_env env, + napi_callback cb, + void *data, + hermes::vm::SymbolID name, + unsigned paramCount, + hermes::vm::PseudoHandle &out); + +/// Check if a HermesValue is a NAPI external (DecoratedObject, not +/// HostObject). Defined in hermes_napi_external.cpp. +bool isNapiExternal(hermes::vm::HermesValue hv); + +/// Set the type tag on a NAPI external value. Returns napi_invalid_arg +/// if the tag is already set. Defined in hermes_napi_external.cpp. +napi_status napiExternalSetTypeTag( + napi_env env, + hermes::vm::HermesValue hv, + const napi_type_tag *type_tag); + +/// Check the type tag on a NAPI external value. Sets *result to true +/// if the tag matches. Defined in hermes_napi_external.cpp. +napi_status napiExternalCheckTypeTag( + napi_env env, + hermes::vm::HermesValue hv, + const napi_type_tag *type_tag, + bool *result); + +#endif // HERMES_NAPI_HERMES_NAPI_INTERNAL_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/AsyncDebuggerAPI.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/AsyncDebuggerAPI.h deleted file mode 100644 index ea718dd4..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/AsyncDebuggerAPI.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_ASYNCDEBUGGERAPI_H -#define HERMES_ASYNCDEBUGGERAPI_H - -#ifdef HERMES_ENABLE_DEBUGGER - -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(__clang__) && (!defined(SWIG)) && defined(_LIBCPP_VERSION) && \ - defined(_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) -#include -#else -#ifndef TSA_GUARDED_BY -#define TSA_GUARDED_BY(x) -#endif -#ifndef TSA_NO_THREAD_SAFETY_ANALYSIS -#define TSA_NO_THREAD_SAFETY_ANALYSIS -#endif -#endif - -namespace facebook { -namespace hermes { -namespace debugger { - -class AsyncDebuggerAPI; - -enum class DebuggerEventType { - // Informational Events - ScriptLoaded, /// A script file was loaded, and the debugger has requested - /// pausing after script load. - Exception, /// An Exception was thrown. - Resumed, /// Script execution has resumed. - - // Events Requiring Next Command - DebuggerStatement, /// A debugger; statement was hit. - Breakpoint, /// A breakpoint was hit. - StepFinish, /// A Step operation completed. - ExplicitPause, /// A pause requested using Explicit AsyncBreak -}; - -/// This represents the list of possible commands that can be given to -/// \p resumeFromPaused. This is used instead of DebuggerAPI's Command class in -/// order to prevent callers from constructing an eval Command. The eval -/// functionality is implemented as a separate mechansim with -/// \p evalWhilePaused. -enum class AsyncDebugCommand { - Continue, /// Continues execution - StepInto, /// Perform a step into and then pause again - StepOver, /// Steps over the current instruction and then pause again - StepOut, /// Step out from the current scope and then pause again -}; - -using DebuggerEventCallback = std::function; -using DebuggerEventCallbackID = uint32_t; -constexpr const uint32_t kInvalidDebuggerEventCallbackID = 0; -using InterruptCallback = std::function; -using EvalCompleteCallback = std::function< - void(HermesRuntime &runtime, const debugger::EvalResult &result)>; - -/// This class wraps the DebuggerAPI to expose an asynchronous didPause -/// functionality as well as an interrupt API. This class must be constructed at -/// the same time as HermesRuntime. -/// -/// Functions in this class with the suffix "_TS" (Thread-Safe) are the only -/// functions that are safe to call on any thread. All other functions must be -/// called on the runtime thread. -class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { - /// Hide the constructor so users can only construct via static create - /// methods. - AsyncDebuggerAPI(HermesRuntime &runtime); - - public: - /// Creates an AsyncDebuggerAPI for use with the provided HermesRuntime. This - /// should be called and created at the same time as creating HermesRuntime. - static std::unique_ptr create(HermesRuntime &runtime); - - /// Must be destroyed on the runtime thread or when you're sure nothing is - /// interacting with the runtime. Must be destroyed before destroying - /// HermesRuntime. - ~AsyncDebuggerAPI() override; - - /// Add a callback function to invoke when the runtime pauses due to various - /// conditions such as hitting a "debugger;" statement. Can be called from any - /// thread. If there are no DebuggerEventCallback, then any reason that might - /// trigger a pause, such as a "debugger;" statement or breakpoints, will not - /// actually pause and will simply continue execution. Any caller that adds an - /// event callback cannot just be observing events and never call - /// \p resumeFromPaused in any of its code paths. The caller must either - /// expose UI enabling human action for controlling the debugger, or it must - /// have programmatic logic that controls the debugger via - /// \p resumeFromPaused. - DebuggerEventCallbackID addDebuggerEventCallback_TS( - DebuggerEventCallback callback); - - /// Remove a previously added callback function. If there is no callback - /// registered using the provided \p id, the function does nothing. - void removeDebuggerEventCallback_TS(DebuggerEventCallbackID id); - - /// Whether the runtime is currently paused waiting for the next action. - /// Should only be called from the runtime thread. - bool isWaitingForCommand(); - - /// Whether the runtime is currently paused for any reason (e.g. script - /// parsed, running interrupts, or waiting for a command). - /// Should only be called from the runtime thread. - bool isPaused(); - - /// Provide the next action to perform. Should only be called from the runtime - /// thread and only if the next command is expected to be set. - bool resumeFromPaused(AsyncDebugCommand command); - - /// Evaluate JavaScript code \p expression in the frame at index - /// \p frameIndex. Receives evaluation result in the \p callback. Should only - /// be called from the runtime thread and only if debugger is paused waiting - /// for the next action. - bool evalWhilePaused( - const std::string &expression, - uint32_t frameIndex, - EvalCompleteCallback callback); - - /// Request to interrupt the runtime at a convenient time and get a callback - /// on the runtime thread. Guaranteed to run "exactly once". This function can - /// be called from any thread, but cannot be called while inside a - /// DebuggerEventCallback. - void triggerInterrupt_TS(InterruptCallback callback); - - /// EventObserver implementation - debugger::Command didPause(debugger::Debugger &debugger) override; - - private: - struct EventCallbackEntry { - DebuggerEventCallbackID id; - DebuggerEventCallback callback; - }; - - /// This function infinite loops and uses \p signal_ to block the runtime - /// thread. It gets woken up if new InterruptCallback is queued or if - /// DebuggerEventCallback changes. - void processInterruptWhilePaused() TSA_NO_THREAD_SAFETY_ANALYSIS; - - /// Dequeues the next InterruptCallback if any. - std::optional takeNextInterruptCallback(); - - /// If \p ignoreNextCommand is true, then runs every InterruptCallback that - /// has been queued up so far. If \p ignoreNextCommand is false, then attempt - /// to run all interrupts, but will stop if any interrupt sets a next command. - void runInterrupts(bool ignoreNextCommand = true); - - /// Returns the next DebuggerEventCallback to execute if any. - std::optional takeNextEventCallback(); - - /// Runs every DebuggerEventCallback that has been registered. - void runEventCallbacks(DebuggerEventType event); - - HermesRuntime &runtime_; - - /// Whether the runtime thread is currently paused in \p didPause and needs to - /// be told what action to take next. - bool isWaitingForCommand_; - - /// Stores the command to return from \p didPause. - debugger::Command nextCommand_; - - /// Callback function to invoke after getting EvalResult from EvalComplete in - /// didPause. Used once and then cleared out. - EvalCompleteCallback oneTimeEvalCompleteCallback_{}; - - /// Tracks whether we are already in a didPause callback to detect recursive - /// calls to didPause. - bool inDidPause_ = false; - - /// Next ID to use when adding a DebuggerEventCallback. - uint32_t nextEventCallbackID_ TSA_GUARDED_BY(mutex_); - - /// Callback functions to invoke to notify events in \p didPause. Using - /// std::list which requires O(N) search when removing an element, but removal - /// should be a rare event. So the choice of using std::list is to optimize - /// for typical usage. - std::list eventCallbacks_ TSA_GUARDED_BY(mutex_){}; - - /// Iterator for eventCallbacks_. Used to traverse through the list when - /// running the callbacks. - std::list::iterator eventCallbackIterator_ - TSA_GUARDED_BY(mutex_); - - /// Queue of interrupt callback functions to invoke. - std::queue interruptCallbacks_ TSA_GUARDED_BY(mutex_){}; - - /// Used as a mechanism to block the runtime thread in \p didPause and for - /// protecting variables used across threads. - std::mutex mutex_{}; - /// Used to implement \p triggerInterrupt while \p didPause is holding onto - /// the runtime thread. - std::condition_variable signal_{}; -}; - -} // namespace debugger -} // namespace hermes -} // namespace facebook - -#else // !HERMES_ENABLE_DEBUGGER - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace debugger { - -class AsyncDebuggerAPI; - -enum class DebuggerEventType { - // Informational Events - ScriptLoaded, /// A script file was loaded, and the debugger has requested - /// pausing after script load. - Exception, /// An Exception was thrown. - Resumed, /// Script execution has resumed. - - // Events Requiring Next Command - DebuggerStatement, /// A debugger; statement was hit. - Breakpoint, /// A breakpoint was hit. - StepFinish, /// A Step operation completed. - ExplicitPause, /// A pause requested using Explicit AsyncBreak -}; - -/// This represents the list of possible commands that can be given to -/// \p resumeFromPaused. This is used instead of DebuggerAPI's Command class in -/// order to prevent callers from constructing an eval Command. The eval -/// functionality is implemented as a separate mechansim with -/// \p evalWhilePaused. -enum class AsyncDebugCommand { - Continue, /// Continues execution - StepInto, /// Perform a step into and then pause again - StepOver, /// Steps over the current instruction and then pause again - StepOut, /// Step out from the current scope and then pause again -}; - -using DebuggerEventCallback = std::function; -using DebuggerEventCallbackID = uint32_t; -constexpr const uint32_t kInvalidDebuggerEventCallbackID = 0; -using InterruptCallback = std::function; -using EvalCompleteCallback = std::function< - void(HermesRuntime &runtime, const debugger::EvalResult &result)>; - -class HERMES_EXPORT AsyncDebuggerAPI { - public: - static std::unique_ptr create(HermesRuntime &runtime) { - return nullptr; - } - - ~AsyncDebuggerAPI() {} - - DebuggerEventCallbackID addDebuggerEventCallback_TS( - DebuggerEventCallback callback) { - return kInvalidDebuggerEventCallbackID; - } - - void removeDebuggerEventCallback_TS(DebuggerEventCallbackID id) {} - - bool isWaitingForCommand() { - return false; - } - - bool isPaused() { - return false; - } - - bool resumeFromPaused(AsyncDebugCommand command) { - return false; - } - - bool evalWhilePaused( - const std::string &expression, - uint32_t frameIndex, - EvalCompleteCallback callback) { - return false; - } - - void triggerInterrupt_TS(InterruptCallback callback) {} -}; - -} // namespace debugger -} // namespace hermes -} // namespace facebook - -#endif // !HERMES_ENABLE_DEBUGGER - -#endif // HERMES_ASYNCDEBUGGERAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/CompileJS.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/CompileJS.h deleted file mode 100644 index 68db11a7..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/CompileJS.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_COMPILEJS_H -#define HERMES_COMPILEJS_H - -#include -#include -#include - -namespace hermes { - -/// Interface for receiving errors, warnings and notes produced by compileJS. -class DiagnosticHandler { - public: - enum Kind { - Error, - Warning, - Note, - }; - - struct Diagnostic { - Kind kind; - int line; /// 1-based index - int column; /// 1-based index - std::string message; - /// 0-based char indices in half-open intervals - std::vector> ranges; - }; - - /// Called once for each diagnostic message produced during compilation. - virtual void handle(const Diagnostic &diagnostic) = 0; - virtual ~DiagnosticHandler() = default; -}; - -/// Compiles JS source \p str and if compilation is successful, returns true -/// and outputs to \p bytecode otherwise returns false. -/// \param sourceURL this will be used as the "file name" of the buffer for -/// errors, stack traces, etc. -/// \param optimize this will enable optimizations. -/// \param emitAsyncBreakCheck this will make the bytecode interruptable. -/// \param diagHandler if not null, receives any and all errors, warnings and -/// notes produced during compilation. -/// \param sourceMapBuf optional source map string. -/// \param debug Wether to generate debugging information in generated bytecode. -bool compileJS( - const std::string &str, - const std::string &sourceURL, - std::string &bytecode, - bool optimize, - bool emitAsyncBreakCheck, - DiagnosticHandler *diagHandler, - std::optional sourceMapBuf = std::nullopt, - bool debug = false); - -bool compileJS( - const std::string &str, - std::string &bytecode, - bool optimize = true); - -bool compileJS( - const std::string &str, - const std::string &sourceURL, - std::string &bytecode, - bool optimize = true); - -/// Options for overload of compileJS that accepts CompileJSOptions. -struct CompileJSOptions { - /// If true, the bytecode will be optimized. - bool optimize{true}; - /// Maximum number of instructions (in addition to parameter handling) - /// that is allowed for inlining of small functions. - unsigned inlineMaxSize{50}; - /// If true, the bytecode will be interruptable. - bool emitAsyncBreakCheck{false}; - /// If true, debugging information will be generated in the bytecode. - bool debug{false}; -}; - -/// Like the other compileJS overloads, but takes a struct of options with some -/// additional configurability. -bool compileJS( - const std::string &str, - const std::string &sourceURL, - std::string &bytecode, - const CompileJSOptions &options, - DiagnosticHandler *diagHandler, - std::optional sourceMapBuf = std::nullopt); - -} // namespace hermes - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/DebuggerAPI.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/DebuggerAPI.h deleted file mode 100644 index 61b0c48b..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/DebuggerAPI.h +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_DEBUGGERAPI_H -#define HERMES_DEBUGGERAPI_H - -#ifdef HERMES_ENABLE_DEBUGGER - -#include -#include -#include -#include - -#include "hermes/Public/DebuggerTypes.h" - -// Forward declarations of internal types. -namespace hermes { -namespace vm { -class CodeBlock; -class Debugger; -class Runtime; -struct DebugCommand; -class HermesValue; -} // namespace vm -} // namespace hermes - -namespace facebook { -namespace hermes { -class HermesRuntime; -// Forward declaration of the internal Root API class, which is marked as a -// friend of the Debugger. -class HermesRootAPI; - -namespace debugger { - -class Debugger; -class EventObserver; - -/// Represents a variable in the debugger. -struct HERMES_EXPORT VariableInfo { - /// Name of the variable in the source. - String name; - - /// Value of the variable. - ::facebook::jsi::Value value; -}; - -/// An EvalResult represents the result of an Eval command. -struct HERMES_EXPORT EvalResult { - /// The resulting JavaScript object, or the thrown exception. - ::facebook::jsi::Value value; - - /// Indicates that the result was an exception. - bool isException = false; - - /// If isException is true, details about the exception. - ExceptionDetails exceptionDetails; - - EvalResult(EvalResult &&) = default; - EvalResult() = default; - - EvalResult( - ::facebook::jsi::Value value, - bool isException, - ExceptionDetails exceptionDetails) - : value(std::move(value)), - isException(isException), - exceptionDetails(std::move(exceptionDetails)) {} -}; - -/// ProgramState represents the state of a paused program. An instance of -/// ProgramState is available as the getProgramState() member function of class -/// Debugger. -class HERMES_EXPORT ProgramState { - public: - /// \return the reason for the Pause. - PauseReason getPauseReason() const { - return pauseReason_; - } - - /// \return the breakpoint if the PauseReason is Breakpoint, otherwise - /// kInvalidBreakpoint. - BreakpointID getBreakpoint() const { - return breakpoint_; - } - - /// \return the evaluation result if the PauseReason is due to EvalComplete. - EvalResult getEvalResult() const; - - /// \returns a stack trace for the current execution. - const StackTrace &getStackTrace() const { - return stackTrace_; - } - - /// \returns lexical information about the state in a given frame. - LexicalInfo getLexicalInfo(uint32_t frameIndex) const; - - /// \return information about a variable in a given lexical scope, in a given - /// frame. - VariableInfo getVariableInfo( - uint32_t frameIndex, - ScopeDepth scopeDepth, - uint32_t variableIndexInScope) const; - - /// \return information about the `this` value at a given stack depth. - VariableInfo getVariableInfoForThis(uint32_t frameIndex) const; - - /// \return the number of variables in a given frame. - /// This is deprecated: prefer using getLexicalInfoInFrame(). - uint32_t getVariablesCountInFrame(uint32_t frameIndex) const { - auto info = getLexicalInfo(frameIndex); - uint32_t result = 0; - for (ScopeDepth i = 0, max = info.getScopesCount(); i < max; i++) - result += info.getVariablesCountInScope(i); - return result; - } - - /// \return info for a variable at a given index \p variableIndex, in a given - /// frame at index \p frameIndex. - /// This is deprecated. Prefer the getVariableInfo() that takes three - /// parameters. - VariableInfo getVariableInfo(uint32_t frameIndex, uint32_t variableIndex) - const { - LexicalInfo info = getLexicalInfo(frameIndex); - uint32_t remaining = variableIndex; - for (ScopeDepth scope = 0;; scope++) { - assert(scope < info.getScopesCount() && "Index out of bounds"); - uint32_t count = info.getVariablesCountInScope(scope); - if (remaining < count) { - return getVariableInfo(frameIndex, scope, remaining); - } - remaining -= count; - } - } - - private: - friend Debugger; - /// ProgramState must not be copied, because some of its implementation - /// requires querying the live program state and so the state must not be - /// retained after the pause returns. - /// ProgramState must not be copied. - ProgramState(const ProgramState &) = delete; - ProgramState &operator=(const ProgramState &) = delete; - - ::hermes::vm::Debugger *impl() const; - - ProgramState(Debugger *dbg) : dbg_(dbg) {} - Debugger *dbg_; - PauseReason pauseReason_{}; - StackTrace stackTrace_; - EvalResult evalResult_; - BreakpointID breakpoint_{kInvalidBreakpoint}; -}; - -/// Command represents an action that you can request the debugger to perform -/// when returned from didPause(). -class HERMES_EXPORT Command { - public: - /// Commands may be moved. - Command(Command &&); - Command &operator=(Command &&); - ~Command(); - - /// \return a Command that steps with the given StepMode \p mode. - static Command step(StepMode mode); - - /// \return a Command that continues execution. - static Command continueExecution(); - - /// \return a Command that evaluates JavaScript code \p src in the - /// frame at index \p frameIndex. - static Command eval(const String &src, uint32_t frameIndex); - - /// \return a boolean whether this Command was constructed using the static - /// eval() method - bool isEval(); - - private: - friend Debugger; - explicit Command(::hermes::vm::DebugCommand &&); - std::unique_ptr<::hermes::vm::DebugCommand> debugCommand_; -}; - -/// Debugger allows access to the Hermes debugging functionality. An instance of -/// Debugger is available from HermesRuntime, and also passed to your -/// EventObserver. -class HERMES_EXPORT Debugger { - public: - /// Set the Debugger event observer. The event observer is notified of - /// debugging event, specifically when the program pauses. This is simply a - /// raw pointer: it is the client's responsibility to clear the event observer - /// if the event observer is deallocated before the Debugger. - void setEventObserver(EventObserver *observer); - - /// Sets the property %isDebuggerAttached in %DebuggerInternal object. Can be - /// called from any thread. - void setIsDebuggerAttached(bool isAttached); - - /// Asynchronously triggers a pause. This may be called from any thread. This - /// is inherently racey and the exact point at which the program pauses is not - /// guaranteed. You can discover when the program has paused through the event - /// observer. - void triggerAsyncPause(AsyncPauseKind kind); - - /// \return the ProgramState representing the state of the paused program. - /// This may only be invoked when the program is paused. - const ProgramState &getProgramState() const { - return state_; - } - - /// \return the source map URL for the \p fileId. - String getSourceMappingUrl(uint32_t fileId) const; - - /// Gets the list of loaded scripts. The order of the scripts in the vector - /// will be the same across calls. - /// \return list of loaded scripts - std::vector getLoadedScripts() const; - - /// Gets the current stack trace. - /// \return stack trace with call frames if runtime is in the interpreter - /// loop, otherwise return no call frames - StackTrace captureStackTrace() const; - - /// -- Breakpoint Management -- - - /// Sets a breakpoint on a given SourceLocation. - /// \return the ID of the breakpoint, 0 if it wasn't created. - BreakpointID setBreakpoint(SourceLocation loc); - - /// Sets the condition on breakpoint \p breakpoint. - /// The condition will be stored with the breakpoint, - /// and if non-empty, will be executed to determine whether to actually - /// pause on the breakpoint; only if ToBoolean(condition) is true - /// and does not throw will the debugger pause on \p breakpoint. - /// \param condition the code to execute to determine whether to break; - /// if empty, the condition is considered to not be set. - void setBreakpointCondition(BreakpointID breakpoint, const String &condition); - - /// Deletes a breakpoint. - void deleteBreakpoint(BreakpointID breakpoint); - - /// Deletes all breakpoints. - void deleteAllBreakpoints(); - - /// Mark a breakpoint as enabled. Breakpoints are by default enabled. - void setBreakpointEnabled(BreakpointID breakpoint, bool enable); - - /// \return information on a breakpoint. - BreakpointInfo getBreakpointInfo(BreakpointID breakpoint); - - /// \return a list of extant breakpoints. - std::vector getBreakpoints(); - - /// Set whether the debugger should pause when an exception is thrown. - void setPauseOnThrowMode(PauseOnThrowMode mode); - - /// \return whether the debugger pauses when an exception is thrown. - PauseOnThrowMode getPauseOnThrowMode() const; - - /// Set whether the debugger should pause after a script was loaded. - void setShouldPauseOnScriptLoad(bool flag); - - /// \return whether the debugger should pause after a script was loaded. - bool getShouldPauseOnScriptLoad() const; - - /// \return the thrown value if paused on an exception, or - /// jsi::Value::undefined() if not. - ::facebook::jsi::Value getThrownValue(); - - private: - friend HermesRootAPI; - friend std::unique_ptr hermes::makeHermesRuntime( - const ::hermes::vm::RuntimeConfig &); - friend std::unique_ptr - hermes::makeThreadSafeHermesRuntime(const ::hermes::vm::RuntimeConfig &); - friend ProgramState; - - /// Debuggers may not be moved or copied. - Debugger(const Debugger &) = delete; - void operator=(const Debugger &) = delete; - Debugger(Debugger &&) = delete; - void operator=(Debugger &&) = delete; - - /// Implementation detail used by ProgramState. - ::facebook::jsi::Value jsiValueFromHermesValue(::hermes::vm::HermesValue hv); - - explicit Debugger( - ::facebook::hermes::HermesRuntime *runtime, - ::hermes::vm::Runtime &vmRuntime); - - ::facebook::hermes::HermesRuntime *const runtime_; - EventObserver *eventObserver_ = nullptr; - ::hermes::vm::Runtime &vmRuntime_; - ::hermes::vm::Debugger *impl_; - ProgramState state_; -}; - -/// A subclass of EventObserver may be set on the Debugger via -/// setEventObserver(). It receives notifications when the Debugger pauses. -class HERMES_EXPORT EventObserver { - public: - /// didPause() is invoked when the JavaScript program has paused. The - /// The Debugger \p debugger can be used to manipulate breakpoints and enqueue - /// debugger commands such as stepping, etc. It can also be used to discover - /// the call stack and variables via debugger.getProgramState(). - /// \return a Command for the debugger to perform. - virtual Command didPause(Debugger &debugger) = 0; - - /// Invoked when the debugger resolves a previously unresolved breakpoint. - /// Note that the debugger is *not* paused during this, - /// and thus debugger.getProgramState() is not valid. - /// This callback may not invoke JavaScript or enqueue debugger commands. - virtual void breakpointResolved(Debugger &debugger, BreakpointID breakpoint) { - } - - virtual ~EventObserver(); -}; - -} // namespace debugger -} // namespace hermes -} // namespace facebook - -#else // !HERMES_ENABLE_DEBUGGER - -#include - -#include "hermes/Public/DebuggerTypes.h" - -namespace facebook { -namespace hermes { -namespace debugger { - -class EventObserver; - -struct VariableInfo { - String name; - ::facebook::jsi::Value value; -}; - -struct EvalResult { - ::facebook::jsi::Value value; - bool isException = false; - ExceptionDetails exceptionDetails; - - EvalResult(EvalResult &&) = default; - EvalResult() = default; - - EvalResult( - ::facebook::jsi::Value value, - bool isException, - ExceptionDetails exceptionDetails) - : value(std::move(value)), - isException(isException), - exceptionDetails(std::move(exceptionDetails)) {} -}; - -class ProgramState { - public: - ProgramState() {} - - PauseReason getPauseReason() const { - return PauseReason::Exception; - } - - BreakpointID getBreakpoint() const { - return 0; - } - - EvalResult getEvalResult() const { - return EvalResult(); - } - - const StackTrace &getStackTrace() const { - return stackTrace_; - } - - LexicalInfo getLexicalInfo(uint32_t frameIndex) const { - return LexicalInfo(); - } - - VariableInfo getVariableInfo( - uint32_t frameIndex, - ScopeDepth scopeDepth, - uint32_t variableIndexInScope) const { - return VariableInfo(); - } - - VariableInfo getVariableInfoForThis(uint32_t frameIndex) const { - return VariableInfo(); - } - - uint32_t getVariablesCountInFrame(uint32_t frameIndex) const { - return 0; - } - - VariableInfo getVariableInfo(uint32_t frameIndex, uint32_t variableIndex) - const { - return VariableInfo(); - } - - private: - ProgramState(const ProgramState &) = delete; - ProgramState &operator=(const ProgramState &) = delete; - - StackTrace stackTrace_; -}; - -class Command { - public: - Command(Command &&) {} - Command &operator=(Command &&); - ~Command() {} - - static Command step(StepMode mode) { - return Command(); - } - static Command continueExecution() { - return Command(); - } - static Command eval(const String &src, uint32_t frameIndex) { - return Command(); - } - bool isEval() { - return false; - } - - private: - Command() {} -}; - -class Debugger { - public: - explicit Debugger() {} - - void setEventObserver(EventObserver *observer) {} - void setIsDebuggerAttached(bool isAttached) {} - void triggerAsyncPause(AsyncPauseKind kind) {} - const ProgramState &getProgramState() const { - return programState_; - } - String getSourceMappingUrl(uint32_t fileId) const { - return ""; - }; - std::vector getLoadedScripts() const { - return {}; - } - StackTrace captureStackTrace() const { - return StackTrace{}; - } - BreakpointID setBreakpoint(SourceLocation loc) { - return 0; - } - void setBreakpointCondition( - BreakpointID breakpoint, - const String &condition) {} - void deleteBreakpoint(BreakpointID breakpoint) {} - void deleteAllBreakpoints() {} - void setBreakpointEnabled(BreakpointID breakpoint, bool enable) {} - BreakpointInfo getBreakpointInfo(BreakpointID breakpoint) { - return BreakpointInfo(); - } - std::vector getBreakpoints() { - return std::vector(); - } - void setPauseOnThrowMode(PauseOnThrowMode mode) {} - PauseOnThrowMode getPauseOnThrowMode() const { - return PauseOnThrowMode::None; - } - void setShouldPauseOnScriptLoad(bool flag) {} - bool getShouldPauseOnScriptLoad() const { - return false; - } - ::facebook::jsi::Value getThrownValue() { - return ::facebook::jsi::Value::undefined(); - } - - private: - Debugger(const Debugger &) = delete; - void operator=(const Debugger &) = delete; - Debugger(Debugger &&) = delete; - void operator=(Debugger &&) = delete; - - ProgramState programState_; -}; - -class EventObserver { - public: - virtual Command didPause(Debugger &debugger) = 0; - virtual void breakpointResolved(Debugger &debugger, BreakpointID breakpoint) { - } - - virtual ~EventObserver() {} -}; - -} // namespace debugger -} // namespace hermes -} // namespace facebook - -#endif // !HERMES_ENABLE_DEBUGGER - -#endif // HERMES_DEBUGGERAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/CrashManager.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/CrashManager.h deleted file mode 100644 index 07a9b592..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/CrashManager.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_CRASHMANAGER_H -#define HERMES_PUBLIC_CRASHMANAGER_H - -#include - -#include -#include - -namespace hermes { -namespace vm { - -/// A CrashManager provides functions that determine what memory and data is -/// included in dumps in case of crashes. -class HERMES_EXPORT CrashManager { - public: - /// CallbackKey is the type of an identifier for a callback supplied to the - /// CrashManager. - using CallbackKey = int; - /// Type for the callback function invoked on crash. The fd supplied is a raw - /// file stream an implementation should write a JSON object to. - using CallbackFunc = std::function; - - /// Registers some memory to be included in any crash dump that occurs. - /// \param mem A pointer to allocated memory. It must be unregistered - /// before being freed. - /// \param length The number of bytes the memory controls. - virtual void registerMemory(void *mem, size_t length) = 0; - - /// Unregisters some memory from being included in any crash dump that occurs. - virtual void unregisterMemory(void *mem) = 0; - - /// Registers custom data to be included in any crash dump that occurs. - /// Calling \c setCustomData on the same key twice will overwrite the previous - /// value. - /// \param key A tag to look for in the custom data output. Distinguishes - /// between multiple values. - /// \param val The value to store for the given key. - virtual void setCustomData(const char *key, const char *val) = 0; - - /// If the given \p key has an associated custom data string, remove the - /// association. If the key hasn't been set before, is a no-op. - virtual void removeCustomData(const char *key) = 0; - - /// Same as \c setCustomData, except it is only set for the current thread. - virtual void setContextualCustomData(const char *key, const char *val) = 0; - - /// Same as \c removeCustomData, except it is for keys set with \c - /// setContextualCustomData. - virtual void removeContextualCustomData(const char *key) = 0; - - /// Registers a function to be called after a crash has occurred. This - /// function can examine memory and serialize this to a JSON output stream. - /// Implmentations decide where the stream is routed to. - /// \param callback A function to called after a crash. - /// \return A CallbackKey representing the function you provided. Pass this - /// key into unregisterCallback when it that callback is no longer needed. - virtual CallbackKey registerCallback(CallbackFunc callback) = 0; - - /// Unregisters a previously registered callback. After this function returns, - /// the previously registered function will not be executed by this - /// CrashManager during a crash. - virtual void unregisterCallback(CallbackKey key) = 0; - - /// the heap information. - struct HeapInformation { - /// The amount of memory that is currently in use - size_t used_{0}; - /// The amount of memory that can currently be allocated - /// before a full GC is triggered. - size_t size_{0}; - }; - - /// Record the heap information. - /// \param heapInfo The current heap information - virtual void setHeapInfo(const HeapInformation &heapInfo) = 0; - - virtual ~CrashManager(); -}; - -/// A CrashManager that does nothing. -class HERMES_EXPORT NopCrashManager final : public CrashManager { - public: - void registerMemory(void *, size_t) override {} - void unregisterMemory(void *) override {} - void setCustomData(const char *, const char *) override {} - void removeCustomData(const char *) override {} - void setContextualCustomData(const char *, const char *) override {} - void removeContextualCustomData(const char *) override {} - CallbackKey registerCallback(CallbackFunc /*callback*/) override { - return 0; - } - void unregisterCallback(CallbackKey /*key*/) override {} - void setHeapInfo(const HeapInformation & /*heapInfo*/) override {} - - ~NopCrashManager() override; -}; - -} // namespace vm -} // namespace hermes -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/CtorConfig.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/CtorConfig.h deleted file mode 100644 index aff3f398..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/CtorConfig.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_CTORCONFIG_H -#define HERMES_PUBLIC_CTORCONFIG_H - -#include - -/// Defines a new class, called \p NAME representing a constructor config, and -/// an associated builder class. -/// -/// The fields of the class (along with their types and default values) are -/// encoded in the \p FIELDS parameter, and any logic to be run whilst building -/// the config can be passed as a code block in \p BUILD_BODY. -/// -/// Example: -/// -/// Suppose we wish to define a configuration class called Foo, with the -/// following fields and default values: -/// -/// int A = 0; -/// int B = 42; -/// std::string C = "hello"; -/// -/// Such that the value in A is at most the length of \c C. -/// -/// We can do so with the following declaration: -/// -/// " #define FIELDS(F) \ " -/// " F(int, A) \ " -/// " F(int, B, 42) \ " -/// " F(std::string, C, "hello") " -/// " " -/// " _HERMES_CTORCONFIG_STRUCT(Foo, FIELDS, { " -/// " A_ = std::min(A_, C_.length()); " -/// " }); " -/// -/// N.B. -/// - The definition of A does not mention any value -- meaning it is -/// default initialised. -/// - References to the fields in the validation logic have a trailling -/// underscore. -/// -#define _HERMES_CTORCONFIG_STRUCT(NAME, FIELDS, BUILD_BODY) \ - class NAME { \ - FIELDS(_HERMES_CTORCONFIG_FIELD_DECL) \ - \ - public: \ - class Builder; \ - friend Builder; \ - FIELDS(_HERMES_CTORCONFIG_GETTER) \ - \ - /* returns a Builder that starts with the current config. */ \ - inline Builder rebuild() const; \ - \ - private: \ - inline void doBuild(const Builder &builder); \ - }; \ - \ - class NAME::Builder { \ - NAME config_; \ - \ - FIELDS(_HERMES_CTORCONFIG_FIELD_EXPLICIT_BOOL_DECL) \ - \ - public: \ - Builder() = default; \ - \ - explicit Builder(const NAME &config) : config_(config) {} \ - \ - inline const NAME build() { \ - config_.doBuild(*this); \ - return config_; \ - } \ - \ - /* The explicitly set fields of \p newconfig update \ - * the corresponding fields of \p this. */ \ - inline Builder update(const NAME::Builder &newConfig); \ - \ - FIELDS(_HERMES_CTORCONFIG_SETTER) \ - FIELDS(_HERMES_CTORCONFIG_FIELD_EXPLICIT_BOOL_ACCESSOR) \ - }; \ - \ - NAME::Builder NAME::rebuild() const { \ - return Builder(*this); \ - } \ - \ - NAME::Builder NAME::Builder::update(const NAME::Builder &newConfig) { \ - FIELDS(_HERMES_CTORCONFIG_UPDATE) \ - return *this; \ - } \ - \ - void NAME::doBuild(const NAME::Builder &builder) { \ - (void)builder; \ - BUILD_BODY \ - } - -/// Helper Macros - -#define _HERMES_CTORCONFIG_FIELD_DECL(CX, TYPE, NAME, ...) \ - TYPE NAME##_{__VA_ARGS__}; - -/// This ignores the first and trailing arguments, and defines a member -/// indicating whether field NAME was set explicitly. -#define _HERMES_CTORCONFIG_FIELD_EXPLICIT_BOOL_DECL(CX, TYPE, NAME, ...) \ - bool NAME##Explicit_{false}; - -/// This defines an accessor for the "Explicit_" fields defined above. -#define _HERMES_CTORCONFIG_FIELD_EXPLICIT_BOOL_ACCESSOR(CX, TYPE, NAME, ...) \ - bool has##NAME() const { \ - return NAME##Explicit_; \ - } - -/// Placeholder token for fields whose defaults are not constexpr, to make the -/// listings more readable. -#define HERMES_NON_CONSTEXPR - -#define _HERMES_CTORCONFIG_GETTER(CX, TYPE, NAME, ...) \ - inline TYPE get##NAME() const { \ - return NAME##_; \ - } \ - static CX TYPE getDefault##NAME() { \ - /* Instead of parens around TYPE (non-standard) */ \ - using TypeAsSingleToken = TYPE; \ - return TypeAsSingleToken{__VA_ARGS__}; \ - } - -#define _HERMES_CTORCONFIG_SETTER(CX, TYPE, NAME, ...) \ - inline auto with##NAME(TYPE NAME)->decltype(*this) { \ - config_.NAME##_ = std::move(NAME); \ - NAME##Explicit_ = true; \ - return *this; \ - } - -#define _HERMES_CTORCONFIG_BUILDER_GETTER(CX, TYPE, NAME, ...) \ - TYPE get##NAME() const { \ - return config_.NAME##_; \ - } - -#define _HERMES_CTORCONFIG_UPDATE(CX, TYPE, NAME, ...) \ - if (newConfig.has##NAME()) { \ - with##NAME(newConfig.config_.get##NAME()); \ - } - -#endif // HERMES_PUBLIC_CTORCONFIG_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/DebuggerTypes.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/DebuggerTypes.h deleted file mode 100644 index 0763c2e9..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/DebuggerTypes.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_DEBUGGERTYPES_H -#define HERMES_PUBLIC_DEBUGGERTYPES_H - -#include -#include -#include - -namespace hermes { -namespace vm { -class Debugger; -} -} // namespace hermes - -namespace facebook { -namespace hermes { -namespace debugger { - -class ProgramState; - -/// Strings in the Debugger are UTF-8 encoded. When converting from a JavaScript -/// string, valid UTF-16 surrogate pairs are decoded. Surrogate halves are -/// converted into the Unicode replacement character. -using String = std::string; - -/// Debugging entities like breakpoints are identified by a unique ID. The -/// Debugger will not re-use IDs even across different entity types. 0 is an -/// invalid ID. -using BreakpointID = uint64_t; -// NOTE: Can't be kInvalidID due to a clash with MacTypes.h's define kInvalidID. -constexpr uint64_t kInvalidBreakpoint = 0; - -/// Scripts when loaded are identified by a script ID. -/// These are not reused within one invocation of the VM. -using ScriptID = uint32_t; - -/// A SourceLocation is a small value-type representing a location in a source -/// file. -constexpr uint32_t kInvalidLocation = ~0u; -struct SourceLocation { - /// Line in the source. 1 based. - uint32_t line = kInvalidLocation; - - /// Column in the source. 1 based. - uint32_t column = kInvalidLocation; - - /// Identifier of the source file. - ScriptID fileId = kInvalidLocation; - - /// Name of the source file. - String fileName; -}; - -/// CallFrameInfo is a value type representing an entry in a call stack. -struct CallFrameInfo { - /// Name of the function executing in this frame. - String functionName; - - /// Source location of the program counter for this frame. - SourceLocation location; -}; - -/// StackTrace represents a list of call frames, either in the current execution -/// or captured in an exception. -struct StackTrace { - /// \return the number of call frames. - uint32_t callFrameCount() const { - return frames_.size(); - } - - /// \return call frame info at a given index. 0 represents the topmost - /// (current) frame on the call stack. - CallFrameInfo callFrameForIndex(uint32_t index) const { - return frames_.at(index); - } - - StackTrace() {} - - private: - explicit StackTrace(std::vector frames) - : frames_(std::move(frames)){}; - friend ProgramState; - friend ::hermes::vm::Debugger; - std::vector frames_; -}; - -/// ExceptionDetails is a value type describing an exception. -struct ExceptionDetails { - /// Textual description of the exception. - String text; - - /// Location where the exception was thrown. - SourceLocation location; - - /// Get the stack trace associated with the exception. - const StackTrace &getStackTrace() const { - return stackTrace_; - } - - private: - friend ::hermes::vm::Debugger; - StackTrace stackTrace_; -}; - -/// A list of possible reasons for a Pause. -enum class PauseReason { - ScriptLoaded, /// A script file was loaded, and the debugger has requested - /// pausing after script load. - DebuggerStatement, /// A debugger; statement was hit. - Breakpoint, /// A breakpoint was hit. - StepFinish, /// A Step operation completed. - Exception, /// An Exception was thrown. - AsyncTriggerImplicit, /// The Pause is the result of - /// triggerAsyncPause(Implicit). - AsyncTriggerExplicit, /// The Pause is the result of - /// triggerAsyncPause(Explicit). - EvalComplete, /// An eval() function finished. -}; - -/// When stepping, the mode with which to step. -enum class StepMode { - Into, /// Enter into any function calls. - Over, /// Skip over any function calls. - Out, /// Step until the current function exits. -}; - -/// When setting pause on throw, this specifies when to pause. -enum class PauseOnThrowMode { - None, /// Never pause on exceptions. - Uncaught, /// Only pause on uncaught exceptions. - All, /// Pause any time an exception is thrown. -}; - -/// When requesting an async break, this specifies whether it was an implicit -/// break from the inspector or a user-requested explicit break. -enum class AsyncPauseKind { - /// Implicit pause to allow movement of jsi::Value types between threads. - /// The user will not be running commands and the inspector will immediately - /// request a Continue. - Implicit, - - /// Explicit pause requested by the user. - /// Clears any stepping state and allows the user to run their own commands. - Explicit, -}; - -/// A type representing depth in a lexical scope chain. -using ScopeDepth = uint32_t; - -/// Information about lexical entities (for now, just variable names). -struct LexicalInfo { - /// \return the number of scopes. - ScopeDepth getScopesCount() const { - return variableCountsByScope_.size(); - } - - /// \return the number of variables in a given scope. - uint32_t getVariablesCountInScope(ScopeDepth depth) const { - return variableCountsByScope_.at(depth); - } - - private: - friend ::hermes::vm::Debugger; - std::vector variableCountsByScope_; -}; - -/// Information about a breakpoint. -struct BreakpointInfo { - /// ID of the breakpoint. - /// kInvalidBreakpoint if the info is not valid. - BreakpointID id; - - /// Whether the breakpoint is enabled. - bool enabled; - - /// Whether the breakpoint has been resolved. - bool resolved; - - /// The originally requested location of the breakpoint. - SourceLocation requestedLocation; - - /// The resolved location of the breakpoint if resolved is true. - SourceLocation resolvedLocation; -}; - -} // namespace debugger -} // namespace hermes -} // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/GCConfig.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/GCConfig.h deleted file mode 100644 index 9d95c755..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/GCConfig.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_GCCONFIG_H -#define HERMES_PUBLIC_GCCONFIG_H - -#include "hermes/Public/CtorConfig.h" -#include "hermes/Public/GCTripwireContext.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace hermes { -namespace vm { - -/// A type big enough to accomodate the entire allocated address space. -/// Individual allocations are always 'uint32_t', but on a 64-bit machine we -/// might want to accommodate a larger total heap (or not, in which case we keep -/// it 32-bit). -using gcheapsize_t = uint32_t; - -/// Represents a value before and after an event. -/// NOTE: Not a std::pair because using the names are more readable than first -/// and second. -struct BeforeAndAfter { - uint64_t before; - uint64_t after; -}; - -struct GCAnalyticsEvent { - /// The same value as \p Name from GCConfig. Stored here for simplicity of - /// the API since this is passed in callbacks that might not be able to store - /// the name. For a given Runtime, this will be the same value every time. - std::string runtimeDescription; - - /// The kind of GC this was. For a given Runtime, this will be the same value - /// every time. - std::string gcKind; - - /// The type of collection that ran, typically differentiating a "young" - /// generation GC and an "old" generation GC. When other values say they're - /// "scoped to the collectionType", it means that for a generation GC - /// they're only reporting the numbers for that generation. - std::string collectionType; - - /// The cause of this GC. Can be an arbitrary string describing the cause. - /// Typically "natural" is used to mean that the GC decided it was time, and - /// other causes mean it was forced by some other condition. - std::string cause; - - /// The wall time a collection took from start to end. - std::chrono::milliseconds duration; - - /// The CPU time a collection took from start to end. This time measure will - /// exclude time waiting on disk, mutexes, or time spent not scheduled to run. - std::chrono::milliseconds cpuDuration; - - /// The number of bytes allocated in the heap before and after the collection. - /// measurement does not include fragmentation, and is the same as the sum of - /// all sizes in calls to \p GC::makeA into that generation (including any - /// rounding up the GC does). - /// The value is scoped to the \p collectionType. - BeforeAndAfter allocated; - - /// The number of bytes in use by the heap before and after the collection. - /// This measurement can include fragmentation if the \p gcKind has that - /// concept. - /// The value is scoped to the \p collectionType. - BeforeAndAfter size; - - /// The number of bytes external to the JS heap before and after the - /// collection. - /// The value is scoped to the \p collectionType. - BeforeAndAfter external; - - /// The ratio of cells that survived the collection to all cells before - /// the collection. Note that this is in term of sizes of cells, not the - /// numbers of cells. Excludes any cells not in direct use by the JS program, - /// such as FillerCell or FreelistCell. - /// The value is scoped to the \p collectionType. - double survivalRatio; - - /// A list of metadata tags to annotate this event with. - std::vector tags; -}; - -/// Parameters to control a tripwire function called when the live set size -/// surpasses a given threshold after collections. Check documentation in -/// README.md -#define GC_TRIPWIRE_FIELDS(F) \ - /* If the heap size is above this threshold after a collection, the tripwire \ - * is triggered. */ \ - F(constexpr, gcheapsize_t, Limit, std::numeric_limits::max()) \ - \ - /* The callback to call when the tripwire is considered triggered. */ \ - F(HERMES_NON_CONSTEXPR, \ - std::function, \ - Callback, \ - nullptr) \ - /* GC_TRIPWIRE_FIELDS END */ - -_HERMES_CTORCONFIG_STRUCT(GCTripwireConfig, GC_TRIPWIRE_FIELDS, {}) - -#undef HEAP_TRIPWIRE_FIELDS - -#define GC_HANDLESAN_FIELDS(F) \ - /* The probability with which the GC should keep moving the heap */ \ - /* to detect stale GC handles. */ \ - F(constexpr, double, SanitizeRate, 0.0) \ - /* Random seed to use for basis of decisions whether or not to */ \ - /* sanitize. A negative value will mean a seed will be chosen at */ \ - /* random. */ \ - F(constexpr, int64_t, RandomSeed, -1) \ - /* GC_HANDLESAN_FIELDS END */ - -_HERMES_CTORCONFIG_STRUCT(GCSanitizeConfig, GC_HANDLESAN_FIELDS, {}) - -#undef GC_HANDLESAN_FIELDS - -/// How aggressively to return unused memory to the OS. -enum ReleaseUnused { - kReleaseUnusedNone = 0, /// Don't try to release unused memory. - kReleaseUnusedOld, /// Only old gen, on full collections. - kReleaseUnusedYoungOnFull, /// Also young gen, but only on full collections. - kReleaseUnusedYoungAlways /// Also young gen, also on young gen collections. -}; - -enum class GCEventKind { - CollectionStart, - CollectionEnd, -}; - -/// Parameters for GC Initialisation. Check documentation in README.md -/// constexpr indicates that the default value is constexpr. -#define GC_FIELDS(F) \ - /* Initial heap size hint. */ \ - F(constexpr, gcheapsize_t, InitHeapSize, 32 << 20) \ - \ - /* Maximum heap size hint. */ \ - F(constexpr, gcheapsize_t, MaxHeapSize, 3u << 30) \ - \ - /* Sizing heuristic: fraction of heap to be occupied by live data. */ \ - F(constexpr, double, OccupancyTarget, 0.5) \ - \ - /* Number of consecutive full collections considered to be an OOM. */ \ - F(constexpr, \ - unsigned, \ - EffectiveOOMThreshold, \ - std::numeric_limits::max()) \ - \ - /* Sanitizer configuration for the GC. */ \ - F(constexpr, GCSanitizeConfig, SanitizeConfig) \ - \ - /* Whether to Keep track of GC Statistics. */ \ - F(constexpr, bool, ShouldRecordStats, false) \ - \ - /* How aggressively to return unused memory to the OS. */ \ - F(constexpr, ReleaseUnused, ShouldReleaseUnused, kReleaseUnusedOld) \ - \ - /* Name for this heap in logs. */ \ - F(HERMES_NON_CONSTEXPR, std::string, Name, "") \ - \ - /* Configuration for the Heap Tripwire. */ \ - F(HERMES_NON_CONSTEXPR, GCTripwireConfig, TripwireConfig) \ - \ - /* Whether to (initially) allocate from the young gen (true) or the */ \ - /* old gen (false). */ \ - F(constexpr, bool, AllocInYoung, true) \ - \ - /* Whether to fill the YG with invalid data after each collection. */ \ - F(constexpr, bool, OverwriteDeadYGObjects, false) \ - \ - /* Whether to revert, if necessary, to young-gen allocation at TTI. */ \ - F(constexpr, bool, RevertToYGAtTTI, false) \ - \ - /* Whether to use mprotect on GC metadata between GCs. */ \ - F(constexpr, bool, ProtectMetadata, false) \ - \ - /* Callout for an analytics event. */ \ - F(HERMES_NON_CONSTEXPR, \ - std::function, \ - AnalyticsCallback, \ - nullptr) \ - \ - /* Called at GC events (see GCEventKind enum for the list). The */ \ - /* second argument contains human-readable details about the event. */ \ - /* NOTE: The function MUST NOT invoke any methods on the Runtime. */ \ - F(HERMES_NON_CONSTEXPR, \ - std::function, \ - Callback, \ - nullptr) \ - /* GC_FIELDS END */ - -_HERMES_CTORCONFIG_STRUCT(GCConfig, GC_FIELDS, { - // Make sure the max is at least the Init. - MaxHeapSize_ = std::max(InitHeapSize_, MaxHeapSize_); -}) - -#undef GC_FIELDS - -} // namespace vm -} // namespace hermes - -#endif // HERMES_PUBLIC_GCCONFIG_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/GCTripwireContext.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/GCTripwireContext.h deleted file mode 100644 index 4a8f500f..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/GCTripwireContext.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_GCTRIPWIRECONTEXT_H -#define HERMES_PUBLIC_GCTRIPWIRECONTEXT_H - -#include - -#include -#include -#include - -namespace hermes { -namespace vm { - -/// Interface passed to the GC tripwire callback when it fires. -class HERMES_EXPORT GCTripwireContext { - public: - virtual ~GCTripwireContext(); - - /// Captures the heap to a file. - /// \param path to save the heap capture. - /// \return Empty error code if the heap capture succeeded, else a real error - /// code. - virtual std::error_code createSnapshotToFile(const std::string &path) = 0; - - /// Captures the heap to a stream. - /// \param os stream to save the heap capture to. - /// \return Empty error code if the heap capture succeeded, else a real error - /// code. - virtual std::error_code createSnapshot( - std::ostream &os, - bool captureNumericValue) = 0; -}; - -} // namespace vm -} // namespace hermes - -#endif // HERMES_PUBLIC_GCTRIPWIRECONTEXT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/HermesExport.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/HermesExport.h deleted file mode 100644 index f9832cb5..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/HermesExport.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_EXPORT -#ifdef _MSC_VER -#define HERMES_EXPORT __declspec(dllexport) -#else // _MSC_VER -#define HERMES_EXPORT __attribute__((visibility("default"))) -#endif // _MSC_VER -#endif // !defined(HERMES_EXPORT) diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/JSOutOfMemoryError.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/JSOutOfMemoryError.h deleted file mode 100644 index 95093ab7..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/JSOutOfMemoryError.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_JSOUTOFMEMORYERROR_H -#define HERMES_PUBLIC_JSOUTOFMEMORYERROR_H - -#include - -#include -#include - -namespace hermes { -namespace vm { - -/// A std::runtime_error class for out-of-memory. -class HERMES_EXPORT JSOutOfMemoryError : public std::runtime_error { - friend class GCBase; - JSOutOfMemoryError(const std::string &what_arg) - : std::runtime_error(what_arg) {} - ~JSOutOfMemoryError() override; -}; - -} // namespace vm -} // namespace hermes - -#endif // HERMES_PUBLIC_JSOUTOFMEMORYERROR_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/RuntimeConfig.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/RuntimeConfig.h deleted file mode 100644 index dc253b47..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/Public/RuntimeConfig.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_PUBLIC_RUNTIMECONFIG_H -#define HERMES_PUBLIC_RUNTIMECONFIG_H - -#include "hermes/Public/CrashManager.h" -#include "hermes/Public/CtorConfig.h" -#include "hermes/Public/GCConfig.h" - -#include -#include - -namespace hermes { -namespace vm { - -enum CompilationMode { - SmartCompilation, - ForceEagerCompilation, - ForceLazyCompilation -}; - -enum class SynthTraceMode : int8_t { - None, - Replaying, - Tracing, - TracingAndReplaying, -}; - -class PinnedHermesValue; - -// Parameters for Runtime initialisation. Check documentation in README.md -// constexpr indicates that the default value is constexpr. -#define RUNTIME_FIELDS(F) \ - /* Parameters to be passed on to the GC. */ \ - F(HERMES_NON_CONSTEXPR, vm::GCConfig, GCConfig) \ - \ - /* Pre-allocated Register Stack */ \ - F(constexpr, PinnedHermesValue *, RegisterStack, nullptr) \ - \ - /* Register Stack Size */ \ - F(constexpr, unsigned, MaxNumRegisters, 128 * 1024) \ - \ - /* Native stack remaining before assuming overflow */ \ - F(constexpr, unsigned, NativeStackGap, 64 * 1024) \ - \ - /* Whether or not the JIT is enabled */ \ - F(constexpr, bool, EnableJIT, false) \ - \ - /* Whether to allow eval and Function ctor */ \ - F(constexpr, bool, EnableEval, true) \ - \ - /* Whether to verify the IR generated by eval and Function ctor */ \ - F(constexpr, bool, VerifyEvalIR, false) \ - \ - /* Whether to optimize the code inside eval and Function ctor */ \ - F(constexpr, bool, OptimizedEval, false) \ - \ - /* Whether to emit async break check instructions in eval code */ \ - F(constexpr, bool, AsyncBreakCheckInEval, true) \ - \ - /* Support for ES6 Proxy. */ \ - F(constexpr, bool, ES6Proxy, true) \ - \ - /* Support for ES6 block scoping. */ \ - F(constexpr, bool, ES6BlockScoping, false) \ - \ - /* Support for async generators in eval. */ \ - F(constexpr, bool, EnableAsyncGenerators, false) \ - \ - /* Support for ECMA-402 Intl APIs. */ \ - F(constexpr, bool, Intl, true) \ - \ - /* Support for using microtasks. */ \ - F(constexpr, bool, MicrotaskQueue, false) \ - \ - /* Runtime set up for synth trace. */ \ - F(constexpr, SynthTraceMode, SynthTraceMode, SynthTraceMode::None) \ - \ - /* Enable sampling certain statistics. */ \ - F(constexpr, bool, EnableSampledStats, false) \ - \ - /* Whether to enable automatic sampling profiler registration */ \ - F(constexpr, bool, EnableSampleProfiling, false) \ - \ - /* Whether to randomize stack placement etc. */ \ - F(constexpr, bool, RandomizeMemoryLayout, false) \ - \ - /* Eagerly read bytecode into page cache. */ \ - F(constexpr, unsigned, BytecodeWarmupPercent, 0) \ - \ - /* Signal-based I/O tracking. Slows down execution. If enabled, */ \ - /* all bytecode buffers > 64 kB passed to Hermes must be mmap:ed. */ \ - F(constexpr, bool, TrackIO, false) \ - \ - /* Enable contents of HermesInternal */ \ - F(constexpr, bool, EnableHermesInternal, true) \ - \ - /* Enable methods exposed to JS for testing */ \ - F(constexpr, bool, EnableHermesInternalTestMethods, false) \ - \ - /* Choose lazy/eager compilation mode. */ \ - F(constexpr, \ - CompilationMode, \ - CompilationMode, \ - CompilationMode::SmartCompilation) \ - \ - /* Choose whether generators are enabled. */ \ - F(constexpr, bool, EnableGenerator, true) \ - \ - /* An interface for managing crashes. */ \ - F(HERMES_NON_CONSTEXPR, \ - std::shared_ptr, \ - CrashMgr, \ - new NopCrashManager) \ - \ - /* The flags passed from a VM experiment */ \ - F(constexpr, uint32_t, VMExperimentFlags, 0) \ - /* RUNTIME_FIELDS END */ - -_HERMES_CTORCONFIG_STRUCT(RuntimeConfig, RUNTIME_FIELDS, {}) - -#undef RUNTIME_FIELDS - -} // namespace vm -} // namespace hermes - -#endif // HERMES_PUBLIC_RUNTIMECONFIG_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/RuntimeTaskRunner.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/RuntimeTaskRunner.h deleted file mode 100644 index 367b267a..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/RuntimeTaskRunner.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_RUNTIMETASKRUNNER_H -#define HERMES_RUNTIMETASKRUNNER_H - -#include "AsyncDebuggerAPI.h" - -namespace facebook { -namespace hermes { -namespace debugger { - -using RuntimeTask = std::function; -using EnqueueRuntimeTaskFunc = std::function; - -enum class TaskQueues { - All, - Integrator, -}; - -/// Helper for users of AsyncDebuggerAPI that makes it easy to find the -/// earliest opportunity to use the runtime. There are two ways to become -/// the exclusive user of the runtime: -/// - Ask the AsyncDebuggerAPI to interrupt execution and provide a reference -/// to the runtime. Interrupting will only succeed when JavaScript is -/// running, so this method won't produce a prompt response if JavaScript is -/// not running. -/// - Ask the owner of the runtime to provide a reference to the runtime. If -/// the owner is currently running JavaScript (e.g. via a call to -/// evaluateJavaScript), this method won't produce a prompt response. -/// To cover both cases (when JavaScript is running, and when JavaScript isn't -/// running), this helper requests the runtime from both sources, executes the -/// task via the first responder, and sets a flag to indicate to the second -/// responder that nothing more needs to be done. -class RuntimeTaskRunner - : public std::enable_shared_from_this { - public: - RuntimeTaskRunner( - debugger::AsyncDebuggerAPI &debugger, - EnqueueRuntimeTaskFunc enqueueRuntimeTaskFunc); - ~RuntimeTaskRunner(); - - /// Schedule a task to be run with access to the runtime at the earliest - /// opportunity. Before returning, the task is added to the relevant task - /// queues managed by the \p AsyncDebuggerAPI and/or the intergator, with no - /// lingering references to the \p RuntimeTaskRunner. Thus, tasks can be - /// enqueued even if the task runner will be destroyed shortly after. - void enqueueTask(RuntimeTask task, TaskQueues queues = TaskQueues::All); - - private: - /// API where the runtime can be obtained when JavaScript is running. - debugger::AsyncDebuggerAPI &debugger_; - - /// Function provided by the integrator that enqueues a task to be run - /// when JavaScript is not running. - EnqueueRuntimeTaskFunc enqueueRuntimeTask_; -}; - -} // namespace debugger -} // namespace hermes -} // namespace facebook - -#endif // HERMES_RUNTIMETASKRUNNER_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/SynthTrace.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/SynthTrace.h deleted file mode 100644 index 09bd0d2d..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/SynthTrace.h +++ /dev/null @@ -1,1527 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_SYNTHTRACE_H -#define HERMES_SYNTHTRACE_H - -#include "hermes/ADT/StringSetVector.h" -#include "hermes/Public/RuntimeConfig.h" -#include "hermes/Support/JSONEmitter.h" -#include "hermes/Support/SHA1.h" -#include "hermes/VM/GCExecTrace.h" - -#include "jsi/jsi.h" - -#include -#include -#include -#include -#include -#include - -namespace llvh { -// Forward declaration to avoid including llvm headers. -class raw_ostream; -} // namespace llvh - -namespace facebook { -namespace hermes { -namespace tracing { - -/// A SynthTrace is a list of events that occur in a run of a JS file by a -/// runtime that uses JSI. -/// It can be serialized into JSON and written to a llvh::raw_ostream. -class SynthTrace { - public: - using ObjectID = uint64_t; - - /// A tagged union representing different types available in the trace. - /// We use a an API very similar to HermesValue, but: - /// a) also represent the JSI type PropNameID, and - /// b) the "payloads" for some the types (Objects, Strings, BigInts, Symbols - /// and PropNameIDs) are unique ObjectIDs, rather than actual values. - /// (This could probably become a std::variant when we could use C++17.) - class TraceValue { - public: - bool isUndefined() const { - return tag_ == Tag::Undefined; - } - - bool isNull() const { - return tag_ == Tag::Null; - } - - bool isNumber() const { - return tag_ == Tag::Number; - } - - bool isBool() const { - return tag_ == Tag::Bool; - } - - bool isObject() const { - return tag_ == Tag::Object; - } - - bool isBigInt() const { - return tag_ == Tag::BigInt; - } - - bool isString() const { - return tag_ == Tag::String; - } - - bool isPropNameID() const { - return tag_ == Tag::PropNameID; - } - - bool isSymbol() const { - return tag_ == Tag::Symbol; - } - - bool isUID() const { - return isObject() || isBigInt() || isString() || isPropNameID() || - isSymbol(); - } - - static TraceValue encodeUndefinedValue() { - return TraceValue(Tag::Undefined); - } - - static TraceValue encodeNullValue() { - return TraceValue(Tag::Null); - } - - static TraceValue encodeBoolValue(bool value) { - return TraceValue(value); - } - - static TraceValue encodeNumberValue(double value) { - return TraceValue(value); - } - - static TraceValue encodeObjectValue(uint64_t uid) { - return TraceValue(Tag::Object, uid); - } - - static TraceValue encodeBigIntValue(uint64_t uid) { - return TraceValue(Tag::BigInt, uid); - } - - static TraceValue encodeStringValue(uint64_t uid) { - return TraceValue(Tag::String, uid); - } - - static TraceValue encodePropNameIDValue(uint64_t uid) { - return TraceValue(Tag::PropNameID, uid); - } - - static TraceValue encodeSymbolValue(uint64_t uid) { - return TraceValue(Tag::Symbol, uid); - } - - bool operator==(const TraceValue &that) const; - - ObjectID getUID() const { - assert(isUID()); - return val_.uid; - } - - bool getBool() const { - assert(isBool()); - return val_.b; - } - - double getNumber() const { - assert(isNumber()); - return val_.n; - } - - private: - enum class Tag { - Undefined, - Null, - Bool, - Number, - Object, - String, - PropNameID, - Symbol, - BigInt, - }; - - explicit TraceValue(Tag tag) : tag_(tag) {} - TraceValue(bool b) : tag_(Tag::Bool) { - val_.b = b; - } - TraceValue(double n) : tag_(Tag::Number) { - val_.n = n; - } - TraceValue(Tag tag, uint64_t uid) : tag_(tag) { - val_.uid = uid; - } - - Tag tag_; - union { - bool b; - double n; - ObjectID uid; - } val_; - }; - - /// Represents the encoding type of a String or PropNameId - enum class StringEncodingType { ASCII, UTF8, UTF16 }; - - /// A TimePoint is a time when some event occurred. - using TimePoint = std::chrono::steady_clock::time_point; - using TimeSinceStart = std::chrono::milliseconds; - -#define SYNTH_TRACE_RECORD_TYPES(RECORD) \ - RECORD(BeginExecJS) \ - RECORD(EndExecJS) \ - RECORD(Marker) \ - RECORD(CreateObject) \ - RECORD(CreateObjectWithPrototype) \ - RECORD(CreateString) \ - RECORD(CreatePropNameID) \ - RECORD(CreatePropNameIDWithValue) \ - RECORD(CreateHostObject) \ - RECORD(CreateHostFunction) \ - RECORD(QueueMicrotask) \ - RECORD(DrainMicrotasks) \ - RECORD(GetProperty) \ - RECORD(SetProperty) \ - RECORD(HasProperty) \ - RECORD(GetPropertyNames) \ - RECORD(CreateArray) \ - RECORD(ArrayRead) \ - RECORD(ArrayWrite) \ - RECORD(CallFromNative) \ - RECORD(ConstructFromNative) \ - RECORD(ReturnFromNative) \ - RECORD(ReturnToNative) \ - RECORD(CallToNative) \ - RECORD(GetPropertyNative) \ - RECORD(GetPropertyNativeReturn) \ - RECORD(SetPropertyNative) \ - RECORD(SetPropertyNativeReturn) \ - RECORD(GetNativePropertyNames) \ - RECORD(GetNativePropertyNamesReturn) \ - RECORD(CreateBigInt) \ - RECORD(BigIntToString) \ - RECORD(SetExternalMemoryPressure) \ - RECORD(Utf8) \ - RECORD(Utf16) \ - RECORD(GetStringData) \ - RECORD(GetPrototype) \ - RECORD(SetPrototype) \ - RECORD(DeleteProperty) \ - RECORD(Global) - - /// RecordType is a tag used to differentiate which type of record it is. - /// There should be a unique tag for each record type. - enum class RecordType { -#define RECORD(name) name, - SYNTH_TRACE_RECORD_TYPES(RECORD) -#undef RECORD - }; - - /// A Record is one element of a trace. - struct Record { - /// The time at which this event occurred with respect to the start of - /// execution. - /// NOTE: This is not compared in the \c operator= in order for tests to - /// pass. - const TimeSinceStart time_; - explicit Record() = delete; - explicit Record(TimeSinceStart time) : time_(time) {} - virtual ~Record() = default; - - /// Write out a serialization of this Record. - /// \param json An emitter connected to an ostream which will write out - /// JSON. - void toJSON(::hermes::JSONEmitter &json) const; - virtual RecordType getType() const = 0; - - // If \p val is an object (that is, an Object or String), push its - // decoding onto objs. - static void pushIfTrackedValue( - const TraceValue &val, - std::vector &objs) { - if (val.isUID()) { - objs.push_back(val.getUID()); - } - } - - /// \return A list of object ids that are defined by this record. - /// Defined means that the record would produce that object, - /// string, or PropNameID as a locally accessible value if it were - /// executed. - virtual std::vector defs() const { - return {}; - } - - /// \return A list of object ids that are used by this record. - /// Used means that the record would use that object, string, or - /// PropNameID as a value if it were executed. - /// If a record uses an object id, then some preceding record - /// (either in the same function invocation, or somewhere - /// globally) must provide a definition. - virtual std::vector uses() const { - return {}; - } - - protected: - /// Emit JSON fields into \p os, excluding the closing curly brace. - /// NOTE: This is overridable, and non-abstract children should call the - /// parent. - virtual void toJSONInternal(::hermes::JSONEmitter &json) const; - }; - - /// If \p traceStream is non-null, the trace will be written to that - /// stream. Otherwise, no trace is written. - explicit SynthTrace( - const ::hermes::vm::RuntimeConfig &conf, - std::unique_ptr traceStream = nullptr, - std::optional = {}); - - template - void emplace_back(Args &&...args) { - records_.emplace_back(new T(std::forward(args)...)); - flushRecordsIfNecessary(); - } - - const std::vector> &records() const { - return records_; - } - - std::optional globalObjID() const { - return globalObjID_; - } - - /// Given a trace value, turn it into its typed string. - static std::string encode(TraceValue value); - /// Encode an undefined JS value for the trace. - static TraceValue encodeUndefined(); - /// Encode a null JS value for the trace. - static TraceValue encodeNull(); - /// Encode a boolean JS value for the trace. - static TraceValue encodeBool(bool value); - /// Encodes a numeric value for the trace. - static TraceValue encodeNumber(double value); - /// Encodes an object for the trace as a unique id. - static TraceValue encodeObject(ObjectID objID); - /// Encodes a bigint for the trace as a unique id. - static TraceValue encodeBigInt(ObjectID objID); - /// Encodes a string for the trace as a unique id. - static TraceValue encodeString(ObjectID objID); - /// Encodes a PropNameID for the trace as a unique id. - static TraceValue encodePropNameID(ObjectID objID); - /// Encodes a Symbol for the trace as a unique id. - static TraceValue encodeSymbol(ObjectID objID); - - /// Decodes a string into a trace value. - static TraceValue decode(const std::string &); - -#ifdef HERMESVM_API_TRACE_DEBUG - /// Given a Value, return a descriptive string. This should only be used to - /// provide more debugging info when creating records. - static std::string getDescriptiveString( - jsi::Runtime &runtime, - const jsi::Value &value); -#endif - - /// The version of the Synth Benchmark - constexpr static uint32_t synthVersion() { - return 5; - } - - static const char *nameFromReleaseUnused(::hermes::vm::ReleaseUnused ru); - static ::hermes::vm::ReleaseUnused releaseUnusedFromName(const char *name); - - private: - llvh::raw_ostream &os() const { - return (*traceStream_); - } - - /// If we're tracing to a file, and the number of accumulated - /// records has reached the limit kTraceRecordsToFlush, below, - /// flush the records to the file, and reset the accumulated records - /// to be empty. - void flushRecordsIfNecessary(); - - /// Assumes we're tracing to a file; flush accumulated records to - /// the file, and reset the accumulated records to be empty. - void flushRecords(); - - static constexpr unsigned kTraceRecordsToFlush = 100; - - /// If we're tracing to a file, pointer to a stream onto - /// traceFilename_. Null otherwise. - std::unique_ptr traceStream_; - /// If we're tracing to a file, pointer to a JSONEmitter writting - /// into *traceStream_. Null otherwise. - std::unique_ptr<::hermes::JSONEmitter> json_; - /// The records currently being accumulated in the trace. If we are - /// tracing to a file, these will be only the records not yet - /// written to the file. - std::vector> records_; - /// The id of the global object. - /// Note: Keeping this as optional to support replaying the older trace - /// records before the change of TracingRuntime's PointerValue based ObjectID. - /// We can remove this once we remove old traces. - /// TODO: T189113203 - const std::optional globalObjID_; - - public: - /// @name Record classes - /// @{ - - /// A MarkerRecord is an event that simply records an interesting event that - /// is not necessarily meaningful to the interpreter. It comes with a tag that - /// says what type of marker it was. - struct MarkerRecord : public Record { - static constexpr RecordType type{RecordType::Marker}; - const std::string tag_; - explicit MarkerRecord(TimeSinceStart time, const std::string &tag) - : Record(time), tag_(tag) {} - RecordType getType() const override { - return type; - } - - protected: - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A BeginExecJSRecord is an event where execution begins of JS source - /// code. This is not necessarily the first record, since native code can - /// inject values into the VM before any source code is run. - struct BeginExecJSRecord final : public Record { - static constexpr RecordType type{RecordType::BeginExecJS}; - explicit BeginExecJSRecord( - TimeSinceStart time, - std::string sourceURL, - ::hermes::SHA1 sourceHash, - bool sourceIsBytecode) - : Record(time), - sourceURL_(std::move(sourceURL)), - sourceHash_(std::move(sourceHash)), - sourceIsBytecode_(sourceIsBytecode) {} - - RecordType getType() const override { - return type; - } - - const std::string &sourceURL() const { - return sourceURL_; - } - - const ::hermes::SHA1 &sourceHash() const { - return sourceHash_; - } - - private: - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - /// The URL providing the source file mapping for the file being executed. - /// Can be empty. - std::string sourceURL_; - - /// A hash of the source that was executed. The source hash must match up - /// when the file is replayed. - /// The hash is optional, and will be all zeros if not provided. - ::hermes::SHA1 sourceHash_; - - /// Whether the input file was source or bytecode. - bool sourceIsBytecode_; - }; - - struct ReturnMixin { - const TraceValue retVal_; - - explicit ReturnMixin(TraceValue value) : retVal_(value) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const; - }; - - /// A EndExecJSRecord is an event where execution of JS source code stops. - /// This does not mean that the source code will never be entered again, just - /// that it has an entered a phase where it is waiting for native code to call - /// into the JS. This event is not guaranteed to be the last event, for the - /// aforementioned reason. The logged retVal is the result of the evaluation - /// ("undefined" in the majority of cases). - struct EndExecJSRecord final : public MarkerRecord, public ReturnMixin { - static constexpr RecordType type{RecordType::EndExecJS}; - EndExecJSRecord(TimeSinceStart time, TraceValue retVal) - : MarkerRecord(time, "end_global_code"), ReturnMixin(retVal) {} - - RecordType getType() const override { - return type; - } - virtual void toJSONInternal(::hermes::JSONEmitter &json) const final; - std::vector defs() const override { - auto defs = MarkerRecord::defs(); - pushIfTrackedValue(retVal_, defs); - return defs; - } - }; - - /// A CreateObjectRecord is an event where an empty object is created by the - /// native code. - struct CreateObjectRecord : public Record { - static constexpr RecordType type{RecordType::CreateObject}; - /// The ObjectID of the object that was created by native function calls - /// like Runtime::createObject(). - const ObjectID objID_; - - explicit CreateObjectRecord(TimeSinceStart time, ObjectID objID) - : Record(time), objID_(objID) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {objID_}; - } - - std::vector uses() const override { - return {}; - } - }; - - /// A CreateBigIntRecord is an event where a jsi::BigInt (and thus a - /// Hermes BigIntPrimitive) is created by the native code. - struct CreateBigIntRecord : public Record { - static constexpr RecordType type{RecordType::CreateBigInt}; - /// The ObjectID of the BigInt that was created by - /// Runtime::createBigIntFromInt64() or Runtime::createBigIntFromUint64(). - const ObjectID objID_; - enum class Method { - FromInt64, - FromUint64, - }; - /// The method used for creating the BigInt. - Method method_; - /// The value used for creating the BigInt. - uint64_t bits_; - - CreateBigIntRecord( - TimeSinceStart time, - ObjectID objID, - Method m, - uint64_t bits) - : Record(time), objID_(objID), method_(m), bits_(bits) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {objID_}; - } - - std::vector uses() const override { - return {}; - } - }; - - /// A BigIntToStringRecord is an event where a jsi::BigInt is converted to a - /// string by native code - struct BigIntToStringRecord : public Record { - static constexpr RecordType type{RecordType::BigIntToString}; - /// The ObjectID of the string that was returned from - /// Runtime::bigintToString(). - const ObjectID strID_; - /// The ObjectID of the BigInt that was passed to Runtime::bigintToString(). - const ObjectID bigintID_; - /// The radix used for converting the BigInt to a string. - int radix_; - - BigIntToStringRecord( - TimeSinceStart time, - ObjectID strID, - ObjectID bigintID, - int radix) - : Record(time), strID_(strID), bigintID_(bigintID), radix_(radix) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {strID_}; - } - - std::vector uses() const override { - return {bigintID_}; - } - }; - - /// A CreateStringRecord is an event where a jsi::String (and thus a - /// Hermes StringPrimitive) is created by the native code. - struct CreateStringRecord : public Record { - static constexpr RecordType type{RecordType::CreateString}; - /// The ObjectID of the string that was created by - /// Runtime::createStringFromAscii() or Runtime::createStringFromUtf8(). - const ObjectID objID_; - /// The string that was passed to Runtime::createStringFromAscii() or - /// Runtime::createStringFromUtf8() when the string was created. - std::string chars_; - /// The string that was passed to Runtime::createStringFromUtf16() - std::u16string chars16_; - /// Whether the String was created from ASCII, UTF-8 or UTF-16 - StringEncodingType encodingType_; - - // General UTF-8. - CreateStringRecord( - TimeSinceStart time, - ObjectID objID, - const uint8_t *chars, - size_t length) - : Record(time), - objID_(objID), - chars_(reinterpret_cast(chars), length), - encodingType_(StringEncodingType::UTF8) {} - // Ascii. - CreateStringRecord( - TimeSinceStart time, - ObjectID objID, - const char *chars, - size_t length) - : Record(time), - objID_(objID), - chars_(chars, length), - encodingType_(StringEncodingType::ASCII) {} - // UTF-16. - CreateStringRecord( - TimeSinceStart time, - ObjectID objID, - const char16_t *chars, - size_t length) - : Record(time), - objID_(objID), - chars16_(chars, length), - encodingType_(StringEncodingType::UTF16) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {objID_}; - } - - std::vector uses() const override { - return {}; - } - }; - - /// A CreatePropNameIDRecord is an event where a jsi::PropNameID is - /// created by the native code. - struct CreatePropNameIDRecord : public Record { - static constexpr RecordType type{RecordType::CreatePropNameID}; - /// The ObjectID of the PropNameID that was created. - const ObjectID propNameID_; - /// The string that was passed to Runtime::createPropNameIDFromAscii() or - /// Runtime::createPropNameIDFromUtf8(). - std::string chars_; - /// The string that was passed to Runtime::createPropNameIDFromUtf16() - std::u16string chars16_; - /// Whether the PropNameID was created from ASCII, UTF-8, or UTF-16 - StringEncodingType encodingType_; - - // General UTF-8. - CreatePropNameIDRecord( - TimeSinceStart time, - ObjectID propNameID, - const uint8_t *chars, - size_t length) - : Record(time), - propNameID_(propNameID), - chars_(reinterpret_cast(chars), length), - encodingType_(StringEncodingType::UTF8) {} - // Ascii. - CreatePropNameIDRecord( - TimeSinceStart time, - ObjectID propNameID, - const char *chars, - size_t length) - : Record(time), - propNameID_(propNameID), - chars_(chars, length), - encodingType_(StringEncodingType::ASCII) {} - // UTF16 - CreatePropNameIDRecord( - TimeSinceStart time, - ObjectID propNameID, - const char16_t *chars, - size_t length) - : Record(time), - propNameID_(propNameID), - chars16_(chars, length), - encodingType_(StringEncodingType::UTF16) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {propNameID_}; - } - - std::vector uses() const override { - return {}; - } - }; - - /// A CreatePropNameIDWithValueRecord is an event where a jsi::PropNameID is - /// created by the native code from JSI Value - struct CreatePropNameIDWithValueRecord : public Record { - static constexpr RecordType type{RecordType::CreatePropNameIDWithValue}; - /// The ObjectID of the PropNameID that was created. - const ObjectID propNameID_; - /// The String or Symbol that was passed to - /// Runtime::createPropNameIDFromString() or - /// Runtime::createPropNameIDFromSymbol(). - const TraceValue traceValue_; - - // jsi::String or jsi::Symbol. - CreatePropNameIDWithValueRecord( - TimeSinceStart time, - ObjectID propNameID, - TraceValue traceValue) - : Record(time), propNameID_(propNameID), traceValue_(traceValue) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {propNameID_}; - } - - std::vector uses() const override { - std::vector vec; - pushIfTrackedValue(traceValue_, vec); - return vec; - } - }; - - struct CreateObjectWithPrototypeRecord : public Record { - static constexpr RecordType type{RecordType::CreateObjectWithPrototype}; - const ObjectID objID_; - /// The prototype being assigned - const TraceValue prototype_; - - CreateObjectWithPrototypeRecord( - TimeSinceStart time, - ObjectID objID, - TraceValue prototype) - : Record(time), objID_(objID), prototype_(prototype) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector uses{objID_}; - pushIfTrackedValue(prototype_, uses); - return uses; - } - }; - - struct CreateHostObjectRecord final : public CreateObjectRecord { - static constexpr RecordType type{RecordType::CreateHostObject}; - using CreateObjectRecord::CreateObjectRecord; - RecordType getType() const override { - return type; - } - }; - - struct CreateHostFunctionRecord final : public CreateObjectRecord { - static constexpr RecordType type{RecordType::CreateHostFunction}; - /// The ObjectID of the PropNameID that was passed to - /// Runtime::createFromHostFunction(). - uint32_t propNameID_; -#ifdef HERMESVM_API_TRACE_DEBUG - const std::string functionName_; -#endif - /// The number of parameters that the created host function takes. - const unsigned paramCount_; - - CreateHostFunctionRecord( - TimeSinceStart time, - ObjectID objID, - ObjectID propNameID, -#ifdef HERMESVM_API_TRACE_DEBUG - std::string functionName, -#endif - unsigned paramCount) - : CreateObjectRecord(time, objID), - propNameID_(propNameID), -#ifdef HERMESVM_API_TRACE_DEBUG - functionName_(std::move(functionName)), -#endif - paramCount_(paramCount) { - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - return {propNameID_}; - } - }; - - struct QueueMicrotaskRecord : public Record { - static constexpr RecordType type{RecordType::QueueMicrotask}; - /// The ObjectID of the callback function that was queued. - const ObjectID callbackID_; - - QueueMicrotaskRecord(TimeSinceStart time, ObjectID callbackID) - : Record(time), callbackID_(callbackID) {} - - RecordType getType() const override { - return type; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - std::vector uses() const override { - return {callbackID_}; - } - }; - - struct DrainMicrotasksRecord : public Record { - static constexpr RecordType type{RecordType::DrainMicrotasks}; - /// maxMicrotasksHint value passed to Runtime::drainMicrotasks() call. - int maxMicrotasksHint_; - - DrainMicrotasksRecord(TimeSinceStart time, int tasksHint = -1) - : Record(time), maxMicrotasksHint_(tasksHint) {} - - RecordType getType() const override { - return type; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A GetPropertyRecord is an event where native code accesses the property - /// of a JS object. - struct GetPropertyRecord : public Record { - /// The ObjectID of the object that was accessed for its property. - const ObjectID objID_; - /// String or PropNameID or Value passed to getProperty. - const TraceValue propID_; -#ifdef HERMESVM_API_TRACE_DEBUG - std::string propNameDbg_; -#endif - - GetPropertyRecord( - TimeSinceStart time, - ObjectID objID, - TraceValue propID -#ifdef HERMESVM_API_TRACE_DEBUG - , - const std::string &propNameDbg -#endif - ) - : Record(time), - objID_(objID), - propID_(propID) -#ifdef HERMESVM_API_TRACE_DEBUG - , - propNameDbg_(propNameDbg) -#endif - { - } - - static constexpr RecordType type{RecordType::GetProperty}; - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector uses{objID_}; - pushIfTrackedValue(propID_, uses); - return uses; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A SetPropertyRecord is an event where native code writes to the property - /// of a JS object. - struct SetPropertyRecord : public Record { - /// The ObjectID of the object that was accessed for its property. - const ObjectID objID_; - /// String or PropNameID or Value passed to setProperty. - const TraceValue propID_; -#ifdef HERMESVM_API_TRACE_DEBUG - std::string propNameDbg_; -#endif - /// The value being assigned. - const TraceValue value_; - - SetPropertyRecord( - TimeSinceStart time, - ObjectID objID, - TraceValue propID, -#ifdef HERMESVM_API_TRACE_DEBUG - const std::string &propNameDbg, -#endif - TraceValue value) - : Record(time), - objID_(objID), - propID_(propID), -#ifdef HERMESVM_API_TRACE_DEBUG - propNameDbg_(propNameDbg), -#endif - value_(value) { - } - - static constexpr RecordType type{RecordType::SetProperty}; - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector uses{objID_}; - pushIfTrackedValue(propID_, uses); - pushIfTrackedValue(value_, uses); - return uses; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A HasPropertyRecord is an event where native code queries whether a - /// property exists on an object. (We don't care about the result because - /// it cannot influence the trace.) - struct HasPropertyRecord final : public Record { - static constexpr RecordType type{RecordType::HasProperty}; - /// The ObjectID of the object that was accessed for its property. - const ObjectID objID_; -#ifdef HERMESVM_API_TRACE_DEBUG - std::string propNameDbg_; -#endif - /// The property name that was passed to hasProperty(). - const TraceValue propID_; - - HasPropertyRecord( - TimeSinceStart time, - ObjectID objID, - TraceValue propID -#ifdef HERMESVM_API_TRACE_DEBUG - , - const std::string &propNameDbg -#endif - ) - : Record(time), - objID_(objID), -#ifdef HERMESVM_API_TRACE_DEBUG - propNameDbg_(propNameDbg), -#endif - propID_(propID) { - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - std::vector uses() const override { - std::vector vec{objID_}; - pushIfTrackedValue(propID_, vec); - return vec; - } - }; - - struct GetPropertyNamesRecord final : public Record { - static constexpr RecordType type{RecordType::GetPropertyNames}; - /// The ObjectID of the object that was accessed for its property. - const ObjectID objID_; - - explicit GetPropertyNamesRecord(TimeSinceStart time, ObjectID objID) - : Record(time), objID_(objID) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - std::vector uses() const override { - return {objID_}; - } - }; - - /// A SetPrototypeRecord is an event where native code sets the prototype of a - /// JS Object - struct SetPrototypeRecord : public Record { - static constexpr RecordType type{RecordType::SetPrototype}; - /// The ObjectID of the object that was accessed for its prototype. - const ObjectID objID_; - /// The custom prototype being assigned - const TraceValue value_; - SetPrototypeRecord(TimeSinceStart time, ObjectID objID, TraceValue value) - : Record(time), objID_(objID), value_(value) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - RecordType getType() const override { - return type; - } - std::vector uses() const override { - std::vector uses{objID_}; - pushIfTrackedValue(value_, uses); - return uses; - } - }; - - struct DeletePropertyRecord final : public Record { - static constexpr RecordType type{RecordType::DeleteProperty}; - /// The object ID of the object that was accessed for its property - const ObjectID objID_; - /// The name of the property being deleted - const TraceValue propID_; - - DeletePropertyRecord(TimeSinceStart time, ObjectID objID, TraceValue propID) - : Record(time), objID_(objID), propID_(propID) {} - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector uses{objID_}; - pushIfTrackedValue(propID_, uses); - return uses; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A GetPrototypeRecord is an event where native code gets the prototype of a - /// JS Object - struct GetPrototypeRecord : public Record { - static constexpr RecordType type{RecordType::GetPrototype}; - /// The ObjectID of the object that was accessed for its prototype. - const ObjectID objID_; - GetPrototypeRecord(TimeSinceStart time, ObjectID objID) - : Record(time), objID_(objID) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - RecordType getType() const override { - return type; - } - std::vector uses() const override { - return {objID_}; - } - }; - - /// A CreateArrayRecord is an event where a new array is created of a specific - /// length. - struct CreateArrayRecord final : public Record { - static constexpr RecordType type{RecordType::CreateArray}; - /// The ObjectID of the array that was created by the createArray(). - const ObjectID objID_; - /// The length of the array that was passed to createArray(). - const size_t length_; - - explicit CreateArrayRecord( - TimeSinceStart time, - ObjectID objID, - size_t length) - : Record(time), objID_(objID), length_(length) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - std::vector defs() const override { - return {objID_}; - } - }; - - /// An ArrayReadRecord is an event where a value was read from an index - /// of an array. - /// It is modeled separately from GetProperty because it is more efficient to - /// read from a numeric index on an array than a string. - struct ArrayReadRecord final : public Record { - /// The ObjectID of the array that was accessed. - const ObjectID objID_; - /// The index of the element that was accessed in the array. - const size_t index_; - - explicit ArrayReadRecord(TimeSinceStart time, ObjectID objID, size_t index) - : Record(time), objID_(objID), index_(index) {} - - static constexpr RecordType type{RecordType::ArrayRead}; - RecordType getType() const override { - return type; - } - std::vector uses() const override { - return {objID_}; - } - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// An ArrayWriteRecord is an event where a value was written into an index - /// of an array. - struct ArrayWriteRecord final : public Record { - /// The ObjectID of the array that was accessed. - const ObjectID objID_; - /// The index of the element that was accessed in the array. - const size_t index_; - /// The value that was written to the array. - const TraceValue value_; - - explicit ArrayWriteRecord( - TimeSinceStart time, - ObjectID objID, - size_t index, - TraceValue value) - : Record(time), objID_(objID), index_(index), value_(value) {} - - static constexpr RecordType type{RecordType::ArrayWrite}; - RecordType getType() const override { - return type; - } - std::vector uses() const override { - std::vector uses{objID_}; - pushIfTrackedValue(value_, uses); - return uses; - } - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - struct CallRecord : public Record { - /// The ObjectID of the function JS object that was called from - /// JS or native. - const ObjectID functionID_; - /// The value of the this argument passed to the function call. - const TraceValue thisArg_; - /// The arguments given to a call (excluding the this parameter), - /// already JSON stringified. - const std::vector args_; - - explicit CallRecord( - TimeSinceStart time, - ObjectID functionID, - TraceValue thisArg, - const std::vector &args) - : Record(time), - functionID_(functionID), - thisArg_(thisArg), - args_(args) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - std::vector uses() const override { - // The function is used regardless of direction. - return {functionID_}; - } - - protected: - std::vector getArgTrackedIDs() const { - std::vector objs; - pushIfTrackedValue(thisArg_, objs); - for (const auto &arg : args_) { - pushIfTrackedValue(arg, objs); - } - return objs; - } - }; - - /// A CallFromNativeRecord is an event where native code calls into a JS - /// function. - struct CallFromNativeRecord : public CallRecord { - static constexpr RecordType type{RecordType::CallFromNative}; - using CallRecord::CallRecord; - RecordType getType() const override { - return type; - } - std::vector uses() const override { - auto uses = CallRecord::uses(); - auto objs = CallRecord::getArgTrackedIDs(); - uses.insert(uses.end(), objs.begin(), objs.end()); - return uses; - } - }; - - /// A ConstructFromNativeRecord is the same as \c CallFromNativeRecord, except - /// the function is called with the new operator. - struct ConstructFromNativeRecord final : public CallFromNativeRecord { - static constexpr RecordType type{RecordType::ConstructFromNative}; - using CallFromNativeRecord::CallFromNativeRecord; - RecordType getType() const override { - return type; - } - }; - - /// A ReturnFromNativeRecord is an event where a native function returns to a - /// JS caller. - /// It pairs with \c CallToNativeRecord. - struct ReturnFromNativeRecord final : public Record, public ReturnMixin { - static constexpr RecordType type{RecordType::ReturnFromNative}; - ReturnFromNativeRecord(TimeSinceStart time, TraceValue retVal) - : Record(time), ReturnMixin(retVal) {} - RecordType getType() const override { - return type; - } - std::vector uses() const override { - auto uses = Record::uses(); - pushIfTrackedValue(retVal_, uses); - return uses; - } - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A ReturnToNativeRecord is an event where a JS function returns to a native - /// caller. - /// It pairs with \c CallFromNativeRecord. - struct ReturnToNativeRecord final : public Record, public ReturnMixin { - static constexpr RecordType type{RecordType::ReturnToNative}; - ReturnToNativeRecord(TimeSinceStart time, TraceValue retVal) - : Record(time), ReturnMixin(retVal) {} - RecordType getType() const override { - return type; - } - std::vector defs() const override { - auto defs = Record::defs(); - pushIfTrackedValue(retVal_, defs); - return defs; - } - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A CallToNativeRecord is an event where JS code calls into a natively - /// defined function. - struct CallToNativeRecord final : public CallRecord { - static constexpr RecordType type{RecordType::CallToNative}; - using CallRecord::CallRecord; - RecordType getType() const override { - return type; - } - std::vector defs() const override { - auto defs = CallRecord::defs(); - auto objs = CallRecord::getArgTrackedIDs(); - defs.insert(defs.end(), objs.begin(), objs.end()); - return defs; - } - }; - - struct GetOrSetPropertyNativeRecord : public Record { - /// The ObjectID of the host object that was being accessed for its - /// property. - const ObjectID hostObjectID_; - /// The ObjectID of the PropNameID that was passed to HostObject::get() - /// or HostObject::set(). - const ObjectID propNameID_; - /// The UTF-8 string of the PropNameID that was passed to HostObject::get() - /// or HostObject::set(). - const std::string propName_; - - GetOrSetPropertyNativeRecord( - TimeSinceStart time, - ObjectID hostObjectID, - ObjectID propNameID, - const std::string &propName) - : Record(time), - hostObjectID_(hostObjectID), - propNameID_(propNameID), - propName_(propName) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - std::vector defs() const override { - return {propNameID_}; - } - std::vector uses() const override { - return {hostObjectID_}; - } - - protected: - }; - - /// A GetPropertyNativeRecord is an event where JS tries to access a property - /// on a native object. - /// This needs to be modeled as a call with no arguments, since native code - /// can arbitrarily affect the JS heap during the accessor. - struct GetPropertyNativeRecord final : public GetOrSetPropertyNativeRecord { - static constexpr RecordType type{RecordType::GetPropertyNative}; - using GetOrSetPropertyNativeRecord::GetOrSetPropertyNativeRecord; - RecordType getType() const override { - return type; - } - }; - - struct GetPropertyNativeReturnRecord final : public Record, - public ReturnMixin { - static constexpr RecordType type{RecordType::GetPropertyNativeReturn}; - GetPropertyNativeReturnRecord(TimeSinceStart time, TraceValue retVal) - : Record(time), ReturnMixin(retVal) {} - RecordType getType() const override { - return type; - } - std::vector uses() const override { - auto uses = Record::uses(); - pushIfTrackedValue(retVal_, uses); - return uses; - } - - protected: - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A SetPropertyNativeRecord is an event where JS code writes to the property - /// of a Native object. - /// This needs to be modeled as a call with one argument, since native code - /// can arbitrarily affect the JS heap during the accessor. - struct SetPropertyNativeRecord final : public GetOrSetPropertyNativeRecord { - static constexpr RecordType type{RecordType::SetPropertyNative}; - /// The value that was passed to HostObject::set() call. - TraceValue value_; - - SetPropertyNativeRecord( - TimeSinceStart time, - ObjectID hostObjectID, - ObjectID propNameID, - const std::string &propName, - TraceValue value) - : GetOrSetPropertyNativeRecord( - time, - hostObjectID, - propNameID, - propName), - value_(value) {} - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - RecordType getType() const override { - return type; - } - std::vector defs() const override { - auto defs = GetOrSetPropertyNativeRecord::defs(); - pushIfTrackedValue(value_, defs); - return defs; - } - }; - - /// A SetPropertyNativeReturnRecord needs to record no extra information - struct SetPropertyNativeReturnRecord final : public Record { - static constexpr RecordType type{RecordType::SetPropertyNativeReturn}; - using Record::Record; - RecordType getType() const override { - return type; - } - }; - - /// A GetNativePropertyNamesRecord records an event where JS asked for a list - /// of property names available on a host object. It records the object, and - /// the returned list of property names. - struct GetNativePropertyNamesRecord : public Record { - static constexpr RecordType type{RecordType::GetNativePropertyNames}; - /// The ObjectID of the host object that was being accessed for - /// HostObjet::getPropertyNames() call. - const ObjectID hostObjectID_; - - explicit GetNativePropertyNamesRecord( - TimeSinceStart time, - ObjectID hostObjectID) - : Record(time), hostObjectID_(hostObjectID) {} - - RecordType getType() const override { - return type; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - std::vector uses() const override { - return {hostObjectID_}; - } - }; - - /// A GetNativePropertyNamesReturnRecord records what property names were - /// returned by the GetNativePropertyNames query. - struct GetNativePropertyNamesReturnRecord final : public Record { - static constexpr RecordType type{RecordType::GetNativePropertyNamesReturn}; - - /// Returned list of property names - const std::vector propNameIDs_; - - explicit GetNativePropertyNamesReturnRecord( - TimeSinceStart time, - const std::vector &propNameIDs) - : Record(time), propNameIDs_(propNameIDs) {} - - RecordType getType() const override { - return type; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - - std::vector uses() const override { - auto uses = Record::uses(); - for (const auto &val : propNameIDs_) { - pushIfTrackedValue(val, uses); - } - return uses; - } - }; - - struct SetExternalMemoryPressureRecord final : public Record { - static constexpr RecordType type{RecordType::SetExternalMemoryPressure}; - /// The ObjectID of the object that was passed to - /// Runtime::setExternalMemoryPressure() call. - const ObjectID objID_; - /// The value passed to Runtime::setExternalMemoryPressure() call. - const size_t amount_; - - explicit SetExternalMemoryPressureRecord( - TimeSinceStart time, - const ObjectID objID, - const size_t amount) - : Record(time), objID_(objID), amount_(amount) {} - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - return {objID_}; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// An Utf8Record is an event where a PropNameID or String or Symbol was - /// converted to utf8. - struct Utf8Record final : public Record { - static constexpr RecordType type{RecordType::Utf8}; - /// PropNameID, String or Symbol passed to utf8() or symbolToString() as an - /// argument - const TraceValue objID_; - /// Returned string from utf8() or symbolToString() - const std::string retVal_; - - explicit Utf8Record( - TimeSinceStart time, - const TraceValue objID, - std::string retval) - : Record(time), objID_(objID), retVal_(std::move(retval)) {} - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector vec; - pushIfTrackedValue(objID_, vec); - return vec; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A Utf16Record is an event where a PropNameID or String was converted to - /// UTF-16. - struct Utf16Record final : public Record { - static constexpr RecordType type{RecordType::Utf16}; - /// PropNameID, String passed to utf16() as an argument - const TraceValue objID_; - /// Returned string from utf16(). - const std::u16string retVal_; - - explicit Utf16Record( - TimeSinceStart time, - const TraceValue objID, - std::u16string retval) - : Record(time), objID_(objID), retVal_(std::move(retval)) {} - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector vec; - pushIfTrackedValue(objID_, vec); - return vec; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// A GetStringData is an event where getStringData or getPropNameIdData was - /// invoked. - struct GetStringDataRecord final : public Record { - static constexpr RecordType type{RecordType::GetStringData}; - /// The String or PropNameID passed into getStringData or getPropNameIdData - const TraceValue objID_; - /// The string content in the String or PropNameID that was passed into the - /// callback - const std::u16string strData_; - - explicit GetStringDataRecord( - TimeSinceStart time, - const TraceValue objID, - std::u16string strData) - : Record(time), objID_(objID), strData_(std::move(strData)) {} - - RecordType getType() const override { - return type; - } - - std::vector uses() const override { - std::vector vec; - pushIfTrackedValue(objID_, vec); - return vec; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - struct GlobalRecord final : public Record { - static constexpr RecordType type{RecordType::Global}; - const ObjectID objID_; // global's ObjectID returned from Runtime::global(). - - explicit GlobalRecord(TimeSinceStart time, ObjectID objID) - : Record(time), objID_(objID) {} - - RecordType getType() const override { - return type; - } - - std::vector defs() const override { - return {objID_}; - } - - void toJSONInternal(::hermes::JSONEmitter &json) const override; - }; - - /// Completes writing of the trace to the trace stream. If writing - /// to a file, disables further writing to the file, or accumulation - /// of data. - void flushAndDisable(const ::hermes::vm::GCExecTrace &gcTrace); -}; - -} // namespace tracing -} // namespace hermes -} // namespace facebook - -#endif // HERMES_SYNTHTRACE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/SynthTraceParser.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/SynthTraceParser.h deleted file mode 100644 index 7844ee50..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/SynthTraceParser.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_SYNTHTRACEPARSER_H -#define HERMES_SYNTHTRACEPARSER_H - -#include - -#include "hermes/Public/RuntimeConfig.h" -#include "hermes/SynthTrace.h" - -#include "llvh/Support/MemoryBuffer.h" - -namespace facebook { -namespace hermes { -namespace tracing { - -/// Parse a trace from a JSON string stored in a MemoryBuffer. -std::tuple< - SynthTrace, - ::hermes::vm::RuntimeConfig::Builder, - ::hermes::vm::GCConfig::Builder> -parseSynthTrace(std::unique_ptr trace); - -/// Parse a trace from a JSON string stored in the given file name. -std::tuple< - SynthTrace, - ::hermes::vm::RuntimeConfig::Builder, - ::hermes::vm::GCConfig::Builder> -parseSynthTrace(const std::string &tracefile); - -} // namespace tracing -} // namespace hermes -} // namespace facebook - -#endif // HERMES_SYNTHTRACEPARSER_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/ThreadSafetyAnalysis.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/ThreadSafetyAnalysis.h deleted file mode 100644 index 39e6cf66..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/ThreadSafetyAnalysis.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Based on mutex.h from https://clang.llvm.org/docs/ThreadSafetyAnalysis.html - -#ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H -#define THREAD_SAFETY_ANALYSIS_MUTEX_H - -// Enable thread safety attributes only with clang. -// The attributes can be safely erased when compiling with other compilers. -#if defined(__clang__) && (!defined(SWIG)) && defined(_LIBCPP_VERSION) && \ - defined(_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) -#define TSA_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) -#else -#define TSA_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op -#endif - -#define TSA_CAPABILITY(x) TSA_THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) - -#define TSA_SCOPED_CAPABILITY TSA_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) - -#define TSA_GUARDED_BY(x) TSA_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) - -#define TSA_PT_GUARDED_BY(x) TSA_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) - -#define TSA_ACQUIRED_BEFORE(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) - -#define TSA_ACQUIRED_AFTER(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) - -#define TSA_REQUIRES(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) - -#define TSA_REQUIRES_SHARED(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) - -#define TSA_ACQUIRE(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) - -#define TSA_ACQUIRE_SHARED(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) - -#define TSA_RELEASE(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) - -#define TSA_RELEASE_SHARED(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) - -#define TSA_RELEASE_GENERIC(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__)) - -#define TSA_TRY_ACQUIRE(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) - -#define TSA_TRY_ACQUIRE_SHARED(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) - -#define TSA_EXCLUDES(...) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) - -#define TSA_ASSERT_CAPABILITY(x) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) - -#define TSA_ASSERT_SHARED_CAPABILITY(x) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) - -#define TSA_RETURN_CAPABILITY(x) \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) - -#define TSA_NO_THREAD_SAFETY_ANALYSIS \ - TSA_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#endif // THREAD_SAFETY_ANALYSIS_MUTEX_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TraceInterpreter.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TraceInterpreter.h deleted file mode 100644 index 83c8a383..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TraceInterpreter.h +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { - -namespace tracing { - -class TraceInterpreter final { - public: - /// Options for executing the trace. - struct ExecuteOptions { - /// Customizes the GCConfig of the Runtime. - ::hermes::vm::GCConfig::Builder gcConfigBuilder; - - /// If true, trace again while replaying. After normalization (see - /// hermes/tools/synth/trace_normalize.py) the output trace should be - /// identical to the input trace. If they're not, there was a bug in replay. - mutable bool traceEnabled{false}; - - /// If true, verify that the replay results such as returned values from JS - /// execution, inputs from JS to native function calls are matching with the - /// trace record. - bool verificationEnabled{false}; - - /// If true, command-line options override the config options recorded in - /// the trace. If false, start from the default config. - bool useTraceConfig{false}; - - /// Enable basic block profiling. - bool basicBlockProfiling{false}; - - // If non-empty, write profiling output to this file, rather than - // to stderr. - std::string profilingOutFile; - - /// Number of initial executions whose stats are discarded. - int warmupReps{0}; - - /// Number of repetitions of execution. Stats returned are those for the rep - /// with the median totalTime. - int reps{1}; - - /// If non-null, holds statistics for every garbage collection that occurs. - const std::vector<::hermes::vm::GCAnalyticsEvent> *gcAnalyticsEvents{ - nullptr}; - - /// If true, run a complete collection before printing stats. Useful for - /// guaranteeing there's no garbage in heap size numbers. - bool forceGCBeforeStats{false}; - - /// If true, use the Hermes VM JIT during execution. - bool enableJIT{false}; - - /// If true, remove the requirement that the input bytecode was compiled - /// from the same source used to record the trace. There must only be one - /// input bytecode file in this case. If its observable behavior deviates - /// from the trace, the results are undefined. - bool disableSourceHashCheck{false}; - - /// A trace contains many MarkerRecords which have a name used to identify - /// them. If the replay encounters this given marker, perform an action - /// described by MarkerAction. All actions will stop the trace early and - /// collect stats at the marker point, unless the marker is set to the - /// special marker "end". In that case the trace will run to completion. - std::string marker{"end"}; - - enum class MarkerAction { - NONE, - /// Take a snapshot at marker. - SNAPSHOT, - /// Take a heap timeline that ends at marker. - TIMELINE, - /// Take a sampling heap profile that ends at marker. - SAMPLE_MEMORY, - /// Take a sampling time profile that ends at marker. - SAMPLE_TIME, - }; - - /// Sets the action to take upon encountering the marker. The action will - /// write results into the \p profileFileName. - MarkerAction action{MarkerAction::NONE}; - - /// Output file name for any profiling information. - std::string profileFileName; - - // These are the config parameters. We wrap them in llvh::Optional - // to indicate whether the corresponding command line flag was set - // explicitly. We override the trace's config only when that is true. - - /// If true, track all disk I/O done by the runtime and print a report at - /// the end to stdout. - llvh::Optional shouldTrackIO; - - /// If present, do a bytecode warmup run that touches a percentage of the - /// bytecode. A value of 50 here means 50% of the bytecode should be warmed. - llvh::Optional bytecodeWarmupPercent; - }; - - private: - jsi::Runtime &rt_; - ExecuteOptions options_; - llvh::raw_ostream *traceStream_; - // Map from source hash to source file to run. - std::map<::hermes::SHA1, std::shared_ptr> bundles_; - const SynthTrace &trace_; - - /// The last use of each object. - std::unordered_map lastUsePerObj_; - - /// The list of pairs from record index to ObjectID. Each record index is the - /// lastly used position of each Object, at which we can remove the object - /// from gom_ and gpnm_. - std::vector> lastUses_; - /// Index of lastUses_ vector that the interpreter is currently processing. - uint64_t lastUsesIndex_{0}; - - // Invariant: the value is either jsi::Object, jsi::String, jsi::Symbol, - // jsi::BigInt. - std::unordered_map gom_; - // For the PropNameIDs, which are not representable as jsi::Value. - std::unordered_map gpnm_; - - std::string stats_; - /// Whether the marker was reached. - bool markerFound_{false}; - /// Depth in the execution stack. Zero is the outermost function. - uint64_t depth_{0}; - - /// The index of the record that the TraceInterpreter is executing. - uint64_t nextExecIndex_{0}; - - public: - /// Execute the trace given by \p traceFile, that was the trace of executing - /// the bundle given by \p bytecodeFile. - /// \return The stats collected by the runtime about times and memory usage. - static std::string execAndGetStats( - const std::string &traceFile, - const std::vector &bytecodeFiles, - const ExecuteOptions &options); - - /// Same as execAndGetStats, except it additionally accepts a function to - /// create the runtime instance for replaying. This can be used to pass, for - /// example, TracingRuntime to trace while replaying. - static std::string execWithRuntime( - const std::string &traceFile, - const std::vector &bytecodeFiles, - const ExecuteOptions &options, - const std::function( - const ::hermes::vm::RuntimeConfig &runtimeConfig)> &createRuntime); - - /// \param traceStream If non-null, write a trace of the execution into this - /// stream. - /// \return Tuple of GC stats and the runtime instance used for replaying. - static std::tuple> - execFromMemoryBuffer( - std::unique_ptr &&traceBuf, - std::vector> &&codeBufs, - const ExecuteOptions &options, - const std::function( - const ::hermes::vm::RuntimeConfig &runtimeConfig)> &createRuntime); - - private: - TraceInterpreter( - jsi::Runtime &rt, - const ExecuteOptions &options, - const SynthTrace &trace, - std::map<::hermes::SHA1, std::shared_ptr> bundles); - - static std::string exec( - jsi::Runtime &rt, - const ExecuteOptions &options, - const SynthTrace &trace, - std::map<::hermes::SHA1, std::shared_ptr> bundles); - - static ::hermes::vm::RuntimeConfig merge( - ::hermes::vm::RuntimeConfig::Builder &, - const ::hermes::vm::GCConfig::Builder &, - const ExecuteOptions &, - bool, - bool); - - /// Requires \p codeBufs to be the memory buffers containing the code - /// referenced (via source hash) by the given \p trace. Returns a map from - /// the source hash to the memory buffer. In addition, if \p codeIsMmapped is - /// non-null, sets \p *codeIsMmapped to indicate whether all the code is - /// mmapped, and, if \p isBytecode is non-null, sets \p *isBytecode - /// to indicate whether all the code is bytecode. - static std::map<::hermes::SHA1, std::shared_ptr> - getSourceHashToBundleMap( - std::vector> &&codeBufs, - const SynthTrace &trace, - const ExecuteOptions &options, - bool *codeIsMmapped = nullptr, - bool *isBytecode = nullptr); - - jsi::Function createHostFunction( - const SynthTrace::CreateHostFunctionRecord &rec, - const jsi::PropNameID &propNameID); - - jsi::Object createHostObject(SynthTrace::ObjectID objID); - - /// Execute the records with the given ExecuteOptions::MarkerOption - std::string executeRecordsWithMarkerOptions(); - - /// Execute the records. JS might call this recursively when HostFunction or - /// HostObject's functions are called. - void executeRecords(); - - /// Requires that \p valID is the proper id for \p val, and that a - /// defining occurrence of \p valID occurs at the current \p defIndex. Decides - /// whether the definition should be recorded, and, if so, adds the - /// association between \p valID and \p val \p gom_ as appropriate. - void addToObjectMap( - SynthTrace::ObjectID valID, - jsi::Value &&val, - uint64_t defIndex); - - /// Similar to addToObjectMap, but for PropNameIDs. - void addToPropNameIDMap( - SynthTrace::ObjectID id, - jsi::PropNameID &&val, - uint64_t defIndex); - - /// If \p traceValue specifies an Object, String, BigInt or Symbol, requires - /// \p val to be of the corresponding runtime type. Adds this \p val to gom_. - /// - /// \p isThis should be true if and only if the value is a 'this' in a call - /// (only used for validation). TODO(T84791675): Remove this parameter. - /// - /// N.B. This method should be called even if you happen to know that the - /// value cannot be an Object, String, Symbol or BigInt, since it performs - /// useful validation. - void ifObjectAddToObjectMap( - SynthTrace::TraceValue traceValue, - const jsi::Value &val, - uint64_t defIndex, - bool isThis = false); - - /// Same as above, except it avoids copies on temporary objects. - void ifObjectAddToObjectMap( - SynthTrace::TraceValue traceValue, - jsi::Value &&val, - uint64_t defIndex, - bool isThis = false); - - /// Check if the \p marker is the one that is being searched for. If this is - /// the first time encountering the matching marker, perform the actions set - /// up for that marker. - void checkMarker(const std::string &marker); - - /// Get a jsi::Value from gom_ for given ObjectID. - jsi::Value getJSIValueForUse(SynthTrace::ObjectID id); - - /// Get a jsi::PropNameID from gpnm_ for given ObjectID. - jsi::PropNameID getPropNameIDForUse(SynthTrace::ObjectID id); - - /// Convert a TraceValue to a jsi::Value. This calls \p getJSIValueForUse, - /// which will remove the entry from gom_ and globalDefsAndUses_. - jsi::Value traceValueToJSIValue(SynthTrace::TraceValue value); - - /// Erase all references to objects of which last use is before the given - /// record index. - void eraseRefsBefore(uint64_t index); - - std::string printStats(); - - LLVM_ATTRIBUTE_NORETURN void crashOnException( - const std::exception &e, - ::hermes::OptValue globalRecordNum); - - void assertMatch( - const SynthTrace::TraceValue &traceValue, - const jsi::Value &val) const; -}; - -} // namespace tracing -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TracingRuntime.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TracingRuntime.h deleted file mode 100644 index a22cc8a2..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/TracingRuntime.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_TRACINGRUNTIME_H -#define HERMES_TRACINGRUNTIME_H - -#include "SynthTrace.h" - -#include -#include -#include "llvh/Support/raw_ostream.h" - -namespace facebook { -namespace hermes { -namespace tracing { - -class TracingRuntime : public jsi::RuntimeDecorator { - public: - using RD = RuntimeDecorator; - - TracingRuntime( - std::shared_ptr runtime, - const ::hermes::vm::RuntimeConfig &conf, - std::unique_ptr traceStream); - - /// Assign a new ObjectID for given jsi::Pointer. - SynthTrace::ObjectID defObjectID(const jsi::Pointer &p); - /// Get the ObjectID for given jsi::Pointer. - SynthTrace::ObjectID useObjectID(const jsi::Pointer &p) const; - - virtual void flushAndDisableTrace() = 0; - - /// @name jsi::Runtime methods. - /// @{ - - jsi::Value evaluateJavaScript( - const std::shared_ptr &buffer, - const std::string &sourceURL) override; - - void queueMicrotask(const jsi::Function &callback) override; - bool drainMicrotasks(int maxMicrotasksHint = -1) override; - - jsi::Object global() override; - - jsi::Object createObject() override; - jsi::Object createObjectWithPrototype(const jsi::Value &prototype) override; - jsi::Object createObject(std::shared_ptr ho) override; - - // Note that the NativeState methods do not need to be traced since they - // cannot be observed in JS. - - jsi::BigInt createBigIntFromInt64(int64_t value) override; - jsi::BigInt createBigIntFromUint64(uint64_t value) override; - jsi::String bigintToString(const jsi::BigInt &bigint, int radix) override; - - jsi::String createStringFromAscii(const char *str, size_t length) override; - jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; - jsi::String createStringFromUtf16(const char16_t *utf16, size_t length) - override; - std::string utf8(const jsi::PropNameID &) override; - - jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) - override; - jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) - override; - jsi::PropNameID createPropNameIDFromUtf16( - const char16_t *utf16, - size_t length) override; - std::string utf8(const jsi::String &) override; - - std::u16string utf16(const jsi::PropNameID &) override; - std::u16string utf16(const jsi::String &) override; - - void getStringData( - const jsi::String &str, - void *ctx, - void (*cb)(void *ctx, bool ascii, const void *data, size_t num)) override; - - void getPropNameIdData( - const jsi::PropNameID &sym, - void *ctx, - void (*cb)(void *ctx, bool ascii, const void *data, size_t num)) override; - - std::string symbolToString(const jsi::Symbol &) override; - - jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override; - jsi::PropNameID createPropNameIDFromSymbol(const jsi::Symbol &sym) override; - - jsi::Value getProperty(const jsi::Object &obj, const jsi::String &name) - override; - jsi::Value getProperty(const jsi::Object &obj, const jsi::PropNameID &name) - override; - jsi::Value getProperty(const jsi::Object &obj, const jsi::Value &name) - override; - - bool hasProperty(const jsi::Object &obj, const jsi::String &name) override; - bool hasProperty(const jsi::Object &obj, const jsi::PropNameID &name) - override; - bool hasProperty(const jsi::Object &obj, const jsi::Value &name) override; - - void setPropertyValue( - const jsi::Object &obj, - const jsi::String &name, - const jsi::Value &value) override; - void setPropertyValue( - const jsi::Object &obj, - const jsi::PropNameID &name, - const jsi::Value &value) override; - void setPropertyValue( - const jsi::Object &obj, - const jsi::Value &name, - const jsi::Value &value) override; - - void deleteProperty(const jsi::Object &obj, const jsi::PropNameID &name) - override; - void deleteProperty(const jsi::Object &obj, const jsi::String &name) override; - void deleteProperty(const jsi::Object &, const jsi::Value &name) override; - - void setPrototypeOf(const jsi::Object &object, const jsi::Value &prototype) - override; - jsi::Value getPrototypeOf(const jsi::Object &object) override; - - jsi::Array getPropertyNames(const jsi::Object &o) override; - - jsi::WeakObject createWeakObject(const jsi::Object &o) override; - - jsi::Value lockWeakObject(const jsi::WeakObject &wo) override; - - jsi::Array createArray(size_t length) override; - jsi::ArrayBuffer createArrayBuffer( - std::shared_ptr buffer) override; - - size_t size(const jsi::Array &arr) override; - size_t size(const jsi::ArrayBuffer &buf) override; - - uint8_t *data(const jsi::ArrayBuffer &buf) override; - - jsi::Value getValueAtIndex(const jsi::Array &arr, size_t i) override; - - void setValueAtIndexImpl( - const jsi::Array &arr, - size_t i, - const jsi::Value &value) override; - - jsi::Function createFunctionFromHostFunction( - const jsi::PropNameID &name, - unsigned int paramCount, - jsi::HostFunctionType func) override; - - jsi::Value call( - const jsi::Function &func, - const jsi::Value &jsThis, - const jsi::Value *args, - size_t count) override; - - jsi::Value callAsConstructor( - const jsi::Function &func, - const jsi::Value *args, - size_t count) override; - - void setExternalMemoryPressure(const jsi::Object &obj, size_t amount) - override; - - /// @} - - void addMarker(const std::string &marker); - - SynthTrace &trace() { - return trace_; - } - - const SynthTrace &trace() const { - return trace_; - } - - void replaceNondeterministicFuncs(); - - // This is the number of records recorded as part of the 'preamble' of a synth - // trace. This means all the records after this amount are from the actual - // execution of the trace. - uint32_t getNumPreambleRecordsForTest() const { - assert( - numPreambleRecords_ > 0 && - "Only call this method if the preamble has been executed"); - return numPreambleRecords_; - } - - private: - SynthTrace::TraceValue defTraceValue(const jsi::Value &value) { - return toTraceValue(value, true); - } - SynthTrace::TraceValue useTraceValue(const jsi::Value &value) { - return toTraceValue(value, false); - } - SynthTrace::TraceValue toTraceValue( - const jsi::Value &value, - bool assignNewUID = false); - - std::vector argStringifyer( - const jsi::Value *args, - size_t count, - bool assignNewUID = false); - - SynthTrace::TimeSinceStart getTimeSinceStart() const; - - std::shared_ptr runtime_; - SynthTrace trace_; - std::deque savedFunctions; - const SynthTrace::TimePoint startTime_{std::chrono::steady_clock::now()}; - uint32_t numPreambleRecords_; - - SynthTrace::ObjectID currentUniqueID_{0}; - - /// Map from PointerValue* to ObjectID. Except WeakRef case (see below), we - /// assign a new ObjectID whenever we see a new def of jsi::Pointer Value. - std::unordered_map - uniqueIDs_; - - /// WeakObject's PointerValue* to ObjectID mapping. - /// The key is the PointerValue of the WeakObject at the time of - /// it is created. - /// The value is newly assign ObjectID for that PointerValue. - std::unordered_map - weakRefIDs_; -}; - -// TracingRuntime is *almost* vm independent. This provides the -// vm-specific bits. And, it's not a HermesRuntime, but it holds one. -class TracingHermesRuntime final : public TracingRuntime { - public: - /// This constructor is not intended to be invoked directly. - /// Use makeTracingHermesRuntime instead. - /// - /// \p traceStream the stream to write trace to. - /// \p commitAction is invoked on completion of tracing. - /// Completion can be triggered implicitly by crash (if crash manager is - /// provided) or explicitly by invocation of flush. If the committed trace - /// can be found in a file, the callback returns the file name. Otherwise, - /// the callback returns empty. - /// \p rollbackAction is invoked if the runtime is destructed prior to - /// completion of tracing. It may or may not invoked if completion failed. - TracingHermesRuntime( - std::shared_ptr runtime, - const ::hermes::vm::RuntimeConfig &runtimeConfig, - std::unique_ptr traceStream, - std::function commitAction, - std::function rollbackAction); - - ~TracingHermesRuntime() override; - - void flushAndDisableTrace() override; - - std::string flushAndDisableBridgeTrafficTrace() override; - - jsi::Value evaluateJavaScript( - const std::shared_ptr &buffer, - const std::string &sourceURL) override; - - HermesRuntime &hermesRuntime() { - return static_cast(plain()); - } - - const HermesRuntime &hermesRuntime() const { - return static_cast(plain()); - } - - private: - void crashCallback(int fd); - - const ::hermes::vm::RuntimeConfig conf_; - const std::function commitAction_; - const std::function rollbackAction_; - const llvh::Optional<::hermes::vm::CrashManager::CallbackKey> - crashCallbackKey_; - - bool flushedAndDisabled_{false}; - std::string committedTraceFilename_; -}; - -/// Creates and returns a HermesRuntime that traces JSI interactions. -/// The trace will be written to \p traceScratchPath incrementally. -/// On completion, the file will be renamed to \p traceResultPath, and -/// \p traceCompletionCallback (for post-processing) will be invoked. -/// Completion can be triggered implicitly by crash (if crash manager is -/// provided) or explicitly by invocation of flush. -/// If the runtime is destructed without triggering trace completion, -/// the file at \p traceScratchPath will be deleted. -/// The return value of \p traceCompletionCallback indicates whether the -/// invocation completed successfully. -std::unique_ptr makeTracingHermesRuntime( - std::shared_ptr hermesRuntime, - const ::hermes::vm::RuntimeConfig &runtimeConfig, - const std::string &traceScratchPath, - const std::string &traceResultPath, - std::function traceCompletionCallback); - -/// Creates and returns a HermesRuntime that traces JSI interactions. -/// If \p traceStream is non-null, writes the trace to \p traceStream. -/// The \p forReplay parameter indicates whether the runtime is being used -/// in trace replay. (Its behavior can differ slightly in that case.) -std::unique_ptr makeTracingHermesRuntime( - std::shared_ptr hermesRuntime, - const ::hermes::vm::RuntimeConfig &runtimeConfig, - std::unique_ptr traceStream, - bool forReplay = false); - -} // namespace tracing -} // namespace hermes -} // namespace facebook - -#endif // HERMES_TRACINGRUNTIME_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CDPAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CDPAgent.h deleted file mode 100644 index 55644044..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CDPAgent.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_CDPAGENT_H -#define HERMES_CDP_CDPAGENT_H - -#include -#include - -#include -#include -#include -#include - -class CDPAgentTest; - -namespace facebook { -namespace hermes { -namespace cdp { - -using OutboundMessageFunc = std::function; - -class CDPAgentImpl; -class CDPDebugAPI; - -/// Public-facing wrapper for internal CDP state that can be preserved across -/// reloads. -class HERMES_EXPORT State { - public: - /// Incomplete type that stores the actual state. - struct Private; - - /// Create a new empty wrapper. - State(); - /// Create a new wrapper with the provided \p privateState. - explicit State(std::unique_ptr privateState); - - State(const State &other) = delete; - State &operator=(const State &other) = delete; - State(State &&other) noexcept; - State &operator=(State &&other) noexcept; - ~State(); - - inline operator bool() const { - return privateState_ != nullptr; - } - - /// Get the wrapped state. - inline Private &operator*() { - return *privateState_.get(); - } - - /// Get the wrapped state. - inline Private *operator->() { - return privateState_.get(); - } - - private: - /// Pointer to the actual stored state, hidden from users of this wrapper. - std::unique_ptr privateState_; -}; - -/// An agent for interacting with the provided \p runtime and -/// \p asyncDebuggerAPI via CDP messages in the Debugger, Runtime, Profiler, -/// HeapProfiler domains. -/// The integrator of the agent is expected to manage a queue of tasks to be -/// executed with exclusive access to the runtime (i.e. executed when -/// JavaScript is not running). Tasks to be run are delivered to the integrator -/// via the provided \p enqueueRuntimeTaskCallback, and should be executed in -/// order, at the first opportunity between evaluating JavaScript. -/// The integrator can deliver CDP commands to the agent via the -/// \p handleCommand method. When a CDP response or event is generated, it will -/// be delivered to the integrator via the provided \p messageCallback. -/// Both callbacks may be invoked from arbitrary threads. -class HERMES_EXPORT CDPAgent { - friend class ::CDPAgentTest; - - /// Hide the constructor so users can only construct via static create - /// methods. - CDPAgent( - int32_t executionContextID, - CDPDebugAPI &cdpDebugAPI, - debugger::EnqueueRuntimeTaskFunc enqueueRuntimeTaskCallback, - OutboundMessageFunc messageCallback, - State state, - std::shared_ptr destroyedDomainAgents); - - public: - /// Create a new CDP Agent. This can be done on an arbitrary thread; the - /// runtime will not be accessed during execution of this function. - static std::unique_ptr create( - int32_t executionContextID, - CDPDebugAPI &cdpDebugAPI, - debugger::EnqueueRuntimeTaskFunc enqueueRuntimeTaskCallback, - OutboundMessageFunc messageCallback, - State state = {}); - - /// Destroy the CDP Agent. This can be done on an arbitrary thread. - /// It's expected that the integrator will continue to process any runtime - /// tasks enqueued during destruction. - ~CDPAgent(); - - /// This function can be called from arbitrary threads. It processes a CDP - /// command encoded in \p json as UTF-8 in accordance with RFC-8259. See: - // https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/public/devtools_protocol/#wire-format_strings-and-binary-values - void handleCommand(std::string json); - - /// Enable the Runtime domain without processing a CDP command or sending a - /// CDP response. This can be called from arbitrary threads. - void enableRuntimeDomain(); - - /// Enable the Debugger domain without processing a CDP command or sending a - /// CDP response. This can be called from arbitrary threads. - void enableDebuggerDomain(); - - /// Extract state to be persisted across reloads. This can be called from - /// arbitrary threads. - State getState(); - - private: - /// This should be a unique_ptr to provide predictable destruction time lined - /// up with when CDPAgent is destroyed. Do not use shared_ptr. - std::unique_ptr impl_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_CDPAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CDPDebugAPI.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CDPDebugAPI.h deleted file mode 100644 index 9809ec9a..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CDPDebugAPI.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_CDPDEBUGAPI_H -#define HERMES_CDP_CDPDEBUGAPI_H - -#include - -#include "ConsoleMessage.h" - -namespace facebook { -namespace hermes { -namespace cdp { - -class CDPAgentImpl; - -/// Storage and interfaces for carrying out a CDP debug session. Contains -/// information and operations that correspond to a single runtime being -/// debugged, independent of any particular CDPAgent. -class HERMES_EXPORT CDPDebugAPI { - public: - /// Create a new CDPDebugAPI instance. The provided runtime must remain valid - /// until the returned CDPDebugAPI is destroyed. - static std::unique_ptr create( - HermesRuntime &runtime, - size_t maxCachedMessages = kMaxCachedConsoleMessages); - ~CDPDebugAPI(); - - /// Gets the runtime originally passed into this instance. - HermesRuntime &runtime() { - return runtime_; - } - - /// Gets the AsyncDebuggerAPI associated with this instance. - debugger::AsyncDebuggerAPI &asyncDebuggerAPI() { - return *asyncDebuggerAPI_; - } - - /// Adds a console message to the current CDPDebugAPI instance, - /// broadcasting it to all current agents, and storing it for - /// future agents (within buffer limitations). This function - /// must only be called from the runtime thread. - void addConsoleMessage(ConsoleMessage message); - - private: - /// Allow CDPAgentImpl (but not integrators) to access - /// consoleMessageStorage_. - friend class CDPAgentImpl; - - CDPDebugAPI(HermesRuntime &runtime, size_t maxCachedMessages); - - HermesRuntime &runtime_; - std::unique_ptr asyncDebuggerAPI_; - ConsoleMessageStorage consoleMessageStorage_; - ConsoleMessageDispatcher consoleMessageDispatcher_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_CDPDEBUGAPI_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CallbackOStream.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CallbackOStream.h deleted file mode 100644 index 8a846344..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/CallbackOStream.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_CALLBACKOSTREAM_H -#define HERMES_CDP_CALLBACKOSTREAM_H - -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace cdp { - -/// Subclass of \c std::ostream where flushing is implemented through a -/// callback. Writes are collected in a buffer. When filled, the buffer's -/// contents are emptied out and sent to a callback. -class CallbackOStream : public std::ostream { - public: - /// Signature of callback called to flush buffer contents. Accepts the buffer - /// as a string. Returns a boolean indicating whether flushing succeeded. - /// Callback failure will be translated to stream failure. If the callback - /// throws an exception it will be swallowed and translated into stream - /// failure. - using Fn = std::function; - - /// Construct a new stream. - /// - /// \p sz The size of the buffer -- how large it can get before it must be - /// flushed. Must be non-zero. - /// \p cb The callback function. - CallbackOStream(size_t sz, Fn cb); - - /// This class is neither movable nor copyable. - CallbackOStream(CallbackOStream &&that) = delete; - CallbackOStream &operator=(CallbackOStream &&that) = delete; - CallbackOStream(const CallbackOStream &that) = delete; - CallbackOStream &operator=(const CallbackOStream &that) = delete; - - private: - /// \c std::streambuf sub-class backed by a std::string buffer and - /// implementing overflow by calling a callback. - class StreamBuf : public std::streambuf { - public: - /// Construct a new streambuf. Parameters are the same as those of - /// \c CallbackOStream . - StreamBuf(size_t sz, Fn cb); - - /// Destruction will flush any remaining buffer contents. - ~StreamBuf() override; - - /// StreamBufs are not copyable, to avoid the flush callback receiving - /// the contents of multiple streams. - StreamBuf(const StreamBuf &) = delete; - StreamBuf &operator=(const StreamBuf &) = delete; - - protected: - /// std::streambuf overrides - int_type overflow(int_type ch) override; - int sync() override; - - private: - /// The size of the backing buffer. Fixed for an instance of the streambuf. - size_t sz_; - - /// The backing buffer that writes will go to until full. - std::unique_ptr buf_; - - /// The function called when buf_ has been filled. - Fn cb_; - - /// Clears the backing buffer. - void reset(); - - /// Clears the backing buffer and returns it contents in a string. - std::string take(); - }; - - StreamBuf sbuf_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_CALLBACKOSTREAM_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/ConsoleMessage.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/ConsoleMessage.h deleted file mode 100644 index 906dbb9a..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/ConsoleMessage.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_CDPCONSOLEMESSAGESTORAGE_H -#define HERMES_CDP_CDPCONSOLEMESSAGESTORAGE_H - -#include -#include -#include - -#include - -#include - -namespace facebook { -namespace hermes { -namespace cdp { - -/// Controls the max number of message to cached in \p consoleMessageCache_. The -/// value here is chosen to match what Chromium uses in their CDP -/// implementation. -static const int kMaxCachedConsoleMessages = 1000; - -enum class ConsoleAPIType { - kLog, - kDebug, - kInfo, - kError, - kWarning, - kDir, - kDirXML, - kTable, - kTrace, - kStartGroup, - kStartGroupCollapsed, - kEndGroup, - kClear, - kAssert, - kTimeEnd, - kCount -}; - -struct ConsoleMessage { - double timestamp; - ConsoleAPIType type; - std::vector args; - debugger::StackTrace stackTrace; - - ConsoleMessage( - double timestamp, - ConsoleAPIType type, - std::vector args, - debugger::StackTrace stackTrace = {}) - : timestamp(timestamp), - type(type), - args(std::move(args)), - stackTrace(stackTrace) {} -}; - -class ConsoleMessageStorage { - public: - ConsoleMessageStorage(size_t maxCachedMessages = kMaxCachedConsoleMessages); - - void addMessage(ConsoleMessage message); - void clear(); - - const std::deque &messages() const; - size_t discarded() const; - std::optional oldestTimestamp() const; - - private: - /// Maximum number of messages to cache. - size_t maxCachedMessages_; - /// Counts the number of console messages discarded when - /// \p consoleMessageCache_ is full. - size_t numConsoleMessagesDiscardedFromCache_ = 0; - /// Cache for storing console messages. Earlier messages are discarded when - /// the cache is full. The choice to use a std::deque is for fast operations - /// at the beginning and the end, so that adding to the cache and discarding - /// from the cache are fast. - std::deque consoleMessageCache_{}; -}; - -class CDPAgent; - -/// Token that identifies a specific subscription to console messages. -using ConsoleMessageRegistration = uint32_t; - -/// Dispatcher to deliver console messages to all registered subscribers. -/// Everything in this class must be used exclusively from the runtime thread. -class ConsoleMessageDispatcher { - public: - ConsoleMessageDispatcher() {} - ~ConsoleMessageDispatcher() {} - - /// Register a subscriber and return a token that can be used to - /// unregister in the future. Must only be called from the runtime thread. - ConsoleMessageRegistration subscribe( - std::function handler) { - auto token = ++tokenCounter_; - subscribers_[token] = handler; - return token; - } - - /// Unregister a subscriber using the token returned from registration. - /// Must only be called from the runtime thread. - void unsubscribe(ConsoleMessageRegistration token) { - subscribers_.erase(token); - } - - /// Deliver a new console message to each subscriber. Must only be called - /// from the runtime thread. - void deliverMessage(const ConsoleMessage &message) { - for (auto &pair : subscribers_) { - pair.second(message); - } - } - - private: - /// Collection of subscribers, identified by registration token. - std::unordered_map< - ConsoleMessageRegistration, - std::function> - subscribers_; - - /// Counter to generate unique registration tokens. - ConsoleMessageRegistration tokenCounter_ = 0; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_CDPCONSOLEMESSAGESTORAGE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DebuggerDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DebuggerDomainAgent.h deleted file mode 100644 index 435cdb03..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DebuggerDomainAgent.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_DEBUGGERDOMAINAGENT_H -#define HERMES_CDP_DEBUGGERDOMAINAGENT_H - -#include -#include - -#include -#include -#include - -#include "DomainAgent.h" -#include "DomainState.h" - -namespace facebook { -namespace hermes { -namespace cdp { - -enum class PausedNotificationReason; - -/// Last explicit debugger step command issued by the user. -enum class LastUserStepRequest { - StepInto, - StepOver, - StepOut, -}; - -namespace m = ::facebook::hermes::cdp::message; - -/// Details about a single Hermes breakpoint, implied by a CDP breakpoint. -struct HermesBreakpoint { - debugger::BreakpointID breakpointID; - debugger::ScriptID scriptID; -}; - -/// Type used to store CDP breakpoint identifiers. These IDs are generated by -/// the CDP Handler, so we can constrain them to a specific range. -using CDPBreakpointID = uint32_t; - -/// Description of where breakpoints should be created. -struct CDPBreakpointDescription : public StateValue { - ~CDPBreakpointDescription() override = default; - std::unique_ptr copy() const override { - auto value = std::make_unique(); - value->line = line; - value->column = column; - value->condition = condition; - value->url = url; - return value; - } - - /// Determines whether this breakpoint can be persisted across sessions - bool persistable() const { - // Only persist breakpoints that can apply to future scripts (i.e. - // breakpoints set on a set of files specified by script URL, not - // breakpoints set on an exact, session-specific script ID). - return url.has_value(); - } - - std::optional url; - long long line; - std::optional column; - std::optional condition; -}; - -/// Details of each existing CDP breakpoint, which may correspond to multiple -/// Hermes breakpoints. -struct CDPBreakpoint { - explicit CDPBreakpoint(CDPBreakpointDescription description) - : description(description) {} - - // Description of where the breakpoint should be applied - CDPBreakpointDescription description; - - // Registered breakpoints in Hermes - std::vector hermesBreakpoints; -}; - -struct HermesBreakpointLocation { - debugger::BreakpointID id; - debugger::SourceLocation location; -}; - -/// Handler for the "Debugger" domain of CDP. Accepts events from the runtime, -/// and CDP requests from the debug client belonging to the "Debugger" domain. -/// Produces CDP responses and events belonging to the "Debugger" domain. All -/// methods expect to be invoked with exclusive access to the runtime. -class DebuggerDomainAgent : public DomainAgent { - public: - DebuggerDomainAgent( - int32_t executionContextID, - HermesRuntime &runtime, - debugger::AsyncDebuggerAPI &asyncDebugger, - SynchronizedOutboundCallback messageCallback, - std::shared_ptr objTable_, - DomainState &state); - ~DebuggerDomainAgent(); - - /// Enables the Debugger domain without processing CDP message or sending a - /// CDP response. It will still send CDP notifications if needed. - void enable(); - /// Handles Debugger.enable request - /// @cdp Debugger.enable If domain is already enabled, will return success. - void enable(const m::debugger::EnableRequest &req); - /// Handles Debugger.disable request - /// @cdp Debugger.disable If domain is already disabled, will return success. - void disable(const m::debugger::DisableRequest &req); - - /// Handles Debugger.pause request - void pause(const m::debugger::PauseRequest &req); - /// Handles Debugger.resume request - void resume(const m::debugger::ResumeRequest &req); - - /// Handles Debugger.stepInto request - void stepInto(const m::debugger::StepIntoRequest &req); - /// Handles Debugger.stepOut request - void stepOut(const m::debugger::StepOutRequest &req); - /// Handles Debugger.stepOver request - void stepOver(const m::debugger::StepOverRequest &req); - - /// Handles Debugger.setBlackboxedRanges request - void setBlackboxedRanges(const m::debugger::SetBlackboxedRangesRequest &req); - /// Handles Debugger.setBlackboxPatterns request - void setBlackboxPatterns(const m::debugger::SetBlackboxPatternsRequest &req); - /// Handles Debugger.setPauseOnExceptions - void setPauseOnExceptions( - const m::debugger::SetPauseOnExceptionsRequest &req); - - /// Handles Debugger.evaluateOnCallFrame - void evaluateOnCallFrame(const m::debugger::EvaluateOnCallFrameRequest &req); - - /// Debugger.setBreakpoint creates a CDP breakpoint that applies to exactly - /// one script (identified by script ID) that does not survive reloads. - void setBreakpoint(const m::debugger::SetBreakpointRequest &req); - // Debugger.setBreakpointByUrl creates a CDP breakpoint that may apply to - // multiple scripts (identified by URL), and survives reloads. - void setBreakpointByUrl(const m::debugger::SetBreakpointByUrlRequest &req); - /// Handles Debugger.removeBreakpoint - void removeBreakpoint(const m::debugger::RemoveBreakpointRequest &req); - /// Handles Debugger.setBreakpointsActive - /// @cdp Debugger.setBreakpointsActive Allowed even if domain is not enabled. - void setBreakpointsActive( - const m::debugger::SetBreakpointsActiveRequest &req); - - private: - /// Handle an event originating from the runtime. - void handleDebuggerEvent( - HermesRuntime &runtime, - debugger::AsyncDebuggerAPI &asyncDebugger, - debugger::DebuggerEventType event); - - /// Send a Debugger.paused notification to the debug client - void sendPausedNotificationToClient(PausedNotificationReason reason); - /// Send a Debugger.scriptParsed notification to the debug client - void sendScriptParsedNotificationToClient( - const debugger::SourceLocation srcLoc); - - /// Obtain the newly loaded script and send a ScriptParsed notification to the - /// debug client - void processNewLoadedScript(); - - std::pair createCDPBreakpoint( - CDPBreakpointDescription &&description, - std::optional hermesBreakpoint = std::nullopt); - - std::optional createHermesBreakpoint( - debugger::ScriptID scriptID, - const CDPBreakpointDescription &description); - - void applyBreakpointAndSendNotification( - CDPBreakpointID cdpBreakpointID, - CDPBreakpoint &cdpBreakpoint, - const debugger::SourceLocation &srcLoc); - - std::optional applyBreakpoint( - CDPBreakpoint &cdpBreakpoint, - debugger::ScriptID scriptID); - - /// Holds a boolean that determines if scripts without a script url - /// (e.g. anonymous scripts) should be blackboxed. - /// Same as V8: - /// https://source.chromium.org/chromium/chromium/src/+/fef5d519bab86dbd712d76bfca5be90a6e03459c:v8/src/inspector/v8-debugger-agent-impl.cc;l=997-999 - bool blackboxAnonymousScripts_ = false; - /// Optionally, holds a compiled regex pattern that is used to test if - /// script urls should be blackboxed. - /// See isLocationBlackboxed below for more details. Same as V8: - /// https://source.chromium.org/chromium/chromium/src/+/fef5d519bab86dbd712d76bfca5be90a6e03459c:v8/src/inspector/v8-debugger-agent-impl.cc;l=993-996 - /// Matching using the compiled regex should be done with - /// ::hermes::regex::searchWithBytecode. - std::optional> compiledBlackboxPatternRegex_; - - /// A vector of 1-based positions per script id indicating where blackbox - /// state changes using [from inclusive, to exclusive) pairs. - /// [ (start) ... position[0]) range is not blackboxed - /// [position[0] ... position[1]) range is blackboxed - /// [position[1] ... position[2]) range is not blackboxed ... ... - /// [position[n] ... (end) ) range is blackboxed if n is even, not - /// blackboxed if odd. - /// This is used to determine if the debugger is paused on one of these - /// blackboxed ranges, to prevent the user from stopping there in the - /// following scenarios: - /// 1. Step out- repeats stepping out until reaches a non-blackboxed range. - /// 2. Step over- stepping over to a blackboxed range meaning that - /// the next un-blackboxed range would be after all the stepping in the - /// function are done (because blackboxing is per file, meaning per function - /// as well) so we can execute step out as well in this case until we - /// step out of blackboxed ranges. - /// Comparing with v8, we don’t check if the user comes from a blackboxed - /// range, but only if a stepover got you to a blackboxed range. However - /// both results in the same thing which is stepping out until reaching a - /// non-blackboxed range. - /// 3. Step into- execute another step into. - /// Repeat this step until outside of a blackboxed range. - /// 4. Exceptions triggering the debugger pause- - /// (uncaught or if the user chooses to stop on all exceptions)- - /// ignore and continue execution - /// 5. Debugger statements- ignore and continue execution - /// 6. Explicit pause- keep stepping in until reaching a non-blackboxed range - /// 7. Manual breakpoints- allow stopping in blackboxed ranges - std::unordered_map>> - blackboxedRanges_; - /// Checks whether the passed location falls within a blackboxed range - /// in blackboxedRanges_. - /// Chrome looks at full functions ("frames") to detemine this. See: - /// https://source.chromium.org/chromium/chromium/src/+/318e9cfd9fbbbc70906f6a78d017a2708248dc6d:v8/src/inspector/v8-debugger-agent-impl.cc;l=984-1026 - /// We, on the other hand, look at individual lines since there's no - /// difference in practise because the current way functions are blackboxed is - /// by using ignoreList in source maps, which blackboxes full files, which - /// means also it blackboxes full functions, so there's no difference between - /// checking if a line in a function is blackboxed or if the whole function is - /// blackboxed. - /// This means that we receive one "Debugger.setBlackboxedRanges" per bundle - /// file comprised of source js files. - /// For each file appearing in the "ignoreList" in source maps, we receive the - /// start positions and end positions of the file inside the bundle file: - /// [ file 1 start position, - /// file 1 end position, - /// file 2 start position, - /// file 2 end position, - /// ... ] - bool isLocationBlackboxed( - debugger::ScriptID scriptID, - std::string scriptName, - int lineNumber, - int columnNumber); - /// Checks whether the location of the top frame of the call stack is - /// blackboxed or not using isLocationBlackboxed - bool isTopFrameLocationBlackboxed(); - - bool checkDebuggerEnabled(const m::Request &req); - bool checkDebuggerPaused(const m::Request &req); - - /// Removes any modifications this agent made to Hermes in order to enable - /// debugging - void cleanUp(); - - HermesRuntime &runtime_; - debugger::AsyncDebuggerAPI &asyncDebugger_; - - /// ID for the registered DebuggerEventCallback - debugger::DebuggerEventCallbackID debuggerEventCallbackId_; - - /// Details of each CDP breakpoint that has been created, and not - /// yet destroyed. - std::unordered_map cdpBreakpoints_{}; - - /// CDP breakpoint IDs are assigned by the DebuggerDomainAgent. Keep track of - /// the next available ID. Starts with 100 to avoid confusion with Hermes - /// breakpoints IDs that start with 1. - CDPBreakpointID nextBreakpointID_ = 100; - - DomainState &state_; - - /// Whether the currently installed breakpoints actually take effect. If - /// they're supposed to be inactive, then debugger agent will automatically - /// resume execution when breakpoints are hit. - bool breakpointsActive_; - - /// Whether Debugger.enable was received and wasn't disabled by receiving - /// Debugger.disable - bool enabled_; - - /// Whether to consider the debugger as currently paused. There are some - /// debugger events such as ScriptLoaded where we don't consider the debugger - /// to be paused. - /// Should only be set using setPaused and setUnpaused. - bool paused_; - - /// Called when the runtime is paused. - void setPaused(PausedNotificationReason pausedNotificationReason); - - /// Called when the runtime is resumed. - void setUnpaused(); - - /// Set to true when the user selects to explicitly pause execution. - /// This is set back to false when the execution is paused. - bool explicitPausePending_ = false; - - /// Last explicit step type issued by the user. - /// * This is never reset because cdp can't tell if a step command was - /// completed since a step command that does not result in further operations - /// resolves to a "resume" without "stepFinished" or debugger pause. - /// That means that this member should only be used in situations where we are - /// sure that a step command was issued in the given scenario. For example, a - /// step into command followed by a resume would leave this member holding an - /// "StepInto" even when minutes later the execution stops on a breakpoint. - std::optional lastUserStepRequest_ = std::nullopt; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_DEBUGGERDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DomainAgent.h deleted file mode 100644 index 6770e829..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DomainAgent.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_DOMAINAGENT_H -#define HERMES_CDP_DOMAINAGENT_H - -#include -#include - -#include -#include - -#if defined(__clang__) && (!defined(SWIG)) && defined(_LIBCPP_VERSION) && \ - defined(_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) -#include -#else -#ifndef TSA_GUARDED_BY -#define TSA_GUARDED_BY(x) -#endif -#endif - -namespace facebook { -namespace hermes { -namespace cdp { - -namespace m = ::facebook::hermes::cdp::message; - -/// A wrapper around std::function to make it safe to use from -/// multiple threads. The wrapper implements an invalidate function so that one -/// thread can clean up the underlying std::function in a thread-safe way. -template -class SynchronizedCallback { - public: - SynchronizedCallback(std::function func) - : funcContainer_(std::make_shared(func)) {} - - /// Thread-safe version that calls the underlying std::function. If the - /// underlying std::function is empty, this function is a no-op. - void operator()(Args... args) const { - std::lock_guard lock(funcContainer_->mutex); - if (funcContainer_->func) { - funcContainer_->func(args...); - } - } - - /// Reset the underlying std::function so that future invocations of - /// operator() would just be a no-op. - void invalidate() { - std::lock_guard lock(funcContainer_->mutex); - funcContainer_->func = std::function(); - } - - private: - struct FunctionContainer { - FunctionContainer(std::function func) : func(func) {} - - std::mutex mutex{}; - - /// The actual std::function to be invoked by operator() - std::function func TSA_GUARDED_BY(mutex); - }; - std::shared_ptr funcContainer_; -}; - -using SynchronizedOutboundCallback = SynchronizedCallback; - -class DomainAgent { - protected: - DomainAgent( - int32_t executionContextID, - SynchronizedOutboundCallback messageCallback, - std::shared_ptr objTable) - : executionContextID_(executionContextID), - messageCallback_(messageCallback), - objTable_(objTable) {} - virtual ~DomainAgent() {} - - /// Sends the provided string back to the debug client - void sendToClient(const std::string &str) { - messageCallback_(str); - } - - /// Sends the provided \p Response back to the debug client - void sendResponseToClient(const m::Response &resp) { - sendToClient(resp.toJsonStr()); - } - - /// Sends the provided \p Notification back to the debug client - void sendNotificationToClient(const m::Notification ¬e) { - sendToClient(note.toJsonStr()); - } - - /// Execution context ID associated with the HermesRuntime - int32_t executionContextID_; - - /// Callback function to send CDP response back to the debug client - SynchronizedOutboundCallback messageCallback_; - - std::shared_ptr objTable_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_DOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DomainState.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DomainState.h deleted file mode 100644 index 5eb6bbb2..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/DomainState.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_DOMAINSTATE_H -#define HERMES_CDP_DOMAINSTATE_H - -#include -#include -#include -#include -#include - -#if defined(__clang__) && (!defined(SWIG)) && defined(_LIBCPP_VERSION) && \ - defined(_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) -#include -#else -#ifndef TSA_GUARDED_BY -#define TSA_GUARDED_BY(x) -#endif -#ifndef TSA_REQUIRES -#define TSA_REQUIRES(x) -#endif -#endif - -namespace facebook { -namespace hermes { -namespace cdp { - -/// Base class for data to be stored in DomainState. -struct StateValue { - virtual ~StateValue() = default; - virtual std::unique_ptr copy() const = 0; -}; - -/// StateValue that can be used as a boolean flag. -struct BooleanStateValue : public StateValue { - ~BooleanStateValue() override = default; - std::unique_ptr copy() const override; - - bool value{false}; -}; - -/// StateValue that can be used as a dictionary. Used as the main storage value -/// of DomainState so that modifications can be based on keys of the dictionary -/// hierarchy. -struct DictionaryStateValue : public StateValue { - ~DictionaryStateValue() override = default; - std::unique_ptr copy() const override; - - std::unordered_map> values; -}; - -using StateModification = - std::pair, std::unique_ptr>; - -/// This class acts as container for saving state that CDP agents need after a -/// reload. Its main purpose is to synchronize the manipulation of state on the -/// runtime thread and when CDPAgent::getState() gets called on arbitrary -/// thread. Functions in this class specifically do not contain callbacks to -/// ensure the mutex locking usage remain simple with no reentrancy to think -/// about. -class DomainState { - public: - DomainState(); - explicit DomainState(std::unique_ptr dict); - - /// TSA doesn't get applied to constructors, so delete the normal mechanism. - /// There is a separate copy() function instead. - DomainState(const DomainState &) = delete; - DomainState &operator=(const DomainState &) = delete; - - /// Deep copy of the data and make a new instance. Used by - /// CDPAgent::getState() to get the state in a thread-safe manner. - std::unique_ptr copy(); - - /// This function allows the caller to access values in the saved state. This - /// obtains a copy of the data so that no further synchronization is required - /// after calling this function. This function is expected to only be called a - /// few times after reload, so it isn't used frequently. All entries in the - /// \p paths vector are expected to be pointing to DictionaryStateValue(s) - /// except the last entry, which is a key to any StateValue. - /// \return a copy of the StateValue stored at \p paths, nullptr if no value - /// exists at paths - std::unique_ptr getCopy(std::vector paths); - - /// This class is the only way for callers to manipulate the DomainState. It - /// is a scope-based commit where the modifications get saved upon the class's - /// destruction. The class must not be saved elsewhere and outlive the - /// DomainState where it came from. The intent is to nudge the caller to batch - /// modifications and commit the changes in one go. Because we make a copy of - /// the state with copy(), we want state changes to be atomic. Caller can - /// still break things up into multiple transactions, but the hope is that - /// this nudges them to think about modifications as one atomic unit. - class Transaction { - public: - explicit Transaction(DomainState &state); - ~Transaction(); - - /// Adds a value to the container. All entries in the \p paths vector are - /// expected to be pointing to DictionaryStateValue(s) except the last - /// entry, which is a key to any StateValue. - void add(std::vector paths, const StateValue &value); - - /// Removes a value from the container. All entries in the \p paths vector - /// are expected to be pointing to DictionaryStateValue(s) except the last - /// entry, which is a key to any StateValue. - void remove(std::vector paths); - - private: - friend DomainState; - - DomainState &state_; - std::vector modifications_{}; - }; - - /// Gets a Transaction for modification. - Transaction transaction(); - - private: - /// Helper function for traversing the dictionary hierarchy. - DictionaryStateValue *getDict( - const std::vector &paths, - bool createMissingDict) TSA_REQUIRES(mutex_); - - /// Save modifications to \p dict_. - void commitTransaction(Transaction &transaction); - - std::mutex mutex_{}; - - /// The actual value container. TSA doesn't work if this is just a direct - /// value on the class, so using an unique_ptr. - std::unique_ptr dict_ TSA_GUARDED_BY(mutex_){}; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_DOMAINSTATE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/HeapProfilerDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/HeapProfilerDomainAgent.h deleted file mode 100644 index 227214bc..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/HeapProfilerDomainAgent.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_HEAPPROFILERDOMAINAGENT_H -#define HERMES_CDP_HEAPPROFILERDOMAINAGENT_H - -#include - -#include "DomainAgent.h" - -namespace facebook { -namespace hermes { -namespace cdp { - -/// Handler for the "HeapProfiler" domain of CDP. All methods expect to be -/// invoked with exclusive access to the runtime. -class HeapProfilerDomainAgent : public DomainAgent { - public: - HeapProfilerDomainAgent( - int32_t executionContextID, - HermesRuntime &runtime, - SynchronizedOutboundCallback messageCallback, - std::shared_ptr objTable); - ~HeapProfilerDomainAgent(); - - /// Handles HeapProfiler.takeHeapSnapshot request - void takeHeapSnapshot(const m::heapProfiler::TakeHeapSnapshotRequest &req); - - /// Handle HeapProfiler.getObjectByHeapObjectId - void getObjectByHeapObjectId( - const m::heapProfiler::GetObjectByHeapObjectIdRequest &req); - - /// Handle HeapProfiler.getObjectByHeapObjectId - void getHeapObjectId(const m::heapProfiler::GetHeapObjectIdRequest &req); - - /// Handle HeapProfiler.collectGarbage - void collectGarbage(const m::heapProfiler::CollectGarbageRequest &req); - - /// Handle HeapProfiler.startTrackingHeapObjects - void startTrackingHeapObjects( - const m::heapProfiler::StartTrackingHeapObjectsRequest &req); - - /// Handle HeapProfiler.stopTrackingHeapObjects - void stopTrackingHeapObjects( - const m::heapProfiler::StopTrackingHeapObjectsRequest &req); - - /// Handle HeapProfiler.startSampling - void startSampling(const m::heapProfiler::StartSamplingRequest &req); - - /// Handle HeapProfiler.stopSampling - void stopSampling(const m::heapProfiler::StopSamplingRequest &req); - - private: - void sendSnapshot(int reqId, bool reportProgress, bool captureNumericValue); - - HermesRuntime &runtime_; - - /// Flag indicating whether this agent is registered to receive heap object - /// tracking callbacks. - bool trackingHeapObjectStackTraces_ = false; - - /// Flag indicating whether this agent is currently running a heap sampling - /// session. - bool samplingHeap_ = false; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_HEAPPROFILERDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/JSONValueInterfaces.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/JSONValueInterfaces.h deleted file mode 100644 index 23a12ba8..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/JSONValueInterfaces.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_JSONVALUEINTERFACES_H -#define HERMES_CDP_JSONVALUEINTERFACES_H - -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace cdp { -using namespace ::hermes::parser; - -/// Convert a string to a JSONValue. Will return nullopt if parsing is not -/// successful. -std::optional parseStr( - const std::string &str, - JSONFactory &factory); - -/// Convert a string to a JSON object. Will return nullopt if parsing is not -/// successful, or the resulting JSON value is not an object. -std::optional parseStrAsJsonObj( - const std::string &str, - JSONFactory &factory); - -/// Convert a JSONValue to a string. -std::string jsonValToStr(const JSONValue *v); - -/// Check if two JSONValues are equal. -bool jsonValsEQ(const JSONValue *A, const JSONValue *B); - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_JSONVALUEINTERFACES_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageConverters.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageConverters.h deleted file mode 100644 index 7397bd1d..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageConverters.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_MESSAGECONVERTERS_H -#define HERMES_CDP_MESSAGECONVERTERS_H - -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace cdp { -namespace message { - -template -void setChromeLocation( - T &chromeLoc, - const facebook::hermes::debugger::SourceLocation &hermesLoc) { - if (hermesLoc.line != facebook::hermes::debugger::kInvalidLocation) { - chromeLoc.lineNumber = hermesLoc.line - 1; - } - - if (hermesLoc.column != facebook::hermes::debugger::kInvalidLocation) { - chromeLoc.columnNumber = hermesLoc.column - 1; - } -} - -/// ErrorCode magic numbers match JSC's (see InspectorBackendDispatcher.cpp) -enum class ErrorCode { - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - ServerError = -32000 -}; - -ErrorResponse -makeErrorResponse(int id, ErrorCode code, const std::string &message); - -OkResponse makeOkResponse(int id); - -namespace debugger { - -Location makeLocation(const facebook::hermes::debugger::SourceLocation &loc); - -} // namespace debugger - -namespace runtime { - -CallFrame makeCallFrame(const facebook::hermes::debugger::CallFrameInfo &info); - -std::vector makeCallFrames( - const facebook::hermes::debugger::StackTrace &stackTrace); - -} // namespace runtime - -namespace heapProfiler { - -std::unique_ptr makeSamplingHeapProfile( - const std::string &value); - -} // namespace heapProfiler - -namespace profiler { - -std::unique_ptr makeProfile(const std::string &value); - -} // namespace profiler - -} // namespace message -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_MESSAGECONVERTERS_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageInterfaces.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageInterfaces.h deleted file mode 100644 index f19418f5..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageInterfaces.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_MESSAGEINTERFACES_H -#define HERMES_CDP_MESSAGEINTERFACES_H - -#include -#include -#include -#include -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace cdp { -namespace message { -using namespace ::hermes::parser; - -struct RequestHandler; - -/// Serializable is an interface for objects that can be serialized to and from -/// JSON. -struct Serializable { - virtual ~Serializable() = default; - virtual JSONValue *toJsonVal(JSONFactory &factory) const = 0; - - std::string toJsonStr() const; -}; - -/// Requests are sent from the debugger to the target. -struct Request : public Serializable { - using ParseResult = std::variant, std::string>; - static std::unique_ptr fromJson(const std::string &str); - - Request() = default; - explicit Request(std::string method) : method(method) {} - - // accept dispatches to the appropriate handler method in RequestHandler based - // on the type of the request. - virtual void accept(RequestHandler &handler) const = 0; - - long long id = 0; - std::string method; -}; - -/// Responses are sent from the target to the debugger in response to a Request. -struct Response : public Serializable { - Response() = default; - - std::optional id = std::nullopt; -}; - -/// Notifications are sent from the target to the debugger. This is used to -/// notify the debugger about events that occur in the target, e.g. stopping -/// at a breakpoint. -struct Notification : public Serializable { - Notification() = default; - explicit Notification(std::string method) : method(method) {} - - std::string method; -}; - -} // namespace message -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_MESSAGEINTERFACES_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageTypes.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageTypes.h deleted file mode 100644 index bdc14d39..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageTypes.h +++ /dev/null @@ -1,1279 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<<1284c402aedd087ebdf70e9e76596f1c>> - -#pragma once - -#include -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace cdp { -namespace message { - -template -void deleter(T *p); -using JSONBlob = std::string; -struct UnknownRequest; - -namespace debugger { -using BreakpointId = std::string; -struct BreakpointResolvedNotification; -struct CallFrame; -using CallFrameId = std::string; -struct DisableRequest; -struct EnableRequest; -struct EvaluateOnCallFrameRequest; -struct EvaluateOnCallFrameResponse; -struct Location; -struct PauseRequest; -struct PausedNotification; -struct RemoveBreakpointRequest; -struct ResumeRequest; -struct ResumedNotification; -struct Scope; -using ScriptLanguage = std::string; -struct ScriptParsedNotification; -struct ScriptPosition; -struct SetBlackboxPatternsRequest; -struct SetBlackboxedRangesRequest; -struct SetBreakpointByUrlRequest; -struct SetBreakpointByUrlResponse; -struct SetBreakpointRequest; -struct SetBreakpointResponse; -struct SetBreakpointsActiveRequest; -struct SetInstrumentationBreakpointRequest; -struct SetInstrumentationBreakpointResponse; -struct SetPauseOnExceptionsRequest; -struct StepIntoRequest; -struct StepOutRequest; -struct StepOverRequest; -} // namespace debugger - -namespace runtime { -struct CallArgument; -struct CallFrame; -struct CallFunctionOnRequest; -struct CallFunctionOnResponse; -struct CompileScriptRequest; -struct CompileScriptResponse; -struct ConsoleAPICalledNotification; -struct CustomPreview; -struct DisableRequest; -struct DiscardConsoleEntriesRequest; -struct EnableRequest; -struct EntryPreview; -struct EvaluateRequest; -struct EvaluateResponse; -struct ExceptionDetails; -struct ExecutionContextCreatedNotification; -struct ExecutionContextDescription; -using ExecutionContextId = long long; -struct GetHeapUsageRequest; -struct GetHeapUsageResponse; -struct GetPropertiesRequest; -struct GetPropertiesResponse; -struct GlobalLexicalScopeNamesRequest; -struct GlobalLexicalScopeNamesResponse; -struct InspectRequestedNotification; -struct InternalPropertyDescriptor; -struct ObjectPreview; -struct PropertyDescriptor; -struct PropertyPreview; -struct ReleaseObjectGroupRequest; -struct ReleaseObjectRequest; -struct RemoteObject; -using RemoteObjectId = std::string; -struct RunIfWaitingForDebuggerRequest; -using ScriptId = std::string; -struct StackTrace; -using Timestamp = double; -using UnserializableValue = std::string; -} // namespace runtime - -namespace heapProfiler { -struct AddHeapSnapshotChunkNotification; -struct CollectGarbageRequest; -struct GetHeapObjectIdRequest; -struct GetHeapObjectIdResponse; -struct GetObjectByHeapObjectIdRequest; -struct GetObjectByHeapObjectIdResponse; -using HeapSnapshotObjectId = std::string; -struct HeapStatsUpdateNotification; -struct LastSeenObjectIdNotification; -struct ReportHeapSnapshotProgressNotification; -struct SamplingHeapProfile; -struct SamplingHeapProfileNode; -struct SamplingHeapProfileSample; -struct StartSamplingRequest; -struct StartTrackingHeapObjectsRequest; -struct StopSamplingRequest; -struct StopSamplingResponse; -struct StopTrackingHeapObjectsRequest; -struct TakeHeapSnapshotRequest; -} // namespace heapProfiler - -namespace profiler { -struct PositionTickInfo; -struct Profile; -struct ProfileNode; -struct StartRequest; -struct StopRequest; -struct StopResponse; -} // namespace profiler - -/// RequestHandler handles requests via the visitor pattern. -struct RequestHandler { - virtual ~RequestHandler() = default; - - virtual void handle(const UnknownRequest &req) = 0; - virtual void handle(const debugger::DisableRequest &req) = 0; - virtual void handle(const debugger::EnableRequest &req) = 0; - virtual void handle(const debugger::EvaluateOnCallFrameRequest &req) = 0; - virtual void handle(const debugger::PauseRequest &req) = 0; - virtual void handle(const debugger::RemoveBreakpointRequest &req) = 0; - virtual void handle(const debugger::ResumeRequest &req) = 0; - virtual void handle(const debugger::SetBlackboxPatternsRequest &req) = 0; - virtual void handle(const debugger::SetBlackboxedRangesRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointByUrlRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointsActiveRequest &req) = 0; - virtual void handle( - const debugger::SetInstrumentationBreakpointRequest &req) = 0; - virtual void handle(const debugger::SetPauseOnExceptionsRequest &req) = 0; - virtual void handle(const debugger::StepIntoRequest &req) = 0; - virtual void handle(const debugger::StepOutRequest &req) = 0; - virtual void handle(const debugger::StepOverRequest &req) = 0; - virtual void handle(const heapProfiler::CollectGarbageRequest &req) = 0; - virtual void handle(const heapProfiler::GetHeapObjectIdRequest &req) = 0; - virtual void handle( - const heapProfiler::GetObjectByHeapObjectIdRequest &req) = 0; - virtual void handle(const heapProfiler::StartSamplingRequest &req) = 0; - virtual void handle( - const heapProfiler::StartTrackingHeapObjectsRequest &req) = 0; - virtual void handle(const heapProfiler::StopSamplingRequest &req) = 0; - virtual void handle( - const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0; - virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; - virtual void handle(const profiler::StartRequest &req) = 0; - virtual void handle(const profiler::StopRequest &req) = 0; - virtual void handle(const runtime::CallFunctionOnRequest &req) = 0; - virtual void handle(const runtime::CompileScriptRequest &req) = 0; - virtual void handle(const runtime::DisableRequest &req) = 0; - virtual void handle(const runtime::DiscardConsoleEntriesRequest &req) = 0; - virtual void handle(const runtime::EnableRequest &req) = 0; - virtual void handle(const runtime::EvaluateRequest &req) = 0; - virtual void handle(const runtime::GetHeapUsageRequest &req) = 0; - virtual void handle(const runtime::GetPropertiesRequest &req) = 0; - virtual void handle(const runtime::GlobalLexicalScopeNamesRequest &req) = 0; - virtual void handle(const runtime::ReleaseObjectRequest &req) = 0; - virtual void handle(const runtime::ReleaseObjectGroupRequest &req) = 0; - virtual void handle(const runtime::RunIfWaitingForDebuggerRequest &req) = 0; -}; - -/// NoopRequestHandler can be subclassed to only handle some requests. -struct NoopRequestHandler : public RequestHandler { - void handle(const UnknownRequest &req) override {} - void handle(const debugger::DisableRequest &req) override {} - void handle(const debugger::EnableRequest &req) override {} - void handle(const debugger::EvaluateOnCallFrameRequest &req) override {} - void handle(const debugger::PauseRequest &req) override {} - void handle(const debugger::RemoveBreakpointRequest &req) override {} - void handle(const debugger::ResumeRequest &req) override {} - void handle(const debugger::SetBlackboxPatternsRequest &req) override {} - void handle(const debugger::SetBlackboxedRangesRequest &req) override {} - void handle(const debugger::SetBreakpointRequest &req) override {} - void handle(const debugger::SetBreakpointByUrlRequest &req) override {} - void handle(const debugger::SetBreakpointsActiveRequest &req) override {} - void handle( - const debugger::SetInstrumentationBreakpointRequest &req) override {} - void handle(const debugger::SetPauseOnExceptionsRequest &req) override {} - void handle(const debugger::StepIntoRequest &req) override {} - void handle(const debugger::StepOutRequest &req) override {} - void handle(const debugger::StepOverRequest &req) override {} - void handle(const heapProfiler::CollectGarbageRequest &req) override {} - void handle(const heapProfiler::GetHeapObjectIdRequest &req) override {} - void handle( - const heapProfiler::GetObjectByHeapObjectIdRequest &req) override {} - void handle(const heapProfiler::StartSamplingRequest &req) override {} - void handle( - const heapProfiler::StartTrackingHeapObjectsRequest &req) override {} - void handle(const heapProfiler::StopSamplingRequest &req) override {} - void handle( - const heapProfiler::StopTrackingHeapObjectsRequest &req) override {} - void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} - void handle(const profiler::StartRequest &req) override {} - void handle(const profiler::StopRequest &req) override {} - void handle(const runtime::CallFunctionOnRequest &req) override {} - void handle(const runtime::CompileScriptRequest &req) override {} - void handle(const runtime::DisableRequest &req) override {} - void handle(const runtime::DiscardConsoleEntriesRequest &req) override {} - void handle(const runtime::EnableRequest &req) override {} - void handle(const runtime::EvaluateRequest &req) override {} - void handle(const runtime::GetHeapUsageRequest &req) override {} - void handle(const runtime::GetPropertiesRequest &req) override {} - void handle(const runtime::GlobalLexicalScopeNamesRequest &req) override {} - void handle(const runtime::ReleaseObjectRequest &req) override {} - void handle(const runtime::ReleaseObjectGroupRequest &req) override {} - void handle(const runtime::RunIfWaitingForDebuggerRequest &req) override {} -}; - -/// Types -struct debugger::Location : public Serializable { - Location() = default; - Location(Location &&) = default; - Location(const Location &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - Location &operator=(const Location &) = delete; - Location &operator=(Location &&) = default; - - runtime::ScriptId scriptId{}; - long long lineNumber{}; - std::optional columnNumber; -}; - -struct runtime::PropertyPreview : public Serializable { - PropertyPreview() = default; - PropertyPreview(PropertyPreview &&) = default; - PropertyPreview(const PropertyPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - PropertyPreview &operator=(const PropertyPreview &) = delete; - PropertyPreview &operator=(PropertyPreview &&) = default; - - std::string name; - std::string type; - std::optional value; - std::unique_ptr< - runtime::ObjectPreview, - std::function> - valuePreview{nullptr, deleter}; - std::optional subtype; -}; - -struct runtime::EntryPreview : public Serializable { - EntryPreview() = default; - EntryPreview(EntryPreview &&) = default; - EntryPreview(const EntryPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - EntryPreview &operator=(const EntryPreview &) = delete; - EntryPreview &operator=(EntryPreview &&) = default; - - std::unique_ptr< - runtime::ObjectPreview, - std::function> - key{nullptr, deleter}; - std::unique_ptr< - runtime::ObjectPreview, - std::function> - value{nullptr, deleter}; -}; - -struct runtime::ObjectPreview : public Serializable { - ObjectPreview() = default; - ObjectPreview(ObjectPreview &&) = default; - ObjectPreview(const ObjectPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ObjectPreview &operator=(const ObjectPreview &) = delete; - ObjectPreview &operator=(ObjectPreview &&) = default; - - std::string type; - std::optional subtype; - std::optional description; - bool overflow{}; - std::vector properties; - std::optional> entries; -}; - -struct runtime::CustomPreview : public Serializable { - CustomPreview() = default; - CustomPreview(CustomPreview &&) = default; - CustomPreview(const CustomPreview &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CustomPreview &operator=(const CustomPreview &) = delete; - CustomPreview &operator=(CustomPreview &&) = default; - - std::string header; - std::optional bodyGetterId; -}; - -struct runtime::RemoteObject : public Serializable { - RemoteObject() = default; - RemoteObject(RemoteObject &&) = default; - RemoteObject(const RemoteObject &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - RemoteObject &operator=(const RemoteObject &) = delete; - RemoteObject &operator=(RemoteObject &&) = default; - - std::string type; - std::optional subtype; - std::optional className; - std::optional value; - std::optional unserializableValue; - std::optional description; - std::optional objectId; - std::optional preview; - std::optional customPreview; -}; - -struct runtime::CallFrame : public Serializable { - CallFrame() = default; - CallFrame(CallFrame &&) = default; - CallFrame(const CallFrame &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CallFrame &operator=(const CallFrame &) = delete; - CallFrame &operator=(CallFrame &&) = default; - - std::string functionName; - runtime::ScriptId scriptId{}; - std::string url; - long long lineNumber{}; - long long columnNumber{}; -}; - -struct runtime::StackTrace : public Serializable { - StackTrace() = default; - StackTrace(StackTrace &&) = default; - StackTrace(const StackTrace &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - StackTrace &operator=(const StackTrace &) = delete; - StackTrace &operator=(StackTrace &&) = default; - - std::optional description; - std::vector callFrames; - std::unique_ptr parent; -}; - -struct runtime::ExceptionDetails : public Serializable { - ExceptionDetails() = default; - ExceptionDetails(ExceptionDetails &&) = default; - ExceptionDetails(const ExceptionDetails &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ExceptionDetails &operator=(const ExceptionDetails &) = delete; - ExceptionDetails &operator=(ExceptionDetails &&) = default; - - long long exceptionId{}; - std::string text; - long long lineNumber{}; - long long columnNumber{}; - std::optional scriptId; - std::optional url; - std::optional stackTrace; - std::optional exception; - std::optional executionContextId; -}; - -struct debugger::Scope : public Serializable { - Scope() = default; - Scope(Scope &&) = default; - Scope(const Scope &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - Scope &operator=(const Scope &) = delete; - Scope &operator=(Scope &&) = default; - - std::string type; - runtime::RemoteObject object{}; - std::optional name; - std::optional startLocation; - std::optional endLocation; -}; - -struct debugger::CallFrame : public Serializable { - CallFrame() = default; - CallFrame(CallFrame &&) = default; - CallFrame(const CallFrame &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CallFrame &operator=(const CallFrame &) = delete; - CallFrame &operator=(CallFrame &&) = default; - - debugger::CallFrameId callFrameId{}; - std::string functionName; - std::optional functionLocation; - debugger::Location location{}; - std::string url; - std::vector scopeChain; - runtime::RemoteObject thisObj{}; - std::optional returnValue; -}; - -struct debugger::ScriptPosition : public Serializable { - ScriptPosition() = default; - ScriptPosition(ScriptPosition &&) = default; - ScriptPosition(const ScriptPosition &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ScriptPosition &operator=(const ScriptPosition &) = delete; - ScriptPosition &operator=(ScriptPosition &&) = default; - - long long lineNumber{}; - long long columnNumber{}; -}; - -struct heapProfiler::SamplingHeapProfileNode : public Serializable { - SamplingHeapProfileNode() = default; - SamplingHeapProfileNode(SamplingHeapProfileNode &&) = default; - SamplingHeapProfileNode(const SamplingHeapProfileNode &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - SamplingHeapProfileNode &operator=(const SamplingHeapProfileNode &) = delete; - SamplingHeapProfileNode &operator=(SamplingHeapProfileNode &&) = default; - - runtime::CallFrame callFrame{}; - double selfSize{}; - long long id{}; - std::vector children; -}; - -struct heapProfiler::SamplingHeapProfileSample : public Serializable { - SamplingHeapProfileSample() = default; - SamplingHeapProfileSample(SamplingHeapProfileSample &&) = default; - SamplingHeapProfileSample(const SamplingHeapProfileSample &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - SamplingHeapProfileSample &operator=(const SamplingHeapProfileSample &) = - delete; - SamplingHeapProfileSample &operator=(SamplingHeapProfileSample &&) = default; - - double size{}; - long long nodeId{}; - double ordinal{}; -}; - -struct heapProfiler::SamplingHeapProfile : public Serializable { - SamplingHeapProfile() = default; - SamplingHeapProfile(SamplingHeapProfile &&) = default; - SamplingHeapProfile(const SamplingHeapProfile &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - SamplingHeapProfile &operator=(const SamplingHeapProfile &) = delete; - SamplingHeapProfile &operator=(SamplingHeapProfile &&) = default; - - heapProfiler::SamplingHeapProfileNode head{}; - std::vector samples; -}; - -struct profiler::PositionTickInfo : public Serializable { - PositionTickInfo() = default; - PositionTickInfo(PositionTickInfo &&) = default; - PositionTickInfo(const PositionTickInfo &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - PositionTickInfo &operator=(const PositionTickInfo &) = delete; - PositionTickInfo &operator=(PositionTickInfo &&) = default; - - long long line{}; - long long ticks{}; -}; - -struct profiler::ProfileNode : public Serializable { - ProfileNode() = default; - ProfileNode(ProfileNode &&) = default; - ProfileNode(const ProfileNode &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ProfileNode &operator=(const ProfileNode &) = delete; - ProfileNode &operator=(ProfileNode &&) = default; - - long long id{}; - runtime::CallFrame callFrame{}; - std::optional hitCount; - std::optional> children; - std::optional deoptReason; - std::optional> positionTicks; -}; - -struct profiler::Profile : public Serializable { - Profile() = default; - Profile(Profile &&) = default; - Profile(const Profile &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - Profile &operator=(const Profile &) = delete; - Profile &operator=(Profile &&) = default; - - std::vector nodes; - double startTime{}; - double endTime{}; - std::optional> samples; - std::optional> timeDeltas; -}; - -struct runtime::CallArgument : public Serializable { - CallArgument() = default; - CallArgument(CallArgument &&) = default; - CallArgument(const CallArgument &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - CallArgument &operator=(const CallArgument &) = delete; - CallArgument &operator=(CallArgument &&) = default; - - std::optional value; - std::optional unserializableValue; - std::optional objectId; -}; - -struct runtime::ExecutionContextDescription : public Serializable { - ExecutionContextDescription() = default; - ExecutionContextDescription(ExecutionContextDescription &&) = default; - ExecutionContextDescription(const ExecutionContextDescription &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - ExecutionContextDescription &operator=(const ExecutionContextDescription &) = - delete; - ExecutionContextDescription &operator=(ExecutionContextDescription &&) = - default; - - runtime::ExecutionContextId id{}; - std::string origin; - std::string name; - std::optional auxData; -}; - -struct runtime::PropertyDescriptor : public Serializable { - PropertyDescriptor() = default; - PropertyDescriptor(PropertyDescriptor &&) = default; - PropertyDescriptor(const PropertyDescriptor &) = delete; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - PropertyDescriptor &operator=(const PropertyDescriptor &) = delete; - PropertyDescriptor &operator=(PropertyDescriptor &&) = default; - - std::string name; - std::optional value; - std::optional writable; - std::optional get; - std::optional set; - bool configurable{}; - bool enumerable{}; - std::optional wasThrown; - std::optional isOwn; - std::optional symbol; -}; - -struct runtime::InternalPropertyDescriptor : public Serializable { - InternalPropertyDescriptor() = default; - InternalPropertyDescriptor(InternalPropertyDescriptor &&) = default; - InternalPropertyDescriptor(const InternalPropertyDescriptor &) = delete; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - InternalPropertyDescriptor &operator=(const InternalPropertyDescriptor &) = - delete; - InternalPropertyDescriptor &operator=(InternalPropertyDescriptor &&) = - default; - - std::string name; - std::optional value; -}; - -/// Requests -struct UnknownRequest : public Request { - UnknownRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional params; -}; - -struct debugger::DisableRequest : public Request { - DisableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::EnableRequest : public Request { - EnableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::EvaluateOnCallFrameRequest : public Request { - EvaluateOnCallFrameRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - debugger::CallFrameId callFrameId{}; - std::string expression; - std::optional objectGroup; - std::optional includeCommandLineAPI; - std::optional silent; - std::optional returnByValue; - std::optional generatePreview; - std::optional throwOnSideEffect; -}; - -struct debugger::PauseRequest : public Request { - PauseRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::RemoveBreakpointRequest : public Request { - RemoveBreakpointRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - debugger::BreakpointId breakpointId{}; -}; - -struct debugger::ResumeRequest : public Request { - ResumeRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional terminateOnResume; -}; - -struct debugger::SetBlackboxPatternsRequest : public Request { - SetBlackboxPatternsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::vector patterns; - std::optional skipAnonymous; -}; - -struct debugger::SetBlackboxedRangesRequest : public Request { - SetBlackboxedRangesRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - runtime::ScriptId scriptId{}; - std::vector positions; -}; - -struct debugger::SetBreakpointRequest : public Request { - SetBreakpointRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - debugger::Location location{}; - std::optional condition; -}; - -struct debugger::SetBreakpointByUrlRequest : public Request { - SetBreakpointByUrlRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - long long lineNumber{}; - std::optional url; - std::optional urlRegex; - std::optional scriptHash; - std::optional columnNumber; - std::optional condition; -}; - -struct debugger::SetBreakpointsActiveRequest : public Request { - SetBreakpointsActiveRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - bool active{}; -}; - -struct debugger::SetInstrumentationBreakpointRequest : public Request { - SetInstrumentationBreakpointRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string instrumentation; -}; - -struct debugger::SetPauseOnExceptionsRequest : public Request { - SetPauseOnExceptionsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string state; -}; - -struct debugger::StepIntoRequest : public Request { - StepIntoRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::StepOutRequest : public Request { - StepOutRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::StepOverRequest : public Request { - StepOverRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::CollectGarbageRequest : public Request { - CollectGarbageRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::GetHeapObjectIdRequest : public Request { - GetHeapObjectIdRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; -}; - -struct heapProfiler::GetObjectByHeapObjectIdRequest : public Request { - GetObjectByHeapObjectIdRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - heapProfiler::HeapSnapshotObjectId objectId{}; - std::optional objectGroup; -}; - -struct heapProfiler::StartSamplingRequest : public Request { - StartSamplingRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional samplingInterval; - std::optional includeObjectsCollectedByMajorGC; - std::optional includeObjectsCollectedByMinorGC; -}; - -struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { - StartTrackingHeapObjectsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional trackAllocations; -}; - -struct heapProfiler::StopSamplingRequest : public Request { - StopSamplingRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::StopTrackingHeapObjectsRequest : public Request { - StopTrackingHeapObjectsRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional reportProgress; - std::optional treatGlobalObjectsAsRoots; - std::optional captureNumericValue; -}; - -struct heapProfiler::TakeHeapSnapshotRequest : public Request { - TakeHeapSnapshotRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional reportProgress; - std::optional treatGlobalObjectsAsRoots; - std::optional captureNumericValue; -}; - -struct profiler::StartRequest : public Request { - StartRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct profiler::StopRequest : public Request { - StopRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::CallFunctionOnRequest : public Request { - CallFunctionOnRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string functionDeclaration; - std::optional objectId; - std::optional> arguments; - std::optional silent; - std::optional returnByValue; - std::optional generatePreview; - std::optional userGesture; - std::optional awaitPromise; - std::optional executionContextId; - std::optional objectGroup; -}; - -struct runtime::CompileScriptRequest : public Request { - CompileScriptRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string expression; - std::string sourceURL; - bool persistScript{}; - std::optional executionContextId; -}; - -struct runtime::DisableRequest : public Request { - DisableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::DiscardConsoleEntriesRequest : public Request { - DiscardConsoleEntriesRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::EnableRequest : public Request { - EnableRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::EvaluateRequest : public Request { - EvaluateRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string expression; - std::optional objectGroup; - std::optional includeCommandLineAPI; - std::optional silent; - std::optional contextId; - std::optional returnByValue; - std::optional generatePreview; - std::optional userGesture; - std::optional awaitPromise; -}; - -struct runtime::GetHeapUsageRequest : public Request { - GetHeapUsageRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::GetPropertiesRequest : public Request { - GetPropertiesRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; - std::optional ownProperties; - std::optional accessorPropertiesOnly; - std::optional generatePreview; -}; - -struct runtime::GlobalLexicalScopeNamesRequest : public Request { - GlobalLexicalScopeNamesRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::optional executionContextId; -}; - -struct runtime::ReleaseObjectRequest : public Request { - ReleaseObjectRequest(); - static std::unique_ptr tryMake(const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; -}; - -struct runtime::ReleaseObjectGroupRequest : public Request { - ReleaseObjectGroupRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; - - std::string objectGroup; -}; - -struct runtime::RunIfWaitingForDebuggerRequest : public Request { - RunIfWaitingForDebuggerRequest(); - static std::unique_ptr tryMake( - const JSONObject *obj); - - JSONValue *toJsonVal(JSONFactory &factory) const override; - void accept(RequestHandler &handler) const override; -}; - -/// Responses -struct ErrorResponse : public Response { - ErrorResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - long long code; - std::string message; - std::optional data; -}; - -struct OkResponse : public Response { - OkResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; -}; - -struct debugger::EvaluateOnCallFrameResponse : public Response { - EvaluateOnCallFrameResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct debugger::SetBreakpointResponse : public Response { - SetBreakpointResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; - debugger::Location actualLocation{}; -}; - -struct debugger::SetBreakpointByUrlResponse : public Response { - SetBreakpointByUrlResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; - std::vector locations; -}; - -struct debugger::SetInstrumentationBreakpointResponse : public Response { - SetInstrumentationBreakpointResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; -}; - -struct heapProfiler::GetHeapObjectIdResponse : public Response { - GetHeapObjectIdResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - heapProfiler::HeapSnapshotObjectId heapSnapshotObjectId{}; -}; - -struct heapProfiler::GetObjectByHeapObjectIdResponse : public Response { - GetObjectByHeapObjectIdResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; -}; - -struct heapProfiler::StopSamplingResponse : public Response { - StopSamplingResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - heapProfiler::SamplingHeapProfile profile{}; -}; - -struct profiler::StopResponse : public Response { - StopResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - profiler::Profile profile{}; -}; - -struct runtime::CallFunctionOnResponse : public Response { - CallFunctionOnResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct runtime::CompileScriptResponse : public Response { - CompileScriptResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::optional scriptId; - std::optional exceptionDetails; -}; - -struct runtime::EvaluateResponse : public Response { - EvaluateResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct runtime::GetHeapUsageResponse : public Response { - GetHeapUsageResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - double usedSize{}; - double totalSize{}; -}; - -struct runtime::GetPropertiesResponse : public Response { - GetPropertiesResponse() = default; - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector result; - std::optional> - internalProperties; - std::optional exceptionDetails; -}; - -struct runtime::GlobalLexicalScopeNamesResponse : public Response { - GlobalLexicalScopeNamesResponse() = default; - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector names; -}; - -/// Notifications -struct debugger::BreakpointResolvedNotification : public Notification { - BreakpointResolvedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - debugger::BreakpointId breakpointId{}; - debugger::Location location{}; -}; - -struct debugger::PausedNotification : public Notification { - PausedNotification(); - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector callFrames; - std::string reason; - std::optional data; - std::optional> hitBreakpoints; - std::optional asyncStackTrace; -}; - -struct debugger::ResumedNotification : public Notification { - ResumedNotification(); - static std::unique_ptr tryMake(const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; -}; - -struct debugger::ScriptParsedNotification : public Notification { - ScriptParsedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::ScriptId scriptId{}; - std::string url; - long long startLine{}; - long long startColumn{}; - long long endLine{}; - long long endColumn{}; - runtime::ExecutionContextId executionContextId{}; - std::string hash; - std::optional executionContextAuxData; - std::optional sourceMapURL; - std::optional hasSourceURL; - std::optional isModule; - std::optional length; - std::optional scriptLanguage; -}; - -struct heapProfiler::AddHeapSnapshotChunkNotification : public Notification { - AddHeapSnapshotChunkNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::string chunk; -}; - -struct heapProfiler::HeapStatsUpdateNotification : public Notification { - HeapStatsUpdateNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::vector statsUpdate; -}; - -struct heapProfiler::LastSeenObjectIdNotification : public Notification { - LastSeenObjectIdNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - long long lastSeenObjectId{}; - double timestamp{}; -}; - -struct heapProfiler::ReportHeapSnapshotProgressNotification - : public Notification { - ReportHeapSnapshotProgressNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - long long done{}; - long long total{}; - std::optional finished; -}; - -struct runtime::ConsoleAPICalledNotification : public Notification { - ConsoleAPICalledNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - std::string type; - std::vector args; - runtime::ExecutionContextId executionContextId{}; - runtime::Timestamp timestamp{}; - std::optional stackTrace; -}; - -struct runtime::ExecutionContextCreatedNotification : public Notification { - ExecutionContextCreatedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::ExecutionContextDescription context{}; -}; - -struct runtime::InspectRequestedNotification : public Notification { - InspectRequestedNotification(); - static std::unique_ptr tryMake( - const JSONObject *obj); - JSONValue *toJsonVal(JSONFactory &factory) const override; - - runtime::RemoteObject object{}; - JSONBlob hints; - std::optional executionContextId; -}; - -} // namespace message -} // namespace cdp -} // namespace hermes -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageTypesInlines.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageTypesInlines.h deleted file mode 100644 index fe765f93..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/MessageTypesInlines.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_MESSAGETYPESINLINES_H -#define HERMES_CDP_MESSAGETYPESINLINES_H - -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace cdp { -namespace message { - -template -using optional = std::optional; - -template -struct is_vector : std::false_type {}; - -template -struct is_vector> : std::true_type {}; - -/// valueFromJson - -/// Convert JSONValue to a Serializable type. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return T::tryMake(res); -} - -/// Convert JSONValue to a bool. -template -typename std::enable_if::value, std::unique_ptr>::type -valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->getValue()); -} - -/// Convert JSONValue to a long long. -template -typename std::enable_if::value, std::unique_ptr>:: - type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->getValue()); -} - -/// Convert JSONValue to a double. -template -typename std::enable_if::value, std::unique_ptr>:: - type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->getValue()); -} - -/// Convert JSONValue to a string. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(const JSONValue *v) { - auto res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res->c_str()); -} - -/// Convert JSONValue to a vector. -template -typename std::enable_if::value, std::unique_ptr>::type -valueFromJson(const JSONValue *items) { - auto *arr = llvh::dyn_cast(items); - std::unique_ptr result = std::make_unique(); - result->reserve(arr->size()); - for (const auto &item : *arr) { - auto itemResult = valueFromJson(item); - if (!itemResult) { - return nullptr; - } - result->push_back(std::move(*itemResult)); - } - return result; -} - -/// Convert JSONValue to a JSONObject. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(JSONValue *v) { - auto *res = llvh::dyn_cast_or_null(v); - if (!res) { - return nullptr; - } - return std::make_unique(res); -} - -/// Pass through JSONValues. -template -typename std:: - enable_if::value, std::unique_ptr>::type - valueFromJson(JSONValue *v) { - return std::make_unique(v); -} - -/// assign(lhs, obj, key) is a wrapper for: -/// -/// lhs = obj[key] -/// -/// It mainly exists so that we can choose the right version of valueFromJson -/// based on the type of lhs. - -template -bool assign(T &lhs, const JSONObject *obj, const U &key) { - JSONValue *v = obj->get(key); - if (v == nullptr) { - return false; - } - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(*convertResult); - return true; - } - return false; -} - -template -bool assign(optional &lhs, const JSONObject *obj, const U &key) { - JSONValue *v = obj->get(key); - if (v != nullptr) { - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(*convertResult); - return true; - } - return false; - } else { - lhs.reset(); - return true; - } -} - -template -bool assign(std::unique_ptr &lhs, const JSONObject *obj, const U &key) { - JSONValue *v = obj->get(key); - if (v != nullptr) { - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(convertResult); - return true; - } - return false; - } else { - lhs.reset(); - return true; - } -} - -template -bool assign( - std::unique_ptr> &lhs, - const JSONObject *obj, - const U &key) { - JSONValue *v = obj->get(key); - if (v != nullptr) { - auto convertResult = valueFromJson(v); - if (convertResult) { - lhs = std::move(convertResult); - return true; - } - return false; - } else { - lhs.reset(); - return true; - } -} - -/// valueToJson - -inline JSONValue *valueToJson(const Serializable &value, JSONFactory &factory) { - return value.toJsonVal(factory); -} - -// Convert a bool to JSONValue. -inline JSONValue *valueToJson(bool b, JSONFactory &factory) { - return factory.getBoolean(b); -} - -// Convert a long long to JSONValue. -inline JSONValue *valueToJson(long long num, JSONFactory &factory) { - return factory.getNumber(num); -} - -// Convert a double to JSONValue. -inline JSONValue *valueToJson(double num, JSONFactory &factory) { - return factory.getNumber(num); -} - -// Convert a string to JSONValue. -inline JSONValue *valueToJson(const std::string &str, JSONFactory &factory) { - return factory.getString(str); -} - -// Convert a vector to JSONValue. -template -JSONValue *valueToJson(const std::vector &items, JSONFactory &factory) { - llvh::SmallVector storage; - for (const auto &item : items) { - storage.push_back(valueToJson(item, factory)); - } - return factory.newArray(storage.size(), storage.begin(), storage.end()); -} - -// Cast a JSONObject to JSONValue. -inline JSONValue *valueToJson(JSONObject *obj, JSONFactory &factory) { - return llvh::cast(obj); -} - -// Pass through JSONValues. -inline JSONValue *valueToJson(JSONValue *v, JSONFactory &factory) { - return v; -} - -/// put(obj, key, value) is meant to be a wrapper for: -/// obj[key] = valueToJson(value); -/// However, JSONObjects are immutable, so we represent a 'put' operation as -/// pushing a new element onto a vector of JSONFactory::Props. - -using Properties = llvh::SmallVectorImpl; - -template -void put( - Properties &props, - const std::string &key, - const V &value, - JSONFactory &factory) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(value, factory); - props.push_back({jsStr, jsVal}); -} - -template -void put( - Properties &props, - const std::string &key, - const optional &optValue, - JSONFactory &factory) { - if (optValue.has_value()) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(optValue.value(), factory); - props.push_back({jsStr, jsVal}); - } -} - -template -void put( - Properties &props, - const std::string &key, - const std::unique_ptr &ptr, - JSONFactory &factory) { - if (ptr.get()) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(*ptr, factory); - props.push_back({jsStr, jsVal}); - } -} - -template -void put( - Properties &props, - const std::string &key, - const std::unique_ptr> &ptr, - JSONFactory &factory) { - if (ptr.get()) { - JSONString *jsStr = factory.getString(key); - JSONValue *jsVal = valueToJson(*ptr, factory); - props.push_back({jsStr, jsVal}); - } -} - -template -void deleter(T *p) { - delete p; -} - -} // namespace message -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_MESSAGETYPESINLINES_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/ProfilerDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/ProfilerDomainAgent.h deleted file mode 100644 index 6c62b9c8..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/ProfilerDomainAgent.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_PROFILERDOMAINAGENT_H -#define HERMES_CDP_PROFILERDOMAINAGENT_H - -#include -#include - -#include "DomainAgent.h" - -namespace facebook { -namespace hermes { -namespace cdp { - -/// Handler for the "Profiler" domain of CDP. All methods expect to be invoked -/// with exclusive access to the runtime. -class ProfilerDomainAgent : public DomainAgent { - public: - ProfilerDomainAgent( - int32_t executionContextID, - HermesRuntime &runtime, - SynchronizedOutboundCallback messageCallback, - std::shared_ptr objTable); - ~ProfilerDomainAgent() = default; - - void start(const m::profiler::StartRequest &req); - void stop(const m::profiler::StopRequest &req); - - private: - HermesRuntime &runtime_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_PROFILERDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RemoteObjectConverters.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RemoteObjectConverters.h deleted file mode 100644 index ae688884..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RemoteObjectConverters.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_REMOTEOBJECTCONVERTERS_H -#define HERMES_CDP_REMOTEOBJECTCONVERTERS_H - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace cdp { - -struct ObjectSerializationOptions { - bool returnByValue = false; - bool generatePreview = false; -}; - -namespace message { - -namespace debugger { - -CallFrame makeCallFrame( - uint32_t callFrameIndex, - const facebook::hermes::debugger::CallFrameInfo &callFrameInfo, - const facebook::hermes::debugger::LexicalInfo &lexicalInfo, - cdp::RemoteObjectsTable &objTable, - jsi::Runtime &runtime, - const facebook::hermes::debugger::ProgramState &state); - -std::vector makeCallFrames( - const facebook::hermes::debugger::ProgramState &state, - cdp::RemoteObjectsTable &objTable, - jsi::Runtime &runtime); - -} // namespace debugger - -namespace runtime { - -RemoteObject makeRemoteObject( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Value &value, - cdp::RemoteObjectsTable &objTable, - const std::string &objectGroup, - const cdp::ObjectSerializationOptions &serializationOptions); - -RemoteObject makeRemoteObjectForError( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Value &value, - cdp::RemoteObjectsTable &objTable, - const std::string &objectGroup); - -ExceptionDetails makeExceptionDetails( - jsi::Runtime &runtime, - const jsi::JSError &error, - cdp::RemoteObjectsTable &objTable, - const std::string &objectGroup); - -ExceptionDetails makeExceptionDetails(const jsi::JSIException &err); - -ExceptionDetails makeExceptionDetails( - facebook::jsi::Runtime &runtime, - const facebook::hermes::debugger::EvalResult &result, - cdp::RemoteObjectsTable &objTable, - const std::string &objectGroup); - -} // namespace runtime - -} // namespace message -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_REMOTEOBJECTCONVERTERS_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RemoteObjectsTable.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RemoteObjectsTable.h deleted file mode 100644 index 1b8fff5a..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RemoteObjectsTable.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_REMOTEOBJECTSTABLE_H -#define HERMES_CDP_REMOTEOBJECTSTABLE_H - -#include -#include -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace cdp { - -/// Well-known object group names - -/** - * Objects created as a result of the Debugger.paused notification (e.g. scope - * objects) are placed in the "backtrace" object group. This object group is - * cleared when the VM resumes. - */ -extern const char *BacktraceObjectGroup; - -/** - * Objects that are created as a result of a console evaluation are placed in - * the "console" object group. This object group is cleared when the client - * clears the console. - */ -extern const char *ConsoleObjectGroup; - -/** - * RemoteObjectsTable manages the mapping of string object ids to scope metadata - * or actual JSI objects. The debugger vends these ids to the client so that the - * client can perform operations on the ids (e.g. enumerate properties on the - * object backed by the id). See Runtime.RemoteObjectId in the CDT docs for - * more details. - * - * Note that object handles are not ref-counted. Suppose an object foo is mapped - * to object id "objId" and is also in object group "objGroup". Then *either* of - * `releaseObject("objId")` or `releaseObjectGroup("objGroup")` will remove foo - * from the table. This matches the behavior of object groups in CDT. - */ -class RemoteObjectsTable { - public: - RemoteObjectsTable(); - ~RemoteObjectsTable(); - - RemoteObjectsTable(const RemoteObjectsTable &) = delete; - RemoteObjectsTable &operator=(const RemoteObjectsTable &) = delete; - - /** - * addScope adds the provided (frameIndex, scopeIndex) mapping to the table. - * If objectGroup is non-empty, then the scope object is also added to that - * object group for releasing via releaseObjectGroup. Returns an object id. - */ - std::string addScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup); - - /** - * addValue adds the JSI value to the table. If objectGroup is non-empty, then - * the scope object is also added to that object group for releasing via - * releaseObjectGroup. Returns an object id. - */ - std::string addValue( - ::facebook::jsi::Value value, - const std::string &objectGroup); - - /// /param objId The object ID. - /// /return true if object ID represents a scope in the scope chain of a call - /// frame. - bool isScopeId(const std::string &objId) const; - - /** - * Retrieves the (frameIndex, scopeIndex) associated with this object id, or - * nullptr if no mapping exists. The pointer stays valid as long as you only - * call const methods on this class. - */ - const std::pair *getScope(const std::string &objId) const; - - /** - * Retrieves the JSI value associated with this object id, or nullptr if no - * mapping exists. The pointer stays valid as long as you only call const - * methods on this class. - */ - const ::facebook::jsi::Value *getValue(const std::string &objId) const; - - /** - * Retrieves the object group that this object id is in, or empty string if it - * isn't in an object group. The returned pointer is only guaranteed to be - * valid until the next call to this class. - */ - std::string getObjectGroup(const std::string &objId) const; - - /** - * Removes the scope or JSI value backed by the provided object ID from the - * table. \return true if the object was removed, false if it was not found. - */ - bool releaseObject(const std::string &objId); - - /** - * Removes all objects that are part of the provided object group from the - * table. - */ - void releaseObjectGroup(const std::string &objectGroup); - - private: - bool releaseObject(int64_t id); - - int64_t scopeId_ = -1; - int64_t valueId_ = 1; - - std::unordered_map> scopes_; - std::unordered_map values_; - std::unordered_map idToGroup_; - std::unordered_map> groupToIds_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_REMOTEOBJECTSTABLE_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RuntimeDomainAgent.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RuntimeDomainAgent.h deleted file mode 100644 index 9c8142aa..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/cdp/RuntimeDomainAgent.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_CDP_RUNTIMEDOMAINAGENT_H -#define HERMES_CDP_RUNTIMEDOMAINAGENT_H - -#include - -#include "CDPDebugAPI.h" -#include "DomainAgent.h" -#include "RemoteObjectConverters.h" - -namespace facebook { -namespace hermes { -namespace cdp { - -namespace m = ::facebook::hermes::cdp::message; - -/// Handler for the "Runtime" domain of CDP. Accepts CDP requests belonging to -/// the "Runtime" domain from the debug client. Produces CDP responses and -/// events belonging to the "Runtime" domain. All methods expect to be invoked -/// with exclusive access to the runtime. -class RuntimeDomainAgent : public DomainAgent { - public: - RuntimeDomainAgent( - int32_t executionContextID, - HermesRuntime &runtime, - debugger::AsyncDebuggerAPI &asyncDebuggerAPI, - SynchronizedOutboundCallback messageCallback, - std::shared_ptr objTable, - ConsoleMessageStorage &consoleMessageStorage, - ConsoleMessageDispatcher &consoleMessageDispatcher); - ~RuntimeDomainAgent(); - - /// Enables the Runtime domain without processing CDP message or sending a CDP - /// response. It will still send CDP notifications if needed. - void enable(); - /// Handles Runtime.enable request - /// @cdp Runtime.enable If domain is already enabled, will return success. - void enable(const m::runtime::EnableRequest &req); - /// @cdp Runtime.discardConsoleEntries - void discardConsoleEntries( - const m::runtime::DiscardConsoleEntriesRequest &req); - /// Handles Runtime.disable request - /// @cdp Runtime.disable If domain is already disabled, will return success. - void disable(const m::runtime::DisableRequest &req); - /// Handles Runtime.getHeapUsage request - /// @cdp Runtime.getHeapUsage Allowed even if domain is not enabled. - void getHeapUsage(const m::runtime::GetHeapUsageRequest &req); - /// Handles Runtime.globalLexicalScopeNames request - /// @cdp Runtime.globalLexicalScopeNames Allowed even if domain is not - /// enabled. - void globalLexicalScopeNames( - const m::runtime::GlobalLexicalScopeNamesRequest &req); - /// Handles Runtime.compileScript request - /// @cdp Runtime.compileScript Not allowed if domain is not enabled. - void compileScript(const m::runtime::CompileScriptRequest &req); - /// Handles Runtime.getProperties request - /// @cdp Runtime.getProperties Allowed even if domain is not enabled. - void getProperties(const m::runtime::GetPropertiesRequest &req); - /// Handles Runtime.evaluate request - /// @cdp Runtime.evaluate Allowed even if domain is not enabled. - void evaluate(const m::runtime::EvaluateRequest &req); - /// Handles Runtime.callFunctionOn request - /// @cdp Runtime.callFunctionOn Allowed even if domain is not enabled. - void callFunctionOn(const m::runtime::CallFunctionOnRequest &req); - /// Dispatches a Runtime.consoleAPICalled notification - void consoleAPICalled(const ConsoleMessage &message, bool isBuffered); - /// Handles Runtime.releaseObject request - /// @cdp Runtime.releaseObject Allowed even if domain is not enabled. - void releaseObject(const m::runtime::ReleaseObjectRequest &req); - /// Handles Runtime.releaseObjectGroup request - /// @cdp Runtime.releaseObjectGroup Allowed even if domain is not enabled. - void releaseObjectGroup(const m::runtime::ReleaseObjectGroupRequest &req); - - private: - struct Helpers { - jsi::Function objectGetOwnPropertySymbols; - jsi::Function objectGetOwnPropertyNames; - jsi::Function objectGetOwnPropertyDescriptor; - jsi::Function objectGetPrototypeOf; - - explicit Helpers(jsi::Runtime &runtime); - }; - - bool checkRuntimeEnabled(const m::Request &req); - - /// Ensure the provided \p executionContextId matches the one - /// indicated via the constructor. Returns true if they match. - /// Sends an error message with the specified \p commandId - /// and returns false otherwise. - bool validateExecutionContextId( - m::runtime::ExecutionContextId executionContextId, - long long commandId); - - std::optional> makePropsFromScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup, - const debugger::ProgramState &state, - const ObjectSerializationOptions &serializationOptions); - std::vector makePropsFromValue( - const jsi::Value &value, - const std::string &objectGroup, - bool onlyOwnProperties, - bool accessorPropertiesOnly, - const ObjectSerializationOptions &serializationOptions); - std::vector - makeInternalPropsFromValue( - const jsi::Value &value, - const std::string &objectGroup, - const ObjectSerializationOptions &serializationOptions); - - HermesRuntime &runtime_; - debugger::AsyncDebuggerAPI &asyncDebuggerAPI_; - ConsoleMessageStorage &consoleMessageStorage_; - ConsoleMessageDispatcher &consoleMessageDispatcher_; - - /// Whether Runtime.enable was received and wasn't disabled by receiving - /// Runtime.disable - bool enabled_; - - // preparedScripts_ stores user-entered scripts that have been prepared for - // execution, and may be invoked by a later command. - std::vector> preparedScripts_; - - /// Console message subscription token, used to unsubscribe during shutdown. - ConsoleMessageRegistration consoleMessageRegistration_; - - /// Cached helper JS functions used by agent methods. - const Helpers helpers_; -}; - -} // namespace cdp -} // namespace hermes -} // namespace facebook - -#endif // HERMES_CDP_RUNTIMEDOMAINAGENT_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes.h deleted file mode 100644 index afae8777..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_HERMES_H -#define HERMES_HERMES_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct HermesTestHelper; -struct SHUnit; -struct SHRuntime; - -namespace hermes { -namespace vm { -class GCExecTrace; -class Runtime; -} // namespace vm -} // namespace hermes - -namespace facebook { -namespace jsi { - -class ThreadSafeRuntime; - -} - -namespace hermes { - -namespace debugger { -class Debugger; -} - -class HermesRuntime; -/// The Hermes Root API interface. This is the entry point to create the Hermes -/// runtime and to access Hermes-specific methods that do not rely on a runtime -/// instance. -class HERMES_EXPORT IHermesRootAPI : public jsi::ICast { - public: - static constexpr jsi::UUID uuid{ - 0xb654d898, - 0xdfad, - 0x11ef, - 0x859a, - 0x325096b39f47}; - - // Returns an instance of Hermes Runtime. - virtual std::unique_ptr makeHermesRuntime( - const ::hermes::vm::RuntimeConfig &runtimeConfig) = 0; - - virtual bool isHermesBytecode(const uint8_t *data, size_t len) = 0; - - // Returns the supported bytecode version. - virtual uint32_t getBytecodeVersion() = 0; - - // (EXPERIMENTAL) Issues madvise calls for portions of the given - // bytecode file that will likely be used when loading the bytecode - // file and running its global function. - virtual void prefetchHermesBytecode(const uint8_t *data, size_t len) = 0; - - // Returns whether the data is valid HBC with more extensive checks than - // isHermesBytecode and returns why it isn't in errorMessage (if nonnull) - // if not. - virtual bool hermesBytecodeSanityCheck( - const uint8_t *data, - size_t len, - std::string *errorMessage = nullptr) = 0; - - /// Sets a global fatal handler that is shared across all active Hermes - /// runtimes. Setting fatal handler in multiple places will override the - /// previous fatal handler set by this functionality. - /// The fatal handler must not throw exceptions, as Hermes is compiled without - /// exceptions. - virtual void setFatalHandler(void (*handler)(const std::string &)) = 0; - - // Assuming that \p data is valid HBC bytecode data, returns a pointer to the - // first element of the epilogue, data append to the end of the bytecode - // stream. Return pair contain ptr to data and header. - virtual std::pair getBytecodeEpilogue( - const uint8_t *data, - size_t len) = 0; - - /// Enable sampling profiler. - /// Starts a separate thread that polls VM state with \p meanHzFreq frequency. - /// Any subsequent call to \c enableSamplingProfiler() is ignored until - /// next call to \c disableSamplingProfiler() - virtual void enableSamplingProfiler(double meanHzFreq = 100) = 0; - - /// Disable the sampling profiler - virtual void disableSamplingProfiler() = 0; - - /// Dump sampled stack trace to the given file name. - virtual void dumpSampledTraceToFile(const std::string &fileName) = 0; - - /// Dump sampled stack trace to the given stream. - virtual void dumpSampledTraceToStream(std::ostream &stream) = 0; - - /// Return the executed JavaScript function info. - /// This information holds the segmentID, Virtualoffset and sourceURL. - /// This information is needed specifically to be able to symbolicate non-CJS - /// bundles correctly. This API will be simplified later to simply return a - /// segmentID and virtualOffset, when we are able to only support CJS bundles. - virtual std::unordered_map> - getExecutedFunctions() = 0; - - /// \return whether code coverage profiler is enabled or not. - virtual bool isCodeCoverageProfilerEnabled() = 0; - - /// Enable code coverage profiler. - virtual void enableCodeCoverageProfiler() = 0; - - /// Disable code coverage profiler. - virtual void disableCodeCoverageProfiler() = 0; - - protected: - /// The destructor is protected as delete calls on interfaces must not occur. - /// It is also non-virtual to simplify the v-table. - ~IHermesRootAPI() {} -}; - -/// The setFatalHandler functionality has global effects, which may cause -/// unintended or surprising behavior for users of this API. For this reason, it -/// is not recommended and the functionality is provided by the optional -/// interface ISetFatalHandler. -class HERMES_EXPORT ISetFatalHandler : public jsi::ICast { - public: - static constexpr jsi::UUID uuid{ - 0xda98a610, - 0x09cb, - 0x11f0, - 0x87bf, - 0x325096b39f47}; - /// Sets a global fatal handler that is shared across all active Hermes - /// runtimes. Setting fatal handler in multiple places will override the - /// previous fatal handler set by this functionality. - /// The fatal handler must not throw exceptions, as Hermes is compiled without - /// exceptions. - virtual void setFatalHandler(void (*handler)(const std::string &)) = 0; - - protected: - ~ISetFatalHandler() = default; -}; - -/// Interface for methods that are exposed for test purposes. -class HERMES_EXPORT IHermesTestHelpers : public jsi::ICast { - public: - static constexpr jsi::UUID uuid{ - 0x664e489a, - 0xf941, - 0x11ef, - 0xa44c, - 0x325096b39f47}; - - virtual size_t rootsListLengthForTests() const = 0; - - protected: - ~IHermesTestHelpers() = default; -}; - -class HermesRuntime : public jsi::Runtime, - public IHermes, - public IHermesSHUnit { - public: - /// Similar to jsi::Runtime, HermesRuntime is treated as an object, rather - /// than a pure interface. This is to prevent breaking usages of - /// HermesRuntime prior to the introduction of jsi::IRuntime, IHermes, and - /// other interfaces. - ~HermesRuntime() override = default; - - using jsi::Runtime::castInterface; -}; - -/// Returns a pointer to an object that can be cast into IHermesRootAPI, which -/// can be used to create a Hermes runtime and to access global Hermes-specific -/// methods. This object has static lifetime. -HERMES_EXPORT jsi::ICast *makeHermesRootAPI(); - -/// Return a RuntimeConfig that is more suited for running untrusted JS than -/// the default config. Disables some language features and may trade off some -/// performance for security. -/// -/// Can serve as a starting point with tweaks to re-enable needed features: -/// auto conf = hardenedHermesRuntimeConfig().rebuild(); -/// conf.withArrayBuffer(true); -/// ... -/// auto runtime = makeHermesRuntime(conf.build()); -HERMES_EXPORT ::hermes::vm::RuntimeConfig hardenedHermesRuntimeConfig(); - -HERMES_EXPORT std::unique_ptr makeHermesRuntime( - const ::hermes::vm::RuntimeConfig &runtimeConfig = - ::hermes::vm::RuntimeConfig()); - -/// Create a HermesRuntime for the given config without throwing any exceptions. -/// This is safe to be called from code that is compiled without exceptions. -/// Returns nullptr on failure. -HERMES_EXPORT std::unique_ptr makeHermesRuntimeNoThrow( - const ::hermes::vm::RuntimeConfig &runtimeConfig = - ::hermes::vm::RuntimeConfig()) noexcept; - -HERMES_EXPORT std::unique_ptr -makeThreadSafeHermesRuntime( - const ::hermes::vm::RuntimeConfig &runtimeConfig = - ::hermes::vm::RuntimeConfig()); -} // namespace hermes -} // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes_node_api.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes_node_api.h deleted file mode 100644 index f0a08c95..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes_node_api.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT license. - */ - -#ifndef HERMES_NODE_API_H -#define HERMES_NODE_API_H - -#include -#include -// #include "hermes/VM/RuntimeModule.h" -#include "js_native_api.h" - -NAPI_EXTERN napi_status NAPI_CDECL napi_run_bytecode(napi_env env, void* data, size_t size, const char* source_url, napi_value *result); - -namespace hermes::node_api { - -class NodeApiEnvironment; - -// A task to execute by TaskRunner. -class Task { - public: - virtual ~Task() = default; - virtual void invoke() noexcept = 0; -}; - -// The TaskRunner interface to schedule tasks in JavaScript thread. -class TaskRunner { - public: - virtual ~TaskRunner() = default; - virtual void post(std::unique_ptr task) noexcept = 0; -}; - -// Get or create a Node API environment associated with the given Hermes -// runtime. The Node API environment is deleted by the runtime destructor. -// HERMES_EXPORT vm::CallResult getOrCreateNodeApiEnvironment( -// vm::Runtime &runtime, -// hbc::CompileFlags compileFlags, -// std::shared_ptr taskRunner, -// const std::function &unhandledErrorCallback, -// int32_t apiVersion) noexcept; - -napi_env createNodeApiEnv( - void* vmRuntime, - std::shared_ptr<::hermes::node_api::TaskRunner> taskRunner, - const std::function &unhandledErrorCallback, - int32_t NODE_API_VERSION -) noexcept; - -// // Initialize new Node API module in a new Node API environment. -// napi_status initializeNodeApiModule( -// vm::Runtime &runtime, -// napi_addon_register_func registerModule, -// int32_t apiVersion, -// napi_value *exports) noexcept; - -// napi_status setNodeApiEnvironmentData( -// napi_env env, -// const napi_type_tag &tag, -// void *data) noexcept; - -// napi_status getNodeApiEnvironmentData( -// napi_env env, -// const napi_type_tag &tag, -// void **data) noexcept; - -// // TODO: can we remove it? -// napi_status checkNodeApiPreconditions(napi_env env) noexcept; - -// // TODO: can we remove it? -// napi_status setNodeApiValue( -// napi_env env, -// ::hermes::vm::CallResult<::hermes::vm::HermesValue> hvResult, -// napi_value *result); - -// // TODO: can we remove it? -// napi_status checkJSErrorStatus( -// napi_env env, -// vm::ExecutionStatus hermesStatus) noexcept; - -// // TODO: remove it -// napi_status queueMicrotask(napi_env env, napi_value callback) noexcept; - -// using nodeApiCallback = hermes::vm::CallResult(void *); - -// napi_status runInNodeApiContext( -// napi_env env, -// nodeApiCallback callback, -// void *data, -// napi_value *result) noexcept; - -// template -// napi_status runInNodeApiContext( -// napi_env env, -// TCallback &&callback, -// napi_value *result) noexcept { -// return runInNodeApiContext( -// env, -// [](void *data) -> ::hermes::vm::CallResult { -// std::remove_reference_t *cb = -// reinterpret_cast *>(data); -// return (*cb)(); -// }, -// &callback, -// result); -// } - -// // TODO: can we remove it? -// template -// napi_status setLastNativeError( -// napi_env env, -// napi_status status, -// const char *fileName, -// uint32_t line, -// TArgs &&...args) noexcept { -// std::ostringstream sb; -// (void)(sb << ... << args); -// const std::string message = sb.str(); -// return setLastNativeError(env, status, fileName, line, message); -// } - -// // TODO: can we remove it? -// template <> -// napi_status setLastNativeError( -// napi_env env, -// napi_status status, -// const char *fileName, -// uint32_t line, -// const std::string &message) noexcept; - -// // TODO: can we remove it? -// napi_status clearLastNativeError(napi_env env) noexcept; - -// // TODO: can we replace it with something else? -// napi_status openNodeApiScope(napi_env env, void **scope) noexcept; - -// // TODO: can we replace it with something else? -// napi_status closeNodeApiScope(napi_env env, void *scope) noexcept; - -} // namespace hermes::node_api - -#endif // HERMES_NODE_API_H diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes_tracing.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes_tracing.h deleted file mode 100644 index bb33b357..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/hermes/hermes_tracing.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifndef HERMES_HERMES_TRACING_H -#define HERMES_HERMES_TRACING_H - -#include - -namespace llvh { -class raw_ostream; -} // namespace llvh - -namespace facebook { -namespace hermes { - -/// Creates and returns a tracing runtime if \p runtimeConfig.SynthTraceMode is -/// either SynthTraceMode::Tracing or SynthTraceMode::TracingAndReplaying. -/// Otherwise, returns the passed \n hermesRuntime as is. -/// The trace will be written to \p traceScratchPath incrementally. -/// On completion, the file will be renamed to \p traceResultPath, and -/// \p traceCompletionCallback (for post-processing) will be invoked. -/// Completion can be triggered implicitly by crash (if crash manager is -/// provided) or explicitly by invocation of flush. -/// If the runtime is destructed without triggering trace completion, -/// the file at \p traceScratchPath will be deleted. -/// The return value of \p traceCompletionCallback indicates whether the -/// invocation completed successfully. If \p traceCompletionCallback is null, it -/// also assumes as if the callback is successful. -std::shared_ptr makeTracingHermesRuntime( - std::shared_ptr hermesRuntime, - const ::hermes::vm::RuntimeConfig &runtimeConfig, - const std::string &traceScratchPath, - const std::string &traceResultPath, - std::function traceCompletionCallback); - -/// Creates and returns a tracing runtime that wrapps the passed -/// \p hermesRuntime. This API is mainly for Synth Trace replay (and tracing), -/// and for testing. -/// \p traceStream the stream to write trace to. -/// \p forReplay indicates whether the runtime is being used in trace replay and -/// tracing. -std::shared_ptr makeTracingHermesRuntime( - std::shared_ptr hermesRuntime, - const ::hermes::vm::RuntimeConfig &runtimeConfig, - std::unique_ptr traceStream, - bool forReplay = false); - -} // namespace hermes -} // namespace facebook - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/JSIDynamic.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/JSIDynamic.h deleted file mode 100644 index d022b639..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/JSIDynamic.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -namespace facebook { -namespace jsi { - -facebook::jsi::Value valueFromDynamic( - facebook::jsi::Runtime& runtime, - const folly::dynamic& dyn); - -folly::dynamic dynamicFromValue( - facebook::jsi::Runtime& runtime, - const facebook::jsi::Value& value, - const std::function& filterObjectKeys = nullptr); - -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/decorator.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/decorator.h deleted file mode 100644 index 7e46db66..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/decorator.h +++ /dev/null @@ -1,1064 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include - -// This file contains objects to help API users create their own -// runtime adapters, i.e. if you want to compose runtimes to add your -// own behavior. - -namespace facebook { -namespace jsi { - -// Use this to wrap host functions. It will pass the member runtime as -// the first arg to the callback. The first argument to the ctor -// should be the decorated runtime, not the plain one. -class DecoratedHostFunction { - public: - DecoratedHostFunction(Runtime& drt, HostFunctionType plainHF) - : drt_(drt), plainHF_(std::move(plainHF)) {} - - Runtime& decoratedRuntime() { - return drt_; - } - - Value - operator()(Runtime&, const Value& thisVal, const Value* args, size_t count) { - return plainHF_(decoratedRuntime(), thisVal, args, count); - } - - private: - template - friend class RuntimeDecorator; - - Runtime& drt_; - HostFunctionType plainHF_; -}; - -// From the perspective of the caller, a plain HostObject is passed to -// the decorated Runtime, and the HostObject methods expect to get -// passed that Runtime. But the plain Runtime will pass itself to its -// callback, so we need a helper here which curries the decorated -// Runtime, and calls the plain HostObject with it. -// -// If the concrete RuntimeDecorator derives DecoratedHostObject, it -// should call the base class get() and set() to invoke the plain -// HostObject functionality. The Runtime& it passes does not matter, -// as it is not used. -class DecoratedHostObject : public HostObject { - public: - DecoratedHostObject(Runtime& drt, std::shared_ptr plainHO) - : drt_(drt), plainHO_(plainHO) {} - - // The derived class methods can call this to get a reference to the - // decorated runtime, since the rt passed to the callback will be - // the plain runtime. - Runtime& decoratedRuntime() { - return drt_; - } - - Value get(Runtime&, const PropNameID& name) override { - return plainHO_->get(decoratedRuntime(), name); - } - - void set(Runtime&, const PropNameID& name, const Value& value) override { - plainHO_->set(decoratedRuntime(), name, value); - } - - std::vector getPropertyNames(Runtime&) override { - return plainHO_->getPropertyNames(decoratedRuntime()); - } - - private: - template - friend class RuntimeDecorator; - - Runtime& drt_; - std::shared_ptr plainHO_; -}; - -/// C++ variant on a standard Decorator pattern, using template -/// parameters. The \c Plain template parameter type is the -/// undecorated Runtime type. You can usually use \c Runtime here, -/// but if you know the concrete type ahead of time and it's final, -/// the compiler can devirtualize calls to the decorated -/// implementation. The \c Base template parameter type will be used -/// as the base class of the decorated type. Here, too, you can -/// usually use \c Runtime, but if you want the decorated type to -/// implement a derived class of Runtime, you can specify that here. -/// For an example, see threadsafe.h. -template -class RuntimeDecorator : public Base, private jsi::Instrumentation { - public: - Plain& plain() { - static_assert( - std::is_base_of::value, - "RuntimeDecorator's Plain type must derive from jsi::Runtime"); - static_assert( - std::is_base_of::value, - "RuntimeDecorator's Base type must derive from jsi::Runtime"); - return plain_; - } - const Plain& plain() const { - return plain_; - } - - ICast* castInterface(const UUID& interfaceUUID) override { - return plain().castInterface(interfaceUUID); - } - - Value evaluateJavaScript( - const std::shared_ptr& buffer, - const std::string& sourceURL) override { - return plain().evaluateJavaScript(buffer, sourceURL); - } - std::shared_ptr prepareJavaScript( - const std::shared_ptr& buffer, - std::string sourceURL) override { - return plain().prepareJavaScript(buffer, std::move(sourceURL)); - } - Value evaluatePreparedJavaScript( - const std::shared_ptr& js) override { - return plain().evaluatePreparedJavaScript(js); - } - void queueMicrotask(const jsi::Function& callback) override { - return plain().queueMicrotask(callback); - } - bool drainMicrotasks(int maxMicrotasksHint) override { - return plain().drainMicrotasks(maxMicrotasksHint); - } - Object global() override { - return plain().global(); - } - std::string description() override { - return plain().description(); - } - bool isInspectable() override { - return plain().isInspectable(); - } - Instrumentation& instrumentation() override { - return *this; - } - - protected: - // plain is generally going to be a reference to an object managed - // by a derived class. We cache it here so this class can be - // concrete, and avoid making virtual calls to find the plain - // Runtime. Note that the ctor and dtor do not access through the - // reference, so passing a reference to an object before its - // lifetime has started is ok. - RuntimeDecorator(Plain& plain) : plain_(plain) {} - - Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { - return plain_.cloneSymbol(pv); - } - Runtime::PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override { - return plain_.cloneBigInt(pv); - } - Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { - return plain_.cloneString(pv); - } - Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { - return plain_.cloneObject(pv); - } - Runtime::PointerValue* clonePropNameID( - const Runtime::PointerValue* pv) override { - return plain_.clonePropNameID(pv); - } - - PropNameID createPropNameIDFromAscii(const char* str, size_t length) - override { - return plain_.createPropNameIDFromAscii(str, length); - } - PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) - override { - return plain_.createPropNameIDFromUtf8(utf8, length); - } - PropNameID createPropNameIDFromString(const String& str) override { - return plain_.createPropNameIDFromString(str); - } - PropNameID createPropNameIDFromUtf16(const char16_t* utf16, size_t length) - override { - return plain_.createPropNameIDFromUtf16(utf16, length); - } - PropNameID createPropNameIDFromSymbol(const Symbol& sym) override { - return plain_.createPropNameIDFromSymbol(sym); - } - std::string utf8(const PropNameID& id) override { - return plain_.utf8(id); - } - bool compare(const PropNameID& a, const PropNameID& b) override { - return plain_.compare(a, b); - } - - std::string symbolToString(const Symbol& sym) override { - return plain_.symbolToString(sym); - } - - BigInt createBigIntFromInt64(int64_t value) override { - return plain_.createBigIntFromInt64(value); - } - BigInt createBigIntFromUint64(uint64_t value) override { - return plain_.createBigIntFromUint64(value); - } - bool bigintIsInt64(const BigInt& b) override { - return plain_.bigintIsInt64(b); - } - bool bigintIsUint64(const BigInt& b) override { - return plain_.bigintIsUint64(b); - } - uint64_t truncate(const BigInt& b) override { - return plain_.truncate(b); - } - String bigintToString(const BigInt& bigint, int radix) override { - return plain_.bigintToString(bigint, radix); - } - - String createStringFromAscii(const char* str, size_t length) override { - return plain_.createStringFromAscii(str, length); - } - String createStringFromUtf8(const uint8_t* utf8, size_t length) override { - return plain_.createStringFromUtf8(utf8, length); - } - String createStringFromUtf16(const char16_t* utf16, size_t length) override { - return plain_.createStringFromUtf16(utf16, length); - } - std::string utf8(const String& s) override { - return plain_.utf8(s); - } - - std::u16string utf16(const String& str) override { - return plain_.utf16(str); - } - std::u16string utf16(const PropNameID& sym) override { - return plain_.utf16(sym); - } - - void getStringData( - const jsi::String& str, - void* ctx, - void ( - *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { - plain_.getStringData(str, ctx, cb); - } - - void getPropNameIdData( - const jsi::PropNameID& sym, - void* ctx, - void ( - *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { - plain_.getPropNameIdData(sym, ctx, cb); - } - - Object createObjectWithPrototype(const Value& prototype) override { - return plain_.createObjectWithPrototype(prototype); - } - - Object createObject() override { - return plain_.createObject(); - } - - Object createObject(std::shared_ptr ho) override { - return plain_.createObject( - std::make_shared(*this, std::move(ho))); - } - std::shared_ptr getHostObject(const jsi::Object& o) override { - std::shared_ptr dho = plain_.getHostObject(o); - return static_cast(*dho).plainHO_; - } - HostFunctionType& getHostFunction(const jsi::Function& f) override { - HostFunctionType& dhf = plain_.getHostFunction(f); - // This will fail if a cpp file including this header is not compiled - // with RTTI. - return dhf.target()->plainHF_; - } - - bool hasNativeState(const Object& o) override { - return plain_.hasNativeState(o); - } - std::shared_ptr getNativeState(const Object& o) override { - return plain_.getNativeState(o); - } - void setNativeState(const Object& o, std::shared_ptr state) - override { - plain_.setNativeState(o, state); - } - - void setExternalMemoryPressure(const Object& obj, size_t amt) override { - plain_.setExternalMemoryPressure(obj, amt); - } - - void setPrototypeOf(const Object& object, const Value& prototype) override { - plain_.setPrototypeOf(object, prototype); - } - - Value getPrototypeOf(const Object& object) override { - return plain_.getPrototypeOf(object); - } - - Value getProperty(const Object& o, const PropNameID& name) override { - return plain_.getProperty(o, name); - } - Value getProperty(const Object& o, const String& name) override { - return plain_.getProperty(o, name); - } - Value getProperty(const Object& o, const Value& name) override { - return plain_.getProperty(o, name); - } - bool hasProperty(const Object& o, const PropNameID& name) override { - return plain_.hasProperty(o, name); - } - bool hasProperty(const Object& o, const String& name) override { - return plain_.hasProperty(o, name); - } - bool hasProperty(const Object& o, const Value& name) override { - return plain_.hasProperty(o, name); - } - void setPropertyValue( - const Object& o, - const PropNameID& name, - const Value& value) override { - plain_.setPropertyValue(o, name, value); - } - void setPropertyValue(const Object& o, const String& name, const Value& value) - override { - plain_.setPropertyValue(o, name, value); - } - void setPropertyValue(const Object& o, const Value& name, const Value& value) - override { - plain_.setPropertyValue(o, name, value); - } - - void deleteProperty(const Object& object, const PropNameID& name) override { - plain_.deleteProperty(object, name); - } - - void deleteProperty(const Object& object, const String& name) override { - plain_.deleteProperty(object, name); - } - - void deleteProperty(const Object& object, const Value& name) override { - plain_.deleteProperty(object, name); - } - - bool isArray(const Object& o) const override { - return plain_.isArray(o); - } - bool isArrayBuffer(const Object& o) const override { - return plain_.isArrayBuffer(o); - } - bool isFunction(const Object& o) const override { - return plain_.isFunction(o); - } - bool isHostObject(const jsi::Object& o) const override { - return plain_.isHostObject(o); - } - bool isHostFunction(const jsi::Function& f) const override { - return plain_.isHostFunction(f); - } - Array getPropertyNames(const Object& o) override { - return plain_.getPropertyNames(o); - } - - WeakObject createWeakObject(const Object& o) override { - return plain_.createWeakObject(o); - } - Value lockWeakObject(const WeakObject& wo) override { - return plain_.lockWeakObject(wo); - } - - Array createArray(size_t length) override { - return plain_.createArray(length); - } - ArrayBuffer createArrayBuffer( - std::shared_ptr buffer) override { - return plain_.createArrayBuffer(std::move(buffer)); - } - size_t size(const Array& a) override { - return plain_.size(a); - } - size_t size(const ArrayBuffer& ab) override { - return plain_.size(ab); - } - uint8_t* data(const ArrayBuffer& ab) override { - return plain_.data(ab); - } - Value getValueAtIndex(const Array& a, size_t i) override { - return plain_.getValueAtIndex(a, i); - } - void setValueAtIndexImpl(const Array& a, size_t i, const Value& value) - override { - plain_.setValueAtIndexImpl(a, i, value); - } - - Function createFunctionFromHostFunction( - const PropNameID& name, - unsigned int paramCount, - HostFunctionType func) override { - return plain_.createFunctionFromHostFunction( - name, paramCount, DecoratedHostFunction(*this, std::move(func))); - } - Value call( - const Function& f, - const Value& jsThis, - const Value* args, - size_t count) override { - return plain_.call(f, jsThis, args, count); - } - Value callAsConstructor(const Function& f, const Value* args, size_t count) - override { - return plain_.callAsConstructor(f, args, count); - } - - void setRuntimeDataImpl( - const UUID& uuid, - const void* data, - void (*deleter)(const void* data)) override { - return plain_.setRuntimeDataImpl(uuid, data, deleter); - } - - const void* getRuntimeDataImpl(const UUID& uuid) override { - return plain_.getRuntimeDataImpl(uuid); - } - - // Private data for managing scopes. - Runtime::ScopeState* pushScope() override { - return plain_.pushScope(); - } - void popScope(Runtime::ScopeState* ss) override { - plain_.popScope(ss); - } - - bool strictEquals(const Symbol& a, const Symbol& b) const override { - return plain_.strictEquals(a, b); - } - bool strictEquals(const BigInt& a, const BigInt& b) const override { - return plain_.strictEquals(a, b); - } - bool strictEquals(const String& a, const String& b) const override { - return plain_.strictEquals(a, b); - } - bool strictEquals(const Object& a, const Object& b) const override { - return plain_.strictEquals(a, b); - } - - bool instanceOf(const Object& o, const Function& f) override { - return plain_.instanceOf(o, f); - } - - // jsi::Instrumentation methods - - std::string getRecordedGCStats() override { - return plain().instrumentation().getRecordedGCStats(); - } - - std::unordered_map getHeapInfo( - bool includeExpensive) override { - return plain().instrumentation().getHeapInfo(includeExpensive); - } - - void collectGarbage(std::string cause) override { - plain().instrumentation().collectGarbage(std::move(cause)); - } - - void startTrackingHeapObjectStackTraces( - std::function)> callback) override { - plain().instrumentation().startTrackingHeapObjectStackTraces( - std::move(callback)); - } - - void stopTrackingHeapObjectStackTraces() override { - plain().instrumentation().stopTrackingHeapObjectStackTraces(); - } - - void startHeapSampling(size_t samplingInterval) override { - plain().instrumentation().startHeapSampling(samplingInterval); - } - - void stopHeapSampling(std::ostream& os) override { - plain().instrumentation().stopHeapSampling(os); - } - - void createSnapshotToFile( - const std::string& path, - const HeapSnapshotOptions& options) override { - plain().instrumentation().createSnapshotToFile(path, options); - } - - void createSnapshotToStream( - std::ostream& os, - const HeapSnapshotOptions& options) override { - plain().instrumentation().createSnapshotToStream(os, options); - } - - std::string flushAndDisableBridgeTrafficTrace() override { - return const_cast(plain()) - .instrumentation() - .flushAndDisableBridgeTrafficTrace(); - } - - void writeBasicBlockProfileTraceToFile( - const std::string& fileName) const override { - const_cast(plain()) - .instrumentation() - .writeBasicBlockProfileTraceToFile(fileName); - } - - void dumpOpcodeStats(std::ostream& os) const override { - const_cast(plain()).instrumentation().dumpOpcodeStats(os); - } - - /// Dump external profiler symbols to the given file name. - void dumpProfilerSymbolsToFile(const std::string& fileName) const override { - const_cast(plain()).instrumentation().dumpProfilerSymbolsToFile( - fileName); - } - - private: - Plain& plain_; -}; - -namespace detail { - -// This metaprogramming allows the With type's methods to be -// optional. - -template -struct BeforeCaller { - static void before(T&) {} -}; - -template -struct AfterCaller { - static void after(T&) {} -}; - -// decltype((void)&...) is either SFINAE, or void. -// So, if SFINAE does not happen for T, then this specialization exists -// for BeforeCaller, and always applies. If not, only the -// default above exists, and that is used instead. -template -struct BeforeCaller { - static void before(T& t) { - t.before(); - } -}; - -template -struct AfterCaller { - static void after(T& t) { - t.after(); - } -}; - -// It's possible to use multiple decorators by nesting -// WithRuntimeDecorator<...>, but this specialization allows use of -// std::tuple of decorator classes instead. See testlib.cpp for an -// example. -template -struct BeforeCaller> { - static void before(std::tuple& tuple) { - all_before<0, T...>(tuple); - } - - private: - template - static void all_before(std::tuple& tuple) { - detail::BeforeCaller::before(std::get(tuple)); - all_before(tuple); - } - - template - static void all_before(std::tuple&) {} -}; - -template -struct AfterCaller> { - static void after(std::tuple& tuple) { - all_after<0, T...>(tuple); - } - - private: - template - static void all_after(std::tuple& tuple) { - all_after(tuple); - detail::AfterCaller::after(std::get(tuple)); - } - - template - static void all_after(std::tuple&) {} -}; - -} // namespace detail - -// A decorator which implements an around idiom. A With instance is -// RAII constructed before each call to the undecorated class; the -// ctor is passed a single argument of type WithArg&. Plain and Base -// are used as in the base class. -template -class WithRuntimeDecorator : public RuntimeDecorator { - public: - using RD = RuntimeDecorator; - - // The reference arguments to the ctor are stored, but not used by - // the ctor, and there is no ctor, so they can be passed members of - // the derived class. - WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {} - - ICast* castInterface(const UUID& interfaceUUID) override { - Around around{with_}; - return RD::castInterface(interfaceUUID); - } - - Value evaluateJavaScript( - const std::shared_ptr& buffer, - const std::string& sourceURL) override { - Around around{with_}; - return RD::evaluateJavaScript(buffer, sourceURL); - } - std::shared_ptr prepareJavaScript( - const std::shared_ptr& buffer, - std::string sourceURL) override { - Around around{with_}; - return RD::prepareJavaScript(buffer, std::move(sourceURL)); - } - Value evaluatePreparedJavaScript( - const std::shared_ptr& js) override { - Around around{with_}; - return RD::evaluatePreparedJavaScript(js); - } - void queueMicrotask(const Function& callback) override { - Around around{with_}; - RD::queueMicrotask(callback); - } - bool drainMicrotasks(int maxMicrotasksHint) override { - Around around{with_}; - return RD::drainMicrotasks(maxMicrotasksHint); - } - Object global() override { - Around around{with_}; - return RD::global(); - } - std::string description() override { - Around around{with_}; - return RD::description(); - } - bool isInspectable() override { - Around around{with_}; - return RD::isInspectable(); - } - - // The jsi:: prefix is necessary because MSVC compiler complains C2247: - // Instrumentation is not accessible because RuntimeDecorator uses private - // to inherit from Instrumentation. - // TODO(T40821815) Consider removing this workaround when updating MSVC - jsi::Instrumentation& instrumentation() override { - Around around{with_}; - return RD::instrumentation(); - } - - protected: - Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneSymbol(pv); - } - Runtime::PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneBigInt(pv); - } - Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneString(pv); - } - Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::cloneObject(pv); - } - Runtime::PointerValue* clonePropNameID( - const Runtime::PointerValue* pv) override { - Around around{with_}; - return RD::clonePropNameID(pv); - } - - PropNameID createPropNameIDFromAscii(const char* str, size_t length) - override { - Around around{with_}; - return RD::createPropNameIDFromAscii(str, length); - } - PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length) - override { - Around around{with_}; - return RD::createPropNameIDFromUtf8(utf8, length); - } - PropNameID createPropNameIDFromUtf16(const char16_t* utf16, size_t length) - override { - Around around{with_}; - return RD::createPropNameIDFromUtf16(utf16, length); - } - PropNameID createPropNameIDFromString(const String& str) override { - Around around{with_}; - return RD::createPropNameIDFromString(str); - } - PropNameID createPropNameIDFromSymbol(const Symbol& sym) override { - Around around{with_}; - return RD::createPropNameIDFromSymbol(sym); - } - std::string utf8(const PropNameID& id) override { - Around around{with_}; - return RD::utf8(id); - } - bool compare(const PropNameID& a, const PropNameID& b) override { - Around around{with_}; - return RD::compare(a, b); - } - - std::string symbolToString(const Symbol& sym) override { - Around around{with_}; - return RD::symbolToString(sym); - } - - BigInt createBigIntFromInt64(int64_t i) override { - Around around{with_}; - return RD::createBigIntFromInt64(i); - } - BigInt createBigIntFromUint64(uint64_t i) override { - Around around{with_}; - return RD::createBigIntFromUint64(i); - } - bool bigintIsInt64(const BigInt& bi) override { - Around around{with_}; - return RD::bigintIsInt64(bi); - } - bool bigintIsUint64(const BigInt& bi) override { - Around around{with_}; - return RD::bigintIsUint64(bi); - } - uint64_t truncate(const BigInt& bi) override { - Around around{with_}; - return RD::truncate(bi); - } - String bigintToString(const BigInt& bi, int i) override { - Around around{with_}; - return RD::bigintToString(bi, i); - } - - String createStringFromAscii(const char* str, size_t length) override { - Around around{with_}; - return RD::createStringFromAscii(str, length); - } - String createStringFromUtf8(const uint8_t* utf8, size_t length) override { - Around around{with_}; - return RD::createStringFromUtf8(utf8, length); - } - String createStringFromUtf16(const char16_t* utf16, size_t length) override { - Around around{with_}; - return RD::createStringFromUtf16(utf16, length); - } - std::string utf8(const String& s) override { - Around around{with_}; - return RD::utf8(s); - } - - std::u16string utf16(const String& str) override { - Around around{with_}; - return RD::utf16(str); - } - std::u16string utf16(const PropNameID& sym) override { - Around around{with_}; - return RD::utf16(sym); - } - - void getStringData( - const jsi::String& str, - void* ctx, - void ( - *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { - Around around{with_}; - RD::getStringData(str, ctx, cb); - } - - void getPropNameIdData( - const jsi::PropNameID& sym, - void* ctx, - void ( - *cb)(void* ctx, bool ascii, const void* data, size_t num)) override { - Around around{with_}; - RD::getPropNameIdData(sym, ctx, cb); - } - - Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override { - Around around{with_}; - return RD::createValueFromJsonUtf8(json, length); - } - - Object createObjectWithPrototype(const Value& prototype) override { - Around around{with_}; - return RD::createObjectWithPrototype(prototype); - } - - Object createObject() override { - Around around{with_}; - return RD::createObject(); - } - Object createObject(std::shared_ptr ho) override { - Around around{with_}; - return RD::createObject(std::move(ho)); - } - std::shared_ptr getHostObject(const jsi::Object& o) override { - Around around{with_}; - return RD::getHostObject(o); - } - HostFunctionType& getHostFunction(const jsi::Function& f) override { - Around around{with_}; - return RD::getHostFunction(f); - } - - bool hasNativeState(const Object& o) override { - Around around{with_}; - return RD::hasNativeState(o); - } - std::shared_ptr getNativeState(const Object& o) override { - Around around{with_}; - return RD::getNativeState(o); - } - void setNativeState(const Object& o, std::shared_ptr state) - override { - Around around{with_}; - RD::setNativeState(o, state); - } - - void setPrototypeOf(const Object& object, const Value& prototype) override { - Around around{with_}; - RD::setPrototypeOf(object, prototype); - } - - Value getPrototypeOf(const Object& object) override { - Around around{with_}; - return RD::getPrototypeOf(object); - } - - Value getProperty(const Object& o, const PropNameID& name) override { - Around around{with_}; - return RD::getProperty(o, name); - } - Value getProperty(const Object& o, const String& name) override { - Around around{with_}; - return RD::getProperty(o, name); - } - Value getProperty(const Object& o, const Value& name) override { - Around around{with_}; - return RD::getProperty(o, name); - } - bool hasProperty(const Object& o, const PropNameID& name) override { - Around around{with_}; - return RD::hasProperty(o, name); - } - bool hasProperty(const Object& o, const String& name) override { - Around around{with_}; - return RD::hasProperty(o, name); - } - bool hasProperty(const Object& o, const Value& name) override { - Around around{with_}; - return RD::hasProperty(o, name); - } - void setPropertyValue( - const Object& o, - const PropNameID& name, - const Value& value) override { - Around around{with_}; - RD::setPropertyValue(o, name, value); - } - void setPropertyValue(const Object& o, const String& name, const Value& value) - override { - Around around{with_}; - RD::setPropertyValue(o, name, value); - } - void setPropertyValue(const Object& o, const Value& name, const Value& value) - override { - Around around{with_}; - RD::setPropertyValue(o, name, value); - } - - void deleteProperty(const Object& object, const PropNameID& name) override { - Around around{with_}; - RD::deleteProperty(object, name); - } - - void deleteProperty(const Object& object, const String& name) override { - Around around{with_}; - RD::deleteProperty(object, name); - } - - void deleteProperty(const Object& object, const Value& name) override { - Around around{with_}; - RD::deleteProperty(object, name); - } - - bool isArray(const Object& o) const override { - Around around{with_}; - return RD::isArray(o); - } - bool isArrayBuffer(const Object& o) const override { - Around around{with_}; - return RD::isArrayBuffer(o); - } - bool isFunction(const Object& o) const override { - Around around{with_}; - return RD::isFunction(o); - } - bool isHostObject(const jsi::Object& o) const override { - Around around{with_}; - return RD::isHostObject(o); - } - bool isHostFunction(const jsi::Function& f) const override { - Around around{with_}; - return RD::isHostFunction(f); - } - Array getPropertyNames(const Object& o) override { - Around around{with_}; - return RD::getPropertyNames(o); - } - - WeakObject createWeakObject(const Object& o) override { - Around around{with_}; - return RD::createWeakObject(o); - } - Value lockWeakObject(const WeakObject& wo) override { - Around around{with_}; - return RD::lockWeakObject(wo); - } - - Array createArray(size_t length) override { - Around around{with_}; - return RD::createArray(length); - } - ArrayBuffer createArrayBuffer( - std::shared_ptr buffer) override { - return RD::createArrayBuffer(std::move(buffer)); - } - size_t size(const Array& a) override { - Around around{with_}; - return RD::size(a); - } - size_t size(const ArrayBuffer& ab) override { - Around around{with_}; - return RD::size(ab); - } - uint8_t* data(const ArrayBuffer& ab) override { - Around around{with_}; - return RD::data(ab); - } - Value getValueAtIndex(const Array& a, size_t i) override { - Around around{with_}; - return RD::getValueAtIndex(a, i); - } - void setValueAtIndexImpl(const Array& a, size_t i, const Value& value) - override { - Around around{with_}; - RD::setValueAtIndexImpl(a, i, value); - } - - Function createFunctionFromHostFunction( - const PropNameID& name, - unsigned int paramCount, - HostFunctionType func) override { - Around around{with_}; - return RD::createFunctionFromHostFunction( - name, paramCount, std::move(func)); - } - Value call( - const Function& f, - const Value& jsThis, - const Value* args, - size_t count) override { - Around around{with_}; - return RD::call(f, jsThis, args, count); - } - Value callAsConstructor(const Function& f, const Value* args, size_t count) - override { - Around around{with_}; - return RD::callAsConstructor(f, args, count); - } - - // Private data for managing scopes. - Runtime::ScopeState* pushScope() override { - Around around{with_}; - return RD::pushScope(); - } - void popScope(Runtime::ScopeState* ss) override { - Around around{with_}; - RD::popScope(ss); - } - - bool strictEquals(const Symbol& a, const Symbol& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - } - bool strictEquals(const BigInt& a, const BigInt& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - } - - bool strictEquals(const String& a, const String& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - } - bool strictEquals(const Object& a, const Object& b) const override { - Around around{with_}; - return RD::strictEquals(a, b); - } - - bool instanceOf(const Object& o, const Function& f) override { - Around around{with_}; - return RD::instanceOf(o, f); - } - - void setExternalMemoryPressure(const jsi::Object& obj, size_t amount) - override { - Around around{with_}; - RD::setExternalMemoryPressure(obj, amount); - } - - void setRuntimeDataImpl( - const UUID& uuid, - const void* data, - void (*deleter)(const void* data)) override { - Around around{with_}; - RD::setRuntimeDataImpl(uuid, data, deleter); - } - - const void* getRuntimeDataImpl(const UUID& uuid) override { - Around around{with_}; - return RD::getRuntimeDataImpl(uuid); - } - - private: - // Wrap an RAII type around With& to guarantee after always happens. - struct Around { - Around(With& with) : with_(with) { - detail::BeforeCaller::before(with_); - } - ~Around() { - detail::AfterCaller::after(with_); - } - - With& with_; - }; - - With& with_; -}; - -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/instrumentation.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/instrumentation.h deleted file mode 100644 index 4a88951f..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/instrumentation.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -namespace facebook { -namespace jsi { - -/// Methods for starting and collecting instrumentation, an \c Instrumentation -/// instance is associated with a particular \c Runtime instance, which it -/// controls the instrumentation of. -/// None of these functions should return newly created jsi values, nor should -/// it modify the values of any jsi values in the heap (although GCs are fine). -class JSI_EXPORT Instrumentation { - public: - /// Additional options controlling what to include when capturing a heap - /// snapshot. - struct HeapSnapshotOptions { - bool captureNumericValue{false}; - }; - - virtual ~Instrumentation() = default; - - /// Returns GC statistics as a JSON-encoded string, with an object containing - /// "type" and "version" fields outermost. "type" is a string, unique to a - /// particular implementation of \c jsi::Instrumentation, and "version" is a - /// number to indicate any revision to that implementation and its output - /// format. - /// - /// \pre This call can only be made on the instrumentation instance of a - /// runtime initialised to collect GC statistics. - /// - /// \post All cumulative measurements mentioned in the output are accumulated - /// across the entire lifetime of the Runtime. - /// - /// \return the GC statistics collected so far, as a JSON-encoded string. - virtual std::string getRecordedGCStats() = 0; - - /// Request statistics about the current state of the runtime's heap. This - /// function can be called at any time, and should produce information that is - /// correct at the instant it is called (i.e, not stale). - /// - /// \return a map from a string key to a number associated with that - /// statistic. - virtual std::unordered_map getHeapInfo( - bool includeExpensive) = 0; - - /// Perform a full garbage collection. - /// \param cause The cause of this collection, as it should be reported in - /// logs. - virtual void collectGarbage(std::string cause) = 0; - - /// A HeapStatsUpdate is a tuple of the fragment index, the number of objects - /// in that fragment, and the number of bytes used by those objects. - /// A "fragment" is a view of all objects allocated within a time slice. - using HeapStatsUpdate = std::tuple; - - /// Start capturing JS stack-traces for all JS heap allocated objects. These - /// can be accessed via \c ::createSnapshotToFile(). - /// \param fragmentCallback If present, invoke this callback every so often - /// with the most recently seen object ID, and a list of fragments that have - /// been updated. This callback will be invoked on the same thread that the - /// runtime is using. - virtual void startTrackingHeapObjectStackTraces( - std::function stats)> fragmentCallback) = 0; - - /// Stop capture JS stack-traces for JS heap allocated objects. - virtual void stopTrackingHeapObjectStackTraces() = 0; - - /// Start a heap sampling profiler that will sample heap allocations, and the - /// stack trace they were allocated at. Reports a summary of which functions - /// allocated the most. - /// \param samplingInterval The number of bytes allocated to wait between - /// samples. This will be used as the expected value of a poisson - /// distribution. - virtual void startHeapSampling(size_t samplingInterval) = 0; - - /// Turns off the heap sampling profiler previously enabled via - /// \c startHeapSampling. Writes the output of the sampling heap profiler to - /// \p os. The output is a JSON formatted string. - virtual void stopHeapSampling(std::ostream& os) = 0; - - /// Captures the heap to a file - /// - /// \param path to save the heap capture. - /// \param options additional options for what to capture. - virtual void createSnapshotToFile( - const std::string& path, - const HeapSnapshotOptions& options = {false}) = 0; - - /// Captures the heap to an output stream - /// - /// \param os output stream to write to. - /// \param options additional options for what to capture. - virtual void createSnapshotToStream( - std::ostream& os, - const HeapSnapshotOptions& options = {false}) = 0; - - /// If the runtime has been created to trace to a temp file, flush - /// any unwritten parts of the trace of bridge traffic to the file, - /// and return the name of the file. Otherwise, return the empty string. - /// Tracing is disabled after this call. - virtual std::string flushAndDisableBridgeTrafficTrace() = 0; - - /// Write basic block profile trace to the given file name. - virtual void writeBasicBlockProfileTraceToFile( - const std::string& fileName) const = 0; - - /// Write the opcode stats to the given stream. - virtual void dumpOpcodeStats(std::ostream& os) const = 0; - - /// Dump external profiler symbols to the given file name. - virtual void dumpProfilerSymbolsToFile(const std::string& fileName) const = 0; -}; - -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsi-inl.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsi-inl.h deleted file mode 100644 index 2f70a594..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsi-inl.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -namespace facebook { -namespace jsi { -namespace detail { - -inline Value toValue(Runtime&, std::nullptr_t) { - return Value::null(); -} -inline Value toValue(Runtime&, bool b) { - return Value(b); -} -inline Value toValue(Runtime&, double d) { - return Value(d); -} -inline Value toValue(Runtime&, float f) { - return Value(static_cast(f)); -} -inline Value toValue(Runtime&, int i) { - return Value(i); -} -inline Value toValue(Runtime& runtime, const char* str) { - return String::createFromAscii(runtime, str); -} -inline Value toValue(Runtime& runtime, const std::string& str) { - return String::createFromUtf8(runtime, str); -} -template -inline Value toValue(Runtime& runtime, const T& other) { - static_assert( - std::is_base_of::value, - "This type cannot be converted to Value"); - return Value(runtime, other); -} -inline Value toValue(Runtime& runtime, const Value& value) { - return Value(runtime, value); -} -inline Value&& toValue(Runtime&, Value&& value) { - return std::move(value); -} - -inline PropNameID toPropNameID(Runtime& runtime, const char* name) { - return PropNameID::forAscii(runtime, name); -} -inline PropNameID toPropNameID(Runtime& runtime, const std::string& name) { - return PropNameID::forUtf8(runtime, name); -} -inline PropNameID&& toPropNameID(Runtime&, PropNameID&& name) { - return std::move(name); -} - -/// Helper to throw while still compiling with exceptions turned off. -template -[[noreturn]] inline void throwOrDie(Args&&... args) { - std::rethrow_exception( - std::make_exception_ptr(E{std::forward(args)...})); -} - -} // namespace detail - -template -inline T Runtime::make(Runtime::PointerValue* pv) { - return T(pv); -} - -inline Runtime::PointerValue* Runtime::getPointerValue(jsi::Pointer& pointer) { - return pointer.ptr_; -} - -inline const Runtime::PointerValue* Runtime::getPointerValue( - const jsi::Pointer& pointer) { - return pointer.ptr_; -} - -inline const Runtime::PointerValue* Runtime::getPointerValue( - const jsi::Value& value) { - return value.data_.pointer.ptr_; -} - -inline void Runtime::setRuntimeData( - const UUID& uuid, - const std::shared_ptr& data) { - auto* dataPtr = new std::shared_ptr(data); - setRuntimeDataImpl(uuid, dataPtr, [](const void* data) { - delete (const std::shared_ptr*)data; - }); -} - -inline std::shared_ptr Runtime::getRuntimeData(const UUID& uuid) { - auto* data = (const std::shared_ptr*)getRuntimeDataImpl(uuid); - return data ? *data : nullptr; -} - -Value Object::getPrototype(Runtime& runtime) const { - return runtime.getPrototypeOf(*this); -} - -inline Value Object::getProperty(Runtime& runtime, const char* name) const { - return getProperty(runtime, String::createFromAscii(runtime, name)); -} - -inline Value Object::getProperty(Runtime& runtime, const String& name) const { - return runtime.getProperty(*this, name); -} - -inline Value Object::getProperty(Runtime& runtime, const PropNameID& name) - const { - return runtime.getProperty(*this, name); -} - -inline Value Object::getProperty(Runtime& runtime, const Value& name) const { - return runtime.getProperty(*this, name); -} - -inline bool Object::hasProperty(Runtime& runtime, const char* name) const { - return hasProperty(runtime, String::createFromAscii(runtime, name)); -} - -inline bool Object::hasProperty(Runtime& runtime, const String& name) const { - return runtime.hasProperty(*this, name); -} - -inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name) - const { - return runtime.hasProperty(*this, name); -} - -inline bool Object::hasProperty(Runtime& runtime, const Value& name) const { - return runtime.hasProperty(*this, name); -} - -template -void Object::setProperty(Runtime& runtime, const char* name, T&& value) const { - setProperty( - runtime, String::createFromAscii(runtime, name), std::forward(value)); -} - -template -void Object::setProperty(Runtime& runtime, const String& name, T&& value) - const { - setPropertyValue( - runtime, name, detail::toValue(runtime, std::forward(value))); -} - -template -void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value) - const { - setPropertyValue( - runtime, name, detail::toValue(runtime, std::forward(value))); -} - -template -void Object::setProperty(Runtime& runtime, const Value& name, T&& value) const { - setPropertyValue( - runtime, name, detail::toValue(runtime, std::forward(value))); -} - -inline void Object::deleteProperty(Runtime& runtime, const char* name) const { - deleteProperty(runtime, String::createFromAscii(runtime, name)); -} - -inline void Object::deleteProperty(Runtime& runtime, const String& name) const { - runtime.deleteProperty(*this, name); -} - -inline void Object::deleteProperty(Runtime& runtime, const PropNameID& name) - const { - runtime.deleteProperty(*this, name); -} - -inline void Object::deleteProperty(Runtime& runtime, const Value& name) const { - runtime.deleteProperty(*this, name); -} - -inline Array Object::getArray(Runtime& runtime) const& { - assert(runtime.isArray(*this)); - (void)runtime; // when assert is disabled we need to mark this as used - return Array(runtime.cloneObject(ptr_)); -} - -inline Array Object::getArray(Runtime& runtime) && { - assert(runtime.isArray(*this)); - (void)runtime; // when assert is disabled we need to mark this as used - Runtime::PointerValue* value = ptr_; - ptr_ = nullptr; - return Array(value); -} - -inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const& { - assert(runtime.isArrayBuffer(*this)); - (void)runtime; // when assert is disabled we need to mark this as used - return ArrayBuffer(runtime.cloneObject(ptr_)); -} - -inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && { - assert(runtime.isArrayBuffer(*this)); - (void)runtime; // when assert is disabled we need to mark this as used - Runtime::PointerValue* value = ptr_; - ptr_ = nullptr; - return ArrayBuffer(value); -} - -inline Function Object::getFunction(Runtime& runtime) const& { - assert(runtime.isFunction(*this)); - return Function(runtime.cloneObject(ptr_)); -} - -inline Function Object::getFunction(Runtime& runtime) && { - assert(runtime.isFunction(*this)); - (void)runtime; // when assert is disabled we need to mark this as used - Runtime::PointerValue* value = ptr_; - ptr_ = nullptr; - return Function(value); -} - -template -inline bool Object::isHostObject(Runtime& runtime) const { - return runtime.isHostObject(*this) && - std::dynamic_pointer_cast(runtime.getHostObject(*this)); -} - -template <> -inline bool Object::isHostObject(Runtime& runtime) const { - return runtime.isHostObject(*this); -} - -template -inline std::shared_ptr Object::getHostObject(Runtime& runtime) const { - assert(isHostObject(runtime)); - return std::static_pointer_cast(runtime.getHostObject(*this)); -} - -template -inline std::shared_ptr Object::asHostObject(Runtime& runtime) const { - if (!isHostObject(runtime)) { - detail::throwOrDie( - "Object is not a HostObject of desired type"); - } - return std::static_pointer_cast(runtime.getHostObject(*this)); -} - -template <> -inline std::shared_ptr Object::getHostObject( - Runtime& runtime) const { - assert(runtime.isHostObject(*this)); - return runtime.getHostObject(*this); -} - -template -inline bool Object::hasNativeState(Runtime& runtime) const { - return runtime.hasNativeState(*this) && - std::dynamic_pointer_cast(runtime.getNativeState(*this)); -} - -template <> -inline bool Object::hasNativeState(Runtime& runtime) const { - return runtime.hasNativeState(*this); -} - -template -inline std::shared_ptr Object::getNativeState(Runtime& runtime) const { - assert(hasNativeState(runtime)); - return std::static_pointer_cast(runtime.getNativeState(*this)); -} - -inline void Object::setNativeState( - Runtime& runtime, - std::shared_ptr state) const { - runtime.setNativeState(*this, state); -} - -inline void Object::setExternalMemoryPressure(Runtime& runtime, size_t amt) - const { - runtime.setExternalMemoryPressure(*this, amt); -} - -inline Array Object::getPropertyNames(Runtime& runtime) const { - return runtime.getPropertyNames(*this); -} - -inline Value WeakObject::lock(Runtime& runtime) const { - return runtime.lockWeakObject(*this); -} - -template -void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value) const { - setValueAtIndexImpl( - runtime, i, detail::toValue(runtime, std::forward(value))); -} - -inline Value Array::getValueAtIndex(Runtime& runtime, size_t i) const { - return runtime.getValueAtIndex(*this, i); -} - -inline Function Function::createFromHostFunction( - Runtime& runtime, - const jsi::PropNameID& name, - unsigned int paramCount, - jsi::HostFunctionType func) { - return runtime.createFunctionFromHostFunction( - name, paramCount, std::move(func)); -} - -inline Value Function::call(Runtime& runtime, const Value* args, size_t count) - const { - return runtime.call(*this, Value::undefined(), args, count); -} - -inline Value Function::call(Runtime& runtime, std::initializer_list args) - const { - return call(runtime, args.begin(), args.size()); -} - -template -inline Value Function::call(Runtime& runtime, Args&&... args) const { - // A more awesome version of this would be able to create raw values - // which can be used directly without wrapping and unwrapping, but - // this will do for now. - return call(runtime, {detail::toValue(runtime, std::forward(args))...}); -} - -inline Value Function::callWithThis( - Runtime& runtime, - const Object& jsThis, - const Value* args, - size_t count) const { - return runtime.call(*this, Value(runtime, jsThis), args, count); -} - -inline Value Function::callWithThis( - Runtime& runtime, - const Object& jsThis, - std::initializer_list args) const { - return callWithThis(runtime, jsThis, args.begin(), args.size()); -} - -template -inline Value Function::callWithThis( - Runtime& runtime, - const Object& jsThis, - Args&&... args) const { - // A more awesome version of this would be able to create raw values - // which can be used directly without wrapping and unwrapping, but - // this will do for now. - return callWithThis( - runtime, jsThis, {detail::toValue(runtime, std::forward(args))...}); -} - -template -inline Array Array::createWithElements(Runtime& runtime, Args&&... args) { - return createWithElements( - runtime, {detail::toValue(runtime, std::forward(args))...}); -} - -template -inline std::vector PropNameID::names( - Runtime& runtime, - Args&&... args) { - return names({detail::toPropNameID(runtime, std::forward(args))...}); -} - -template -inline std::vector PropNameID::names( - PropNameID (&&propertyNames)[N]) { - std::vector result; - result.reserve(N); - for (auto& name : propertyNames) { - result.push_back(std::move(name)); - } - return result; -} - -inline Value Function::callAsConstructor( - Runtime& runtime, - const Value* args, - size_t count) const { - return runtime.callAsConstructor(*this, args, count); -} - -inline Value Function::callAsConstructor( - Runtime& runtime, - std::initializer_list args) const { - return callAsConstructor(runtime, args.begin(), args.size()); -} - -template -inline Value Function::callAsConstructor(Runtime& runtime, Args&&... args) - const { - return callAsConstructor( - runtime, {detail::toValue(runtime, std::forward(args))...}); -} - -String BigInt::toString(Runtime& runtime, int radix) const { - return runtime.bigintToString(*this, radix); -} - -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsi.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsi.h deleted file mode 100644 index 08edcd2a..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsi.h +++ /dev/null @@ -1,1864 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#ifndef JSI_EXPORT -#ifdef _MSC_VER -#ifdef CREATE_SHARED_LIBRARY -#define JSI_EXPORT __declspec(dllexport) -#else -#define JSI_EXPORT -#endif // CREATE_SHARED_LIBRARY -#else // _MSC_VER -#define JSI_EXPORT __attribute__((visibility("default"))) -#endif // _MSC_VER -#endif // !defined(JSI_EXPORT) - -class FBJSRuntime; -namespace facebook { -namespace jsi { - -/// UUID version 1 implementation. This should be constructed with constant -/// arguments to identify fixed UUIDs. -class JSI_EXPORT UUID { - public: - // Construct from raw parts - constexpr UUID( - uint32_t timeLow, - uint16_t timeMid, - uint16_t timeHighAndVersion, - uint16_t variantAndClockSeq, - uint64_t node) - : high( - ((uint64_t)(timeLow) << 32) | ((uint64_t)(timeMid) << 16) | - ((uint64_t)(timeHighAndVersion))), - low(((uint64_t)(variantAndClockSeq) << 48) | node) {} - - // Default constructor (zero UUID) - constexpr UUID() : high(0), low(0) {} - - constexpr UUID(const UUID&) = default; - constexpr UUID& operator=(const UUID&) = default; - - constexpr bool operator==(const UUID& other) const { - return high == other.high && low == other.low; - } - constexpr bool operator!=(const UUID& other) const { - return !(*this == other); - } - - // Ordering (for std::map, sorting, etc.) - constexpr bool operator<(const UUID& other) const { - return (high < other.high) || (high == other.high && low < other.low); - } - - // Hash support for UUID (for unordered_map compatibility) - struct Hash { - std::size_t operator()(const UUID& uuid) const noexcept { - return std::hash{}(uuid.high) ^ - (std::hash{}(uuid.low) << 1); - } - }; - - // UUID format: 8-4-4-4-12 - std::string toString() const { - std::string buffer(36, ' '); - std::snprintf( - buffer.data(), - buffer.size() + 1, - "%08x-%04x-%04x-%04x-%012llx", - getTimeLow(), - getTimeMid(), - getTimeHighAndVersion(), - getVariantAndClockSeq(), - (unsigned long long)getNode()); - return buffer; - } - - constexpr uint32_t getTimeLow() const { - return (uint32_t)(high >> 32); - } - - constexpr uint16_t getTimeMid() const { - return (uint16_t)(high >> 16); - } - - constexpr uint16_t getTimeHighAndVersion() const { - return (uint16_t)high; - } - - constexpr uint16_t getVariantAndClockSeq() const { - return (uint16_t)(low >> 48); - } - - constexpr uint64_t getNode() const { - return low & 0xFFFFFFFFFFFF; - } - - private: - uint64_t high; - uint64_t low; -}; - -/// Base interface that all JSI interfaces inherit from. Users should not try to -/// manipulate this base type directly, and should use castInterface to get the -/// appropriate subtype. -struct JSI_EXPORT ICast { - /// If the current object can be cast into the interface specified by \p - /// interfaceUUID, return a pointer to the object. Otherwise, return a null - /// pointer. - /// The returned interface has the same lifetime as the underlying object. It - /// does not need to be released when not needed. - virtual ICast* castInterface(const UUID& interfaceUUID) = 0; - - protected: - /// Interfaces are not destructible, thus the destructor is intentionally - /// protected to prevent delete calls on the interface. - /// Additionally, the destructor is non-virtual to reduce the vtable - /// complexity from inheritance. - ~ICast() = default; -}; - -/// Base class for buffers of data or bytecode that need to be passed to the -/// runtime. The buffer is expected to be fully immutable, so the result of -/// size(), data(), and the contents of the pointer returned by data() must not -/// change after construction. -class JSI_EXPORT Buffer { - public: - virtual ~Buffer(); - virtual size_t size() const = 0; - virtual const uint8_t* data() const = 0; -}; - -class JSI_EXPORT StringBuffer : public Buffer { - public: - StringBuffer(std::string s) : s_(std::move(s)) {} - size_t size() const override { - return s_.size(); - } - const uint8_t* data() const override { - return reinterpret_cast(s_.data()); - } - - private: - std::string s_; -}; - -/// Base class for buffers of data that need to be passed to the runtime. The -/// result of size() and data() must not change after construction. However, the -/// region pointed to by data() may be modified by the user or the runtime. The -/// user must ensure that access to the contents of the buffer is properly -/// synchronised. -class JSI_EXPORT MutableBuffer { - public: - virtual ~MutableBuffer(); - virtual size_t size() const = 0; - virtual uint8_t* data() = 0; -}; - -/// PreparedJavaScript is a base class representing JavaScript which is in a -/// form optimized for execution, in a runtime-specific way. Construct one via -/// jsi::Runtime::prepareJavaScript(). -/// ** This is an experimental API that is subject to change. ** -class JSI_EXPORT PreparedJavaScript { - protected: - PreparedJavaScript() = default; - - public: - virtual ~PreparedJavaScript() = 0; -}; - -class Runtime; -class Pointer; -class PropNameID; -class Symbol; -class BigInt; -class String; -class Object; -class WeakObject; -class Array; -class ArrayBuffer; -class Function; -class Value; -class Instrumentation; -class Scope; -class JSIException; -class JSError; - -/// A function which has this type can be registered as a function -/// callable from JavaScript using Function::createFromHostFunction(). -/// When the function is called, args will point to the arguments, and -/// count will indicate how many arguments are passed. The function -/// can return a Value to the caller, or throw an exception. If a C++ -/// exception is thrown, a JS Error will be created and thrown into -/// JS; if the C++ exception extends std::exception, the Error's -/// message will be whatever what() returns. Note that it is undefined whether -/// HostFunctions may or may not be called in strict mode; that is `thisVal` -/// can be any value - it will not necessarily be coerced to an object or -/// or set to the global object. -using HostFunctionType = std::function< - Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>; - -/// An object which implements this interface can be registered as an -/// Object with the JS runtime. -class JSI_EXPORT HostObject { - public: - // The C++ object's dtor will be called when the GC finalizes this - // object. (This may be as late as when the Runtime is shut down.) - // You have no control over which thread it is called on. This will - // be called from inside the GC, so it is unsafe to do any VM - // operations which require a Runtime&. Derived classes' dtors - // should also avoid doing anything expensive. Calling the dtor on - // a jsi object is explicitly ok. If you want to do JS operations, - // or any nontrivial work, you should add it to a work queue, and - // manage it externally. - virtual ~HostObject(); - - // When JS wants a property with a given name from the HostObject, - // it will call this method. If it throws an exception, the call - // will throw a JS \c Error object. By default this returns undefined. - // \return the value for the property. - virtual Value get(Runtime&, const PropNameID& name); - - // When JS wants to set a property with a given name on the HostObject, - // it will call this method. If it throws an exception, the call will - // throw a JS \c Error object. By default this throws a type error exception - // mimicking the behavior of a frozen object in strict mode. - virtual void set(Runtime&, const PropNameID& name, const Value& value); - - // When JS wants a list of property names for the HostObject, it will - // call this method. If it throws an exception, the call will throw a - // JS \c Error object. The default implementation returns empty vector. - virtual std::vector getPropertyNames(Runtime& rt); -}; - -/// Native state (and destructor) that can be attached to any JS object -/// using setNativeState. -class JSI_EXPORT NativeState { - public: - virtual ~NativeState(); -}; - -/// Represents a JS runtime. Movable, but not copyable. Note that -/// this object may not be thread-aware, but cannot be used safely from -/// multiple threads at once. The application is responsible for -/// ensuring that it is used safely. This could mean using the -/// Runtime from a single thread, using a mutex, doing all work on a -/// serial queue, etc. This restriction applies to the methods of -/// this class, and any method in the API which take a Runtime& as an -/// argument. Destructors (all but ~Scope), operators, or other methods -/// which do not take Runtime& as an argument are safe to call from any -/// thread, but it is still forbidden to make write operations on a single -/// instance of any class from more than one thread. In addition, to -/// make shutdown safe, destruction of objects associated with the Runtime -/// must be destroyed before the Runtime is destroyed, or from the -/// destructor of a managed HostObject or HostFunction. Informally, this -/// means that the main source of unsafe behavior is to hold a jsi object -/// in a non-Runtime-managed object, and not clean it up before the Runtime -/// is shut down. If your lifecycle is such that avoiding this is hard, -/// you will probably need to do use your own locks. -class JSI_EXPORT Runtime : public ICast { - public: - virtual ~Runtime(); - - ICast* castInterface(const UUID& interfaceUUID) override; - - /// Evaluates the given JavaScript \c buffer. \c sourceURL is used - /// to annotate the stack trace if there is an exception. The - /// contents may be utf8-encoded JS source code, or binary bytecode - /// whose format is specific to the implementation. If the input - /// format is unknown, or evaluation causes an error, a JSIException - /// will be thrown. - /// Note this function should ONLY be used when there isn't another means - /// through the JSI API. For example, it will be much slower to use this to - /// call a global function than using the JSI APIs to read the function - /// property from the global object and then calling it explicitly. - virtual Value evaluateJavaScript( - const std::shared_ptr& buffer, - const std::string& sourceURL) = 0; - - /// Prepares to evaluate the given JavaScript \c buffer by processing it into - /// a form optimized for execution. This may include pre-parsing, compiling, - /// etc. If the input is invalid (for example, cannot be parsed), a - /// JSIException will be thrown. The resulting object is tied to the - /// particular concrete type of Runtime from which it was created. It may be - /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete - /// type. - /// The PreparedJavaScript object may be passed to multiple VM instances, so - /// they can all share and benefit from the prepared script. - /// As with evaluateJavaScript(), using JavaScript code should be avoided - /// when the JSI API is sufficient. - virtual std::shared_ptr prepareJavaScript( - const std::shared_ptr& buffer, - std::string sourceURL) = 0; - - /// Evaluates a PreparedJavaScript. If evaluation causes an error, a - /// JSIException will be thrown. - /// As with evaluateJavaScript(), using JavaScript code should be avoided - /// when the JSI API is sufficient. - virtual Value evaluatePreparedJavaScript( - const std::shared_ptr& js) = 0; - - /// Queues a microtask in the JavaScript VM internal Microtask (a.k.a. Job in - /// ECMA262) queue, to be executed when the host drains microtasks in - /// its event loop implementation. - /// - /// \param callback a function to be executed as a microtask. - virtual void queueMicrotask(const jsi::Function& callback) = 0; - - /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue. - /// - /// \param maxMicrotasksHint a hint to tell an implementation that it should - /// make a best effort not execute more than the given number. It's default - /// to -1 for infinity (unbounded execution). - /// \return true if the queue is drained or false if there is more work to do. - /// - /// When there were exceptions thrown from the execution of microtasks, - /// implementations shall discard the exceptional jobs. An implementation may - /// \throw a \c JSError object to signal the hosts to handle. In that case, an - /// implementation may or may not suspend the draining. - /// - /// Hosts may call this function again to resume the draining if it was - /// suspended due to either exceptions or the \p maxMicrotasksHint bound. - /// E.g. a host may repetitively invoke this function until the queue is - /// drained to implement the "microtask checkpoint" defined in WHATWG HTML - /// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint. - /// - /// Note that error propagation is only a concern if a host needs to implement - /// `queueMicrotask`, a recent API that allows enqueueing arbitrary functions - /// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are - /// handled internally to VMs and are never propagated to hosts. - /// - /// This API offers some queue management to hosts at its best effort due to - /// different behaviors and limitations imposed by different VMs and APIs. By - /// the time this is written, An implementation may swallow exceptions (JSC), - /// may not pause (V8), and may not support bounded executions. - virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; - - /// \return the global object - virtual Object global() = 0; - - /// \return a short printable description of the instance. It should - /// at least include some human-readable indication of the runtime - /// implementation. This should only be used by logging, debugging, - /// and other developer-facing callers. - virtual std::string description() = 0; - - /// \return whether or not the underlying runtime supports debugging via the - /// Chrome remote debugging protocol. - /// - /// NOTE: the API for determining whether a runtime is debuggable and - /// registering a runtime with the debugger is still in flux, so please don't - /// use this API unless you know what you're doing. - virtual bool isInspectable() = 0; - - /// \return an interface to extract metrics from this \c Runtime. The default - /// implementation of this function returns an \c Instrumentation instance - /// which returns no metrics. - virtual Instrumentation& instrumentation(); - - /// Stores the pointer \p data with the \p uuid in the runtime. This can be - /// used to store some custom data within the runtime. When the runtime is - /// destroyed, or if an entry at an existing key is overwritten, the runtime - /// will release its ownership of the held object. - void setRuntimeData(const UUID& uuid, const std::shared_ptr& data); - - /// Returns the data associated with the \p uuid in the runtime. If there's no - /// data associated with the uuid, return a null pointer. - std::shared_ptr getRuntimeData(const UUID& uuid); - - protected: - friend class Pointer; - friend class PropNameID; - friend class Symbol; - friend class BigInt; - friend class String; - friend class Object; - friend class WeakObject; - friend class Array; - friend class ArrayBuffer; - friend class Function; - friend class Value; - friend class Scope; - friend class JSError; - - /// Stores the pointer \p data with the \p uuid in the runtime. This can be - /// used to store some custom data within the runtime. When the runtime is - /// destroyed, or if an entry at an existing key is overwritten, the runtime - /// will release its ownership by calling \p deleter. - virtual void setRuntimeDataImpl( - const UUID& uuid, - const void* data, - void (*deleter)(const void* data)); - - /// Returns the data associated with the \p uuid in the runtime. If there's no - /// data associated with the uuid, return a null pointer. - virtual const void* getRuntimeDataImpl(const UUID& uuid); - - // Potential optimization: avoid the cloneFoo() virtual dispatch, - // and instead just fix the number of fields, and copy them, since - // in practice they are trivially copyable. Sufficient use of - // rvalue arguments/methods would also reduce the number of clones. - - struct PointerValue { - virtual void invalidate() noexcept = 0; - - protected: - virtual ~PointerValue() = default; - }; - - virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneBigInt(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0; - - virtual PropNameID createPropNameIDFromAscii( - const char* str, - size_t length) = 0; - virtual PropNameID createPropNameIDFromUtf8( - const uint8_t* utf8, - size_t length) = 0; - virtual PropNameID createPropNameIDFromUtf16( - const char16_t* utf16, - size_t length); - virtual PropNameID createPropNameIDFromString(const String& str) = 0; - virtual PropNameID createPropNameIDFromSymbol(const Symbol& sym) = 0; - virtual std::string utf8(const PropNameID&) = 0; - virtual bool compare(const PropNameID&, const PropNameID&) = 0; - - virtual std::string symbolToString(const Symbol&) = 0; - - virtual BigInt createBigIntFromInt64(int64_t) = 0; - virtual BigInt createBigIntFromUint64(uint64_t) = 0; - virtual bool bigintIsInt64(const BigInt&) = 0; - virtual bool bigintIsUint64(const BigInt&) = 0; - virtual uint64_t truncate(const BigInt&) = 0; - virtual String bigintToString(const BigInt&, int) = 0; - - virtual String createStringFromAscii(const char* str, size_t length) = 0; - virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; - virtual String createStringFromUtf16(const char16_t* utf16, size_t length); - virtual std::string utf8(const String&) = 0; - - // \return a \c Value created from a utf8-encoded JSON string. The default - // implementation creates a \c String and invokes JSON.parse. - virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length); - - virtual Object createObject() = 0; - virtual Object createObject(std::shared_ptr ho) = 0; - virtual std::shared_ptr getHostObject(const jsi::Object&) = 0; - virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0; - - // Creates a new Object with the custom prototype - virtual Object createObjectWithPrototype(const Value& prototype); - - virtual bool hasNativeState(const jsi::Object&) = 0; - virtual std::shared_ptr getNativeState(const jsi::Object&) = 0; - virtual void setNativeState( - const jsi::Object&, - std::shared_ptr state) = 0; - - virtual void setPrototypeOf(const Object& object, const Value& prototype); - virtual Value getPrototypeOf(const Object& object); - - virtual Value getProperty(const Object&, const PropNameID& name) = 0; - virtual Value getProperty(const Object&, const String& name) = 0; - virtual Value getProperty(const Object&, const Value& name); - virtual bool hasProperty(const Object&, const PropNameID& name) = 0; - virtual bool hasProperty(const Object&, const String& name) = 0; - virtual bool hasProperty(const Object&, const Value& name); - virtual void setPropertyValue( - const Object&, - const PropNameID& name, - const Value& value) = 0; - virtual void - setPropertyValue(const Object&, const String& name, const Value& value) = 0; - virtual void - setPropertyValue(const Object&, const Value& name, const Value& value); - - virtual void deleteProperty(const Object&, const PropNameID& name); - virtual void deleteProperty(const Object&, const String& name); - virtual void deleteProperty(const Object&, const Value& name); - - virtual bool isArray(const Object&) const = 0; - virtual bool isArrayBuffer(const Object&) const = 0; - virtual bool isFunction(const Object&) const = 0; - virtual bool isHostObject(const jsi::Object&) const = 0; - virtual bool isHostFunction(const jsi::Function&) const = 0; - virtual Array getPropertyNames(const Object&) = 0; - - virtual WeakObject createWeakObject(const Object&) = 0; - virtual Value lockWeakObject(const WeakObject&) = 0; - - virtual Array createArray(size_t length) = 0; - virtual ArrayBuffer createArrayBuffer( - std::shared_ptr buffer) = 0; - virtual size_t size(const Array&) = 0; - virtual size_t size(const ArrayBuffer&) = 0; - virtual uint8_t* data(const ArrayBuffer&) = 0; - virtual Value getValueAtIndex(const Array&, size_t i) = 0; - virtual void - setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0; - - virtual Function createFunctionFromHostFunction( - const PropNameID& name, - unsigned int paramCount, - HostFunctionType func) = 0; - virtual Value call( - const Function&, - const Value& jsThis, - const Value* args, - size_t count) = 0; - virtual Value - callAsConstructor(const Function&, const Value* args, size_t count) = 0; - - // Private data for managing scopes. - struct ScopeState; - virtual ScopeState* pushScope(); - virtual void popScope(ScopeState*); - - virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0; - virtual bool strictEquals(const BigInt& a, const BigInt& b) const = 0; - virtual bool strictEquals(const String& a, const String& b) const = 0; - virtual bool strictEquals(const Object& a, const Object& b) const = 0; - - virtual bool instanceOf(const Object& o, const Function& f) = 0; - - /// See Object::setExternalMemoryPressure. - virtual void setExternalMemoryPressure( - const jsi::Object& obj, - size_t amount) = 0; - - virtual std::u16string utf16(const String& str); - virtual std::u16string utf16(const PropNameID& sym); - - /// Invokes the provided callback \p cb with the String content in \p str. - /// The callback must take in three arguments: bool ascii, const void* data, - /// and size_t num, respectively. \p ascii indicates whether the \p data - /// passed to the callback should be interpreted as a pointer to a sequence of - /// \p num ASCII characters or UTF16 characters. Depending on the internal - /// representation of the string, the function may invoke the callback - /// multiple times, with a different format on each invocation. The callback - /// must not access runtime functionality, as any operation on the runtime may - /// invalidate the data pointers. - virtual void getStringData( - const jsi::String& str, - void* ctx, - void (*cb)(void* ctx, bool ascii, const void* data, size_t num)); - - /// Invokes the provided callback \p cb with the PropNameID content in \p sym. - /// The callback must take in three arguments: bool ascii, const void* data, - /// and size_t num, respectively. \p ascii indicates whether the \p data - /// passed to the callback should be interpreted as a pointer to a sequence of - /// \p num ASCII characters or UTF16 characters. Depending on the internal - /// representation of the string, the function may invoke the callback - /// multiple times, with a different format on each invocation. The callback - /// must not access runtime functionality, as any operation on the runtime may - /// invalidate the data pointers. - virtual void getPropNameIdData( - const jsi::PropNameID& sym, - void* ctx, - void (*cb)(void* ctx, bool ascii, const void* data, size_t num)); - - // These exist so derived classes can access the private parts of - // Value, Symbol, String, and Object, which are all friends of Runtime. - template - static T make(PointerValue* pv); - static PointerValue* getPointerValue(Pointer& pointer); - static const PointerValue* getPointerValue(const Pointer& pointer); - static const PointerValue* getPointerValue(const Value& value); - - friend class ::FBJSRuntime; - template - friend class RuntimeDecorator; -}; - -// Base class for pointer-storing types. -class JSI_EXPORT Pointer { - protected: - explicit Pointer(Pointer&& other) noexcept : ptr_(other.ptr_) { - other.ptr_ = nullptr; - } - - ~Pointer() { - if (ptr_) { - ptr_->invalidate(); - } - } - - Pointer& operator=(Pointer&& other) noexcept; - - friend class Runtime; - friend class Value; - - explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {} - - typename Runtime::PointerValue* ptr_; -}; - -/// Represents something that can be a JS property key. Movable, not copyable. -class JSI_EXPORT PropNameID : public Pointer { - public: - using Pointer::Pointer; - - PropNameID(Runtime& runtime, const PropNameID& other) - : Pointer(runtime.clonePropNameID(other.ptr_)) {} - - PropNameID(PropNameID&& other) = default; - PropNameID& operator=(PropNameID&& other) = default; - - /// Create a JS property name id from ascii values. The data is - /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) { - return runtime.createPropNameIDFromAscii(str, length); - } - - /// Create a property name id from a nul-terminated C ascii name. The data is - /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str) { - return forAscii(runtime, str, strlen(str)); - } - - /// Create a PropNameID from a C++ string. The string is copied. - static PropNameID forAscii(Runtime& runtime, const std::string& str) { - return forAscii(runtime, str.c_str(), str.size()); - } - - /// Create a PropNameID from utf8 values. The data is copied. - /// Results are undefined if \p utf8 contains invalid code points. - static PropNameID - forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { - return runtime.createPropNameIDFromUtf8(utf8, length); - } - - /// Create a PropNameID from utf8-encoded octets stored in a - /// std::string. The string data is transformed and copied. - /// Results are undefined if \p utf8 contains invalid code points. - static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) { - return runtime.createPropNameIDFromUtf8( - reinterpret_cast(utf8.data()), utf8.size()); - } - - /// Given a series of UTF-16 encoded code units, create a PropNameId. The - /// input may contain unpaired surrogates, which will be interpreted as a code - /// point of the same value. - static PropNameID - forUtf16(Runtime& runtime, const char16_t* utf16, size_t length) { - return runtime.createPropNameIDFromUtf16(utf16, length); - } - - /// Given a series of UTF-16 encoded code units stored inside std::u16string, - /// create a PropNameId. The input may contain unpaired surrogates, which - /// will be interpreted as a code point of the same value. - static PropNameID forUtf16(Runtime& runtime, const std::u16string& str) { - return runtime.createPropNameIDFromUtf16(str.data(), str.size()); - } - - /// Create a PropNameID from a JS string. - static PropNameID forString(Runtime& runtime, const jsi::String& str) { - return runtime.createPropNameIDFromString(str); - } - - /// Create a PropNameID from a JS symbol. - static PropNameID forSymbol(Runtime& runtime, const jsi::Symbol& sym) { - return runtime.createPropNameIDFromSymbol(sym); - } - - // Creates a vector of PropNameIDs constructed from given arguments. - template - static std::vector names(Runtime& runtime, Args&&... args); - - // Creates a vector of given PropNameIDs. - template - static std::vector names(PropNameID (&&propertyNames)[N]); - - /// Copies the data in a PropNameID as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { - return runtime.utf8(*this); - } - - /// Copies the data in a PropNameID as utf16 into a C++ string. - std::u16string utf16(Runtime& runtime) const { - return runtime.utf16(*this); - } - - /// Invokes the user provided callback to process the content in PropNameId. - /// The callback must take in three arguments: bool ascii, const void* data, - /// and size_t num, respectively. \p ascii indicates whether the \p data - /// passed to the callback should be interpreted as a pointer to a sequence of - /// \p num ASCII characters or UTF16 characters. The function may invoke the - /// callback multiple times, with a different format on each invocation. The - /// callback must not access runtime functionality, as any operation on the - /// runtime may invalidate the data pointers. - template - void getPropNameIdData(Runtime& runtime, CB& cb) const { - runtime.getPropNameIdData( - *this, &cb, [](void* ctx, bool ascii, const void* data, size_t num) { - (*((CB*)ctx))(ascii, data, num); - }); - } - - static bool compare( - Runtime& runtime, - const jsi::PropNameID& a, - const jsi::PropNameID& b) { - return runtime.compare(a, b); - } - - friend class Runtime; - friend class Value; -}; - -/// Represents a JS Symbol (es6). Movable, not copyable. -/// TODO T40778724: this is a limited implementation sufficient for -/// the debugger not to crash when a Symbol is a property in an Object -/// or element in an array. Complete support for creating will come -/// later. -class JSI_EXPORT Symbol : public Pointer { - public: - using Pointer::Pointer; - - Symbol(Symbol&& other) = default; - Symbol& operator=(Symbol&& other) = default; - - /// \return whether a and b refer to the same symbol. - static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) { - return runtime.strictEquals(a, b); - } - - /// Converts a Symbol into a C++ string as JS .toString would. The output - /// will look like \c Symbol(description) . - std::string toString(Runtime& runtime) const { - return runtime.symbolToString(*this); - } - - friend class Runtime; - friend class Value; -}; - -/// Represents a JS BigInt. Movable, not copyable. -class JSI_EXPORT BigInt : public Pointer { - public: - using Pointer::Pointer; - - BigInt(BigInt&& other) = default; - BigInt& operator=(BigInt&& other) = default; - - /// Create a BigInt representing the signed 64-bit \p value. - static BigInt fromInt64(Runtime& runtime, int64_t value) { - return runtime.createBigIntFromInt64(value); - } - - /// Create a BigInt representing the unsigned 64-bit \p value. - static BigInt fromUint64(Runtime& runtime, uint64_t value) { - return runtime.createBigIntFromUint64(value); - } - - /// \return whether a === b. - static bool strictEquals(Runtime& runtime, const BigInt& a, const BigInt& b) { - return runtime.strictEquals(a, b); - } - - /// \returns This bigint truncated to a signed 64-bit integer. - int64_t getInt64(Runtime& runtime) const { - return runtime.truncate(*this); - } - - /// \returns Whether this bigint can be losslessly converted to int64_t. - bool isInt64(Runtime& runtime) const { - return runtime.bigintIsInt64(*this); - } - - /// \returns This bigint truncated to a signed 64-bit integer. Throws a - /// JSIException if the truncation is lossy. - int64_t asInt64(Runtime& runtime) const; - - /// \returns This bigint truncated to an unsigned 64-bit integer. - uint64_t getUint64(Runtime& runtime) const { - return runtime.truncate(*this); - } - - /// \returns Whether this bigint can be losslessly converted to uint64_t. - bool isUint64(Runtime& runtime) const { - return runtime.bigintIsUint64(*this); - } - - /// \returns This bigint truncated to an unsigned 64-bit integer. Throws a - /// JSIException if the truncation is lossy. - uint64_t asUint64(Runtime& runtime) const; - - /// \returns this BigInt converted to a String in base \p radix. Throws a - /// JSIException if radix is not in the [2, 36] range. - inline String toString(Runtime& runtime, int radix = 10) const; - - friend class Runtime; - friend class Value; -}; - -/// Represents a JS String. Movable, not copyable. -class JSI_EXPORT String : public Pointer { - public: - using Pointer::Pointer; - - String(String&& other) = default; - String& operator=(String&& other) = default; - - /// Create a JS string from ascii values. The string data is - /// copied. - static String - createFromAscii(Runtime& runtime, const char* str, size_t length) { - return runtime.createStringFromAscii(str, length); - } - - /// Create a JS string from a nul-terminated C ascii string. The - /// string data is copied. - static String createFromAscii(Runtime& runtime, const char* str) { - return createFromAscii(runtime, str, strlen(str)); - } - - /// Create a JS string from a C++ string. The string data is - /// copied. - static String createFromAscii(Runtime& runtime, const std::string& str) { - return createFromAscii(runtime, str.c_str(), str.size()); - } - - /// Create a JS string from utf8-encoded octets. The string data is - /// transformed and copied. Results are undefined if \p utf8 contains invalid - /// code points. - static String - createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { - return runtime.createStringFromUtf8(utf8, length); - } - - /// Create a JS string from utf8-encoded octets stored in a - /// std::string. The string data is transformed and copied. Results are - /// undefined if \p utf8 contains invalid code points. - static String createFromUtf8(Runtime& runtime, const std::string& utf8) { - return runtime.createStringFromUtf8( - reinterpret_cast(utf8.data()), utf8.length()); - } - - /// Given a series of UTF-16 encoded code units, create a JS String. The input - /// may contain unpaired surrogates, which will be interpreted as a code point - /// of the same value. - static String - createFromUtf16(Runtime& runtime, const char16_t* utf16, size_t length) { - return runtime.createStringFromUtf16(utf16, length); - } - - /// Given a series of UTF-16 encoded code units stored inside std::u16string, - /// create a JS String. The input may contain unpaired surrogates, which will - /// be interpreted as a code point of the same value. - static String createFromUtf16(Runtime& runtime, const std::u16string& utf16) { - return runtime.createStringFromUtf16(utf16.data(), utf16.length()); - } - - /// \return whether a and b contain the same characters. - static bool strictEquals(Runtime& runtime, const String& a, const String& b) { - return runtime.strictEquals(a, b); - } - - /// Copies the data in a JS string as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { - return runtime.utf8(*this); - } - - /// Copies the data in a JS string as utf16 into a C++ string. - std::u16string utf16(Runtime& runtime) const { - return runtime.utf16(*this); - } - - /// Invokes the user provided callback to process content in String. The - /// callback must take in three arguments: bool ascii, const void* data, and - /// size_t num, respectively. \p ascii indicates whether the \p data passed to - /// the callback should be interpreted as a pointer to a sequence of \p num - /// ASCII characters or UTF16 characters. The function may invoke the callback - /// multiple times, with a different format on each invocation. The callback - /// must not access runtime functionality, as any operation on the runtime may - /// invalidate the data pointers. - template - void getStringData(Runtime& runtime, CB& cb) const { - runtime.getStringData( - *this, &cb, [](void* ctx, bool ascii, const void* data, size_t num) { - (*((CB*)ctx))(ascii, data, num); - }); - } - - friend class Runtime; - friend class Value; -}; - -class Array; -class Function; - -/// Represents a JS Object. Movable, not copyable. -class JSI_EXPORT Object : public Pointer { - public: - using Pointer::Pointer; - - Object(Object&& other) = default; - Object& operator=(Object&& other) = default; - - /// Creates a new Object instance, like '{}' in JS. - explicit Object(Runtime& runtime) : Object(runtime.createObject()) {} - - static Object createFromHostObject( - Runtime& runtime, - std::shared_ptr ho) { - return runtime.createObject(ho); - } - - /// Creates a new Object with the custom prototype - static Object create(Runtime& runtime, const Value& prototype) { - return runtime.createObjectWithPrototype(prototype); - } - - /// \return whether this and \c obj are the same JSObject or not. - static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) { - return runtime.strictEquals(a, b); - } - - /// \return the result of `this instanceOf ctor` in JS. - bool instanceOf(Runtime& rt, const Function& ctor) const { - return rt.instanceOf(*this, ctor); - } - - /// Sets \p prototype as the prototype of the object. The prototype must be - /// either an Object or null. If the prototype was not set successfully, this - /// method will throw. - void setPrototype(Runtime& runtime, const Value& prototype) const { - return runtime.setPrototypeOf(*this, prototype); - } - - /// \return the prototype of the object - inline Value getPrototype(Runtime& runtime) const; - - /// \return the property of the object with the given ascii name. - /// If the name isn't a property on the object, returns the - /// undefined value. - Value getProperty(Runtime& runtime, const char* name) const; - - /// \return the property of the object with the String name. - /// If the name isn't a property on the object, returns the - /// undefined value. - Value getProperty(Runtime& runtime, const String& name) const; - - /// \return the property of the object with the given JS PropNameID - /// name. If the name isn't a property on the object, returns the - /// undefined value. - Value getProperty(Runtime& runtime, const PropNameID& name) const; - - /// \return the Property of the object with the given JS Value name. If the - /// name isn't a property on the object, returns the undefined value.This - /// attempts to convert the JS Value to convert to a property key. If the - /// conversion fails, this method may throw. - Value getProperty(Runtime& runtime, const Value& name) const; - - /// \return true if and only if the object has a property with the - /// given ascii name. - bool hasProperty(Runtime& runtime, const char* name) const; - - /// \return true if and only if the object has a property with the - /// given String name. - bool hasProperty(Runtime& runtime, const String& name) const; - - /// \return true if and only if the object has a property with the - /// given PropNameID name. - bool hasProperty(Runtime& runtime, const PropNameID& name) const; - - /// \return true if and only if the object has a property with the given - /// JS Value name. This attempts to convert the JS Value to convert to a - /// property key. If the conversion fails, this method may throw. - bool hasProperty(Runtime& runtime, const Value& name) const; - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. - template - void setProperty(Runtime& runtime, const char* name, T&& value) const; - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. - template - void setProperty(Runtime& runtime, const String& name, T&& value) const; - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. - template - void setProperty(Runtime& runtime, const PropNameID& name, T&& value) const; - - /// Sets the property value from a Value or anything which can be - /// used to make one: nullptr_t, bool, double, int, const char*, - /// String, or Object. This takes a JS Value as the property name, and - /// attempts to convert to a property key. If the conversion fails, this - /// method may throw. - template - void setProperty(Runtime& runtime, const Value& name, T&& value) const; - - /// Delete the property with the given ascii name. Throws if the deletion - /// failed. - void deleteProperty(Runtime& runtime, const char* name) const; - - /// Delete the property with the given String name. Throws if the deletion - /// failed. - void deleteProperty(Runtime& runtime, const String& name) const; - - /// Delete the property with the given PropNameID name. Throws if the deletion - /// failed. - void deleteProperty(Runtime& runtime, const PropNameID& name) const; - - /// Delete the property with the given Value name. Throws if the deletion - /// failed. - void deleteProperty(Runtime& runtime, const Value& name) const; - - /// \return true iff JS \c Array.isArray() would return \c true. If - /// so, then \c getArray() will succeed. - bool isArray(Runtime& runtime) const { - return runtime.isArray(*this); - } - - /// \return true iff the Object is an ArrayBuffer. If so, then \c - /// getArrayBuffer() will succeed. - bool isArrayBuffer(Runtime& runtime) const { - return runtime.isArrayBuffer(*this); - } - - /// \return true iff the Object is callable. If so, then \c - /// getFunction will succeed. - bool isFunction(Runtime& runtime) const { - return runtime.isFunction(*this); - } - - /// \return true iff the Object was initialized with \c createFromHostObject - /// and the HostObject passed is of type \c T. If returns \c true then - /// \c getHostObject will succeed. - template - bool isHostObject(Runtime& runtime) const; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) const&; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) &&; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will throw - /// JSIException. - Array asArray(Runtime& runtime) const&; - - /// \return an Array instance which refers to the same underlying - /// object. If \c isArray() would return false, this will throw - /// JSIException. - Array asArray(Runtime& runtime) &&; - - /// \return an ArrayBuffer instance which refers to the same underlying - /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) const&; - - /// \return an ArrayBuffer instance which refers to the same underlying - /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) &&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) const&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) &&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will throw - /// JSIException. - Function asFunction(Runtime& runtime) const&; - - /// \return a Function instance which refers to the same underlying - /// object. If \c isFunction() would return false, this will throw - /// JSIException. - Function asFunction(Runtime& runtime) &&; - - /// \return a shared_ptr which refers to the same underlying - /// \c HostObject that was used to create this object. If \c isHostObject - /// is false, this will assert. Note that this does a type check and will - /// assert if the underlying HostObject isn't of type \c T - template - std::shared_ptr getHostObject(Runtime& runtime) const; - - /// \return a shared_ptr which refers to the same underlying - /// \c HostObject that was used to create this object. If \c isHostObject - /// is false, this will throw. - template - std::shared_ptr asHostObject(Runtime& runtime) const; - - /// \return whether this object has native state of type T previously set by - /// \c setNativeState. - template - bool hasNativeState(Runtime& runtime) const; - - /// \return a shared_ptr to the state previously set by \c setNativeState. - /// If \c hasNativeState is false, this will assert. Note that this does a - /// type check and will assert if the native state isn't of type \c T - template - std::shared_ptr getNativeState(Runtime& runtime) const; - - /// Set the internal native state property of this object, overwriting any old - /// value. Creates a new shared_ptr to the object managed by \p state, which - /// will live until the value at this property becomes unreachable. - /// - /// Throws a type error if this object is a proxy or host object. - void setNativeState(Runtime& runtime, std::shared_ptr state) - const; - - /// \return same as \c getProperty(name).asObject(), except with - /// a better exception message. - Object getPropertyAsObject(Runtime& runtime, const char* name) const; - - /// \return similar to \c - /// getProperty(name).getObject().getFunction(), except it will - /// throw JSIException instead of asserting if the property is - /// not an object, or the object is not callable. - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; - - /// \return an Array consisting of all enumerable property names in - /// the object and its prototype chain. All values in the return - /// will be isString(). (This is probably not optimal, but it - /// works. I only need it in one place.) - Array getPropertyNames(Runtime& runtime) const; - - /// Inform the runtime that there is additional memory associated with a given - /// JavaScript object that is not visible to the GC. This can be used if an - /// object is known to retain some native memory, and may be used to guide - /// decisions about when to run garbage collection. - /// This method may be invoked multiple times on an object, and subsequent - /// calls will overwrite any previously set value. Once the object is garbage - /// collected, the associated external memory will be considered freed and may - /// no longer factor into GC decisions. - void setExternalMemoryPressure(Runtime& runtime, size_t amt) const; - - protected: - void setPropertyValue( - Runtime& runtime, - const String& name, - const Value& value) const { - return runtime.setPropertyValue(*this, name, value); - } - - void setPropertyValue( - Runtime& runtime, - const PropNameID& name, - const Value& value) const { - return runtime.setPropertyValue(*this, name, value); - } - - void setPropertyValue(Runtime& runtime, const Value& name, const Value& value) - const { - return runtime.setPropertyValue(*this, name, value); - } - - friend class Runtime; - friend class Value; -}; - -/// Represents a weak reference to a JS Object. If the only reference -/// to an Object are these, the object is eligible for GC. Method -/// names are inspired by C++ weak_ptr. Movable, not copyable. -class JSI_EXPORT WeakObject : public Pointer { - public: - using Pointer::Pointer; - - WeakObject(WeakObject&& other) = default; - WeakObject& operator=(WeakObject&& other) = default; - - /// Create a WeakObject from an Object. - WeakObject(Runtime& runtime, const Object& o) - : WeakObject(runtime.createWeakObject(o)) {} - - /// \return a Value representing the underlying Object if it is still valid; - /// otherwise returns \c undefined. Note that this method has nothing to do - /// with threads or concurrency. The name is based on std::weak_ptr::lock() - /// which serves a similar purpose. - Value lock(Runtime& runtime) const; - - friend class Runtime; -}; - -/// Represents a JS Object which can be efficiently used as an array -/// with integral indices. -class JSI_EXPORT Array : public Object { - public: - Array(Array&&) = default; - /// Creates a new Array instance, with \c length undefined elements. - Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {} - - Array& operator=(Array&&) = default; - - /// \return the size of the Array, according to its length property. - /// (C++ naming convention) - size_t size(Runtime& runtime) const { - return runtime.size(*this); - } - - /// \return the size of the Array, according to its length property. - /// (JS naming convention) - size_t length(Runtime& runtime) const { - return size(runtime); - } - - /// \return the property of the array at index \c i. If there is no - /// such property, returns the undefined value. If \c i is out of - /// range [ 0..\c length ] throws a JSIException. - Value getValueAtIndex(Runtime& runtime, size_t i) const; - - /// Sets the property of the array at index \c i. The argument - /// value behaves as with Object::setProperty(). If \c i is out of - /// range [ 0..\c length ] throws a JSIException. - template - void setValueAtIndex(Runtime& runtime, size_t i, T&& value) const; - - /// There is no current API for changing the size of an array once - /// created. We'll probably need that eventually. - - /// Creates a new Array instance from provided values - template - static Array createWithElements(Runtime&, Args&&... args); - - /// Creates a new Array instance from initializer list. - static Array createWithElements( - Runtime& runtime, - std::initializer_list elements); - - private: - friend class Object; - friend class Value; - friend class Runtime; - - void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) - const { - return runtime.setValueAtIndexImpl(*this, i, value); - } - - Array(Runtime::PointerValue* value) : Object(value) {} -}; - -/// Represents a JSArrayBuffer -class JSI_EXPORT ArrayBuffer : public Object { - public: - ArrayBuffer(ArrayBuffer&&) = default; - ArrayBuffer& operator=(ArrayBuffer&&) = default; - - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) - : ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {} - - /// \return the size of the ArrayBuffer storage. This is not affected by - /// overriding the byteLength property. - /// (C++ naming convention) - size_t size(Runtime& runtime) const { - return runtime.size(*this); - } - - size_t length(Runtime& runtime) const { - return runtime.size(*this); - } - - uint8_t* data(Runtime& runtime) const { - return runtime.data(*this); - } - - private: - friend class Object; - friend class Value; - friend class Runtime; - - ArrayBuffer(Runtime::PointerValue* value) : Object(value) {} -}; - -/// Represents a JS Object which is guaranteed to be Callable. -class JSI_EXPORT Function : public Object { - public: - Function(Function&&) = default; - Function& operator=(Function&&) = default; - - /// Create a function which, when invoked, calls C++ code. If the - /// function throws an exception, a JS Error will be created and - /// thrown. - /// \param name the name property for the function. - /// \param paramCount the length property for the function, which - /// may not be the number of arguments the function is passed. - /// \note The std::function's dtor will be called when the GC finalizes this - /// function. As with HostObject, this may be as late as when the Runtime is - /// shut down, and may occur on an arbitrary thread. If the function contains - /// any captured values, you are responsible for ensuring that their - /// destructors are safe to call on any thread. - static Function createFromHostFunction( - Runtime& runtime, - const jsi::PropNameID& name, - unsigned int paramCount, - jsi::HostFunctionType func); - - /// Calls the function with \c count \c args. The \c this value of the JS - /// function will not be set by the C++ caller, similar to calling - /// Function.prototype.apply(undefined, args) in JS. - /// \b Note: as with Function.prototype.apply, \c this may not always be - /// \c undefined in the function itself. If the function is non-strict, - /// \c this will be set to the global object. - Value call(Runtime& runtime, const Value* args, size_t count) const; - - /// Calls the function with a \c std::initializer_list of Value - /// arguments. The \c this value of the JS function will not be set by the - /// C++ caller, similar to calling Function.prototype.apply(undefined, args) - /// in JS. - /// \b Note: as with Function.prototype.apply, \c this may not always be - /// \c undefined in the function itself. If the function is non-strict, - /// \c this will be set to the global object. - Value call(Runtime& runtime, std::initializer_list args) const; - - /// Calls the function with any number of arguments similarly to - /// Object::setProperty(). The \c this value of the JS function will not be - /// set by the C++ caller, similar to calling - /// Function.prototype.call(undefined, ...args) in JS. - /// \b Note: as with Function.prototype.call, \c this may not always be - /// \c undefined in the function itself. If the function is non-strict, - /// \c this will be set to the global object. - template - Value call(Runtime& runtime, Args&&... args) const; - - /// Calls the function with \c count \c args and \c jsThis value passed - /// as the \c this value. - Value callWithThis( - Runtime& Runtime, - const Object& jsThis, - const Value* args, - size_t count) const; - - /// Calls the function with a \c std::initializer_list of Value - /// arguments and \c jsThis passed as the \c this value. - Value callWithThis( - Runtime& runtime, - const Object& jsThis, - std::initializer_list args) const; - - /// Calls the function with any number of arguments similarly to - /// Object::setProperty(), and with \c jsThis passed as the \c this value. - template - Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args) - const; - - /// Calls the function as a constructor with \c count \c args. Equivalent - /// to calling `new Func` where `Func` is the js function reqresented by - /// this. - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) - const; - - /// Same as above `callAsConstructor`, except use an initializer_list to - /// supply the arguments. - Value callAsConstructor(Runtime& runtime, std::initializer_list args) - const; - - /// Same as above `callAsConstructor`, but automatically converts/wraps - /// any argument with a jsi Value. - template - Value callAsConstructor(Runtime& runtime, Args&&... args) const; - - /// Returns whether this was created with Function::createFromHostFunction. - /// If true then you can use getHostFunction to get the underlying - /// HostFunctionType. - bool isHostFunction(Runtime& runtime) const { - return runtime.isHostFunction(*this); - } - - /// Returns the underlying HostFunctionType iff isHostFunction returns true - /// and asserts otherwise. You can use this to use std::function<>::target - /// to get the object that was passed to create the HostFunctionType. - /// - /// Note: The reference returned is borrowed from the JS object underlying - /// \c this, and thus only lasts as long as the object underlying - /// \c this does. - HostFunctionType& getHostFunction(Runtime& runtime) const { - assert(isHostFunction(runtime)); - return runtime.getHostFunction(*this); - } - - private: - friend class Object; - friend class Value; - friend class Runtime; - - Function(Runtime::PointerValue* value) : Object(value) {} -}; - -/// Represents any JS Value (undefined, null, boolean, number, symbol, -/// string, or object). Movable, or explicitly copyable (has no copy -/// ctor). -class JSI_EXPORT Value { - public: - /// Default ctor creates an \c undefined JS value. - Value() noexcept : Value(UndefinedKind) {} - - /// Creates a \c null JS value. - /* implicit */ Value(std::nullptr_t) : kind_(NullKind) {} - - /// Creates a boolean JS value. - /* implicit */ Value(bool b) : Value(BooleanKind) { - data_.boolean = b; - } - - /// Creates a number JS value. - /* implicit */ Value(double d) : Value(NumberKind) { - data_.number = d; - } - - /// Creates a number JS value. - /* implicit */ Value(int i) : Value(NumberKind) { - data_.number = i; - } - - /// Moves a Symbol, String, or Object rvalue into a new JS value. - template < - typename T, - typename = std::enable_if_t< - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value>> - /* implicit */ Value(T&& other) : Value(kindOf(other)) { - new (&data_.pointer) T(std::move(other)); - } - - /// Value("foo") will treat foo as a bool. This makes doing that a - /// compile error. - template - Value(const char*) { - static_assert( - !std::is_same::value, - "Value cannot be constructed directly from const char*"); - } - - Value(Value&& other) noexcept; - - /// Copies a Symbol lvalue into a new JS value. - Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) { - new (&data_.pointer) Symbol(runtime.cloneSymbol(sym.ptr_)); - } - - /// Copies a BigInt lvalue into a new JS value. - Value(Runtime& runtime, const BigInt& bigint) : Value(BigIntKind) { - new (&data_.pointer) BigInt(runtime.cloneBigInt(bigint.ptr_)); - } - - /// Copies a String lvalue into a new JS value. - Value(Runtime& runtime, const String& str) : Value(StringKind) { - new (&data_.pointer) String(runtime.cloneString(str.ptr_)); - } - - /// Copies a Object lvalue into a new JS value. - Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) { - new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_)); - } - - /// Creates a JS value from another Value lvalue. - Value(Runtime& runtime, const Value& value); - - /// Value(rt, "foo") will treat foo as a bool. This makes doing - /// that a compile error. - template - Value(Runtime&, const char*) { - static_assert( - !std::is_same::value, - "Value cannot be constructed directly from const char*"); - } - - ~Value(); - // \return the undefined \c Value. - static Value undefined() { - return Value(); - } - - // \return the null \c Value. - static Value null() { - return Value(nullptr); - } - - // \return a \c Value created from a utf8-encoded JSON string. - static Value - createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length) { - return runtime.createValueFromJsonUtf8(json, length); - } - - /// \return according to the Strict Equality Comparison algorithm, see: - /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison - static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); - - Value& operator=(Value&& other) noexcept { - this->~Value(); - new (this) Value(std::move(other)); - return *this; - } - - bool isUndefined() const { - return kind_ == UndefinedKind; - } - - bool isNull() const { - return kind_ == NullKind; - } - - bool isBool() const { - return kind_ == BooleanKind; - } - - bool isNumber() const { - return kind_ == NumberKind; - } - - bool isString() const { - return kind_ == StringKind; - } - - bool isBigInt() const { - return kind_ == BigIntKind; - } - - bool isSymbol() const { - return kind_ == SymbolKind; - } - - bool isObject() const { - return kind_ == ObjectKind; - } - - /// \return the boolean value, or asserts if not a boolean. - bool getBool() const { - assert(isBool()); - return data_.boolean; - } - - /// \return the boolean value, or throws JSIException if not a - /// boolean. - bool asBool() const; - - /// \return the number value, or asserts if not a number. - double getNumber() const { - assert(isNumber()); - return data_.number; - } - - /// \return the number value, or throws JSIException if not a - /// number. - double asNumber() const; - - /// \return the Symbol value, or asserts if not a symbol. - Symbol getSymbol(Runtime& runtime) const& { - assert(isSymbol()); - return Symbol(runtime.cloneSymbol(data_.pointer.ptr_)); - } - - /// \return the Symbol value, or asserts if not a symbol. - /// Can be used on rvalue references to avoid cloning more symbols. - Symbol getSymbol(Runtime&) && { - assert(isSymbol()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the Symbol value, or throws JSIException if not a - /// symbol - Symbol asSymbol(Runtime& runtime) const&; - Symbol asSymbol(Runtime& runtime) &&; - - /// \return the BigInt value, or asserts if not a bigint. - BigInt getBigInt(Runtime& runtime) const& { - assert(isBigInt()); - return BigInt(runtime.cloneBigInt(data_.pointer.ptr_)); - } - - /// \return the BigInt value, or asserts if not a bigint. - /// Can be used on rvalue references to avoid cloning more bigints. - BigInt getBigInt(Runtime&) && { - assert(isBigInt()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the BigInt value, or throws JSIException if not a - /// bigint - BigInt asBigInt(Runtime& runtime) const&; - BigInt asBigInt(Runtime& runtime) &&; - - /// \return the String value, or asserts if not a string. - String getString(Runtime& runtime) const& { - assert(isString()); - return String(runtime.cloneString(data_.pointer.ptr_)); - } - - /// \return the String value, or asserts if not a string. - /// Can be used on rvalue references to avoid cloning more strings. - String getString(Runtime&) && { - assert(isString()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the String value, or throws JSIException if not a - /// string. - String asString(Runtime& runtime) const&; - String asString(Runtime& runtime) &&; - - /// \return the Object value, or asserts if not an object. - Object getObject(Runtime& runtime) const& { - assert(isObject()); - return Object(runtime.cloneObject(data_.pointer.ptr_)); - } - - /// \return the Object value, or asserts if not an object. - /// Can be used on rvalue references to avoid cloning more objects. - Object getObject(Runtime&) && { - assert(isObject()); - auto ptr = data_.pointer.ptr_; - data_.pointer.ptr_ = nullptr; - return static_cast(ptr); - } - - /// \return the Object value, or throws JSIException if not an - /// object. - Object asObject(Runtime& runtime) const&; - Object asObject(Runtime& runtime) &&; - - // \return a String like JS .toString() would do. - String toString(Runtime& runtime) const; - - private: - friend class Runtime; - - enum ValueKind { - UndefinedKind, - NullKind, - BooleanKind, - NumberKind, - SymbolKind, - BigIntKind, - StringKind, - ObjectKind, - PointerKind = SymbolKind, - }; - - union Data { - // Value's ctor and dtor will manage the lifecycle of the contained Data. - Data() { - static_assert( - sizeof(Data) == sizeof(uint64_t), - "Value data should fit in a 64-bit register"); - } - ~Data() {} - - // scalars - bool boolean; - double number; - // pointers - Pointer pointer; // Symbol, String, Object, Array, Function - }; - - Value(ValueKind kind) : kind_(kind) {} - - constexpr static ValueKind kindOf(const Symbol&) { - return SymbolKind; - } - constexpr static ValueKind kindOf(const BigInt&) { - return BigIntKind; - } - constexpr static ValueKind kindOf(const String&) { - return StringKind; - } - constexpr static ValueKind kindOf(const Object&) { - return ObjectKind; - } - - ValueKind kind_; - Data data_; - - // In the future: Value becomes NaN-boxed. See T40538354. -}; - -/// Not movable and not copyable RAII marker advising the underlying -/// JavaScript VM to track resources allocated since creation until -/// destruction so that they can be recycled eagerly when the Scope -/// goes out of scope instead of floating in the air until the next -/// garbage collection or any other delayed release occurs. -/// -/// This API should be treated only as advice, implementations can -/// choose to ignore the fact that Scopes are created or destroyed. -/// -/// This class is an exception to the rule allowing destructors to be -/// called without proper synchronization (see Runtime documentation). -/// The whole point of this class is to enable all sorts of clean ups -/// when the destructor is called and this proper synchronization is -/// required at that time. -/// -/// Instances of this class are intended to be created as automatic stack -/// variables in which case destructor calls don't require any additional -/// locking, provided that the lock (if any) is managed with RAII helpers. -class JSI_EXPORT Scope { - public: - explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {} - ~Scope() { - rt_.popScope(prv_); - } - - Scope(const Scope&) = delete; - Scope(Scope&&) = delete; - - Scope& operator=(const Scope&) = delete; - Scope& operator=(Scope&&) = delete; - - template - static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) { - Scope s(rt); - return f(); - } - - private: - Runtime& rt_; - Runtime::ScopeState* prv_; -}; - -/// Base class for jsi exceptions -class JSI_EXPORT JSIException : public std::exception { - protected: - JSIException() {} - JSIException(std::string what) : what_(std::move(what)) {} - - public: - JSIException(const JSIException&) = default; - - virtual const char* what() const noexcept override { - return what_.c_str(); - } - - virtual ~JSIException() override; - - protected: - std::string what_; -}; - -/// This exception will be thrown by API functions on errors not related to -/// JavaScript execution. -class JSI_EXPORT JSINativeException : public JSIException { - public: - JSINativeException(std::string what) : JSIException(std::move(what)) {} - - JSINativeException(const JSINativeException&) = default; - - virtual ~JSINativeException(); -}; - -/// This exception will be thrown by API functions whenever a JS -/// operation causes an exception as described by the spec, or as -/// otherwise described. -class JSI_EXPORT JSError : public JSIException { - public: - /// Creates a JSError referring to provided \c value - JSError(Runtime& r, Value&& value); - - /// Creates a JSError referring to new \c Error instance capturing current - /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, std::string message); - - /// Creates a JSError referring to new \c Error instance capturing current - /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, const char* message) - : JSError(rt, std::string(message)) {} - - /// Creates a JSError referring to a JavaScript Object having message and - /// stack properties set to provided values. - JSError(Runtime& rt, std::string message, std::string stack); - - /// Creates a JSError referring to provided value and what string - /// set to provided message. This argument order is a bit weird, - /// but necessary to avoid ambiguity with the above. - JSError(std::string what, Runtime& rt, Value&& value); - - /// Creates a JSError referring to the provided value, message and stack. This - /// constructor does not take a Runtime parameter, and therefore cannot result - /// in recursively invoking the JSError constructor. - JSError(Value&& value, std::string message, std::string stack); - - JSError(const JSError&) = default; - - virtual ~JSError(); - - const std::string& getStack() const { - return stack_; - } - - const std::string& getMessage() const { - return message_; - } - - const jsi::Value& value() const { - assert(value_); - return *value_; - } - - private: - // This initializes the value_ member and does some other - // validation, so it must be called by every branch through the - // constructors. - void setValue(Runtime& rt, Value&& value); - - // This needs to be on the heap, because throw requires the object - // be copyable, and Value is not. - std::shared_ptr value_; - std::string message_; - std::string stack_; -}; - -/// Helper function to cast the object pointed to by \p ptr into an interface -/// specified by \c U. If cast is successful, return a pointer to the object -/// as a raw pointer of \c U. Otherwise, return nullptr. -/// The returned interface same lifetime as the object referenced by \p ptr. -template -U* castInterface(T* ptr) { - if (ptr) { - return static_cast(ptr->castInterface(U::uuid)); - } - return nullptr; -} - -/// Helper function to cast the object managed by the shared_ptr \p ptr into an -/// interface specified by \c U. If the cast is successful, return a shared_ptr -/// of type \c U to the object. Otherwise, return an empty pointer. -/// The returned shared_ptr shares ownership of the object with \p ptr. -template -std::shared_ptr dynamicInterfaceCast(T&& ptr) { - auto* p = ptr->castInterface(U::uuid); - U* res = static_cast(p); - if (res) { - return std::shared_ptr(std::forward(ptr), res); - } - return nullptr; -} - -} // namespace jsi -} // namespace facebook - -#include diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsilib.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsilib.h deleted file mode 100644 index c94de89f..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/jsilib.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace facebook { -namespace jsi { - -class FileBuffer : public Buffer { - public: - FileBuffer(const std::string& path); - ~FileBuffer() override; - - size_t size() const override { - return size_; - } - - const uint8_t* data() const override { - return data_; - } - - private: - size_t size_; - uint8_t* data_; -}; - -// A trivial implementation of PreparedJavaScript that simply stores the source -// buffer and URL. -class SourceJavaScriptPreparation final : public jsi::PreparedJavaScript, - public jsi::Buffer { - std::shared_ptr buf_; - std::string sourceURL_; - - public: - SourceJavaScriptPreparation( - std::shared_ptr buf, - std::string sourceURL) - : buf_(std::move(buf)), sourceURL_(std::move(sourceURL)) {} - - const std::string& sourceURL() const { - return sourceURL_; - } - - size_t size() const override { - return buf_->size(); - } - const uint8_t* data() const override { - return buf_->data(); - } -}; - -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/test/testlib.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/test/testlib.h deleted file mode 100644 index b56d41b8..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/test/testlib.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -#include -#include - -namespace facebook { -namespace jsi { - -class Runtime; - -using RuntimeFactory = std::function()>; - -std::vector runtimeGenerators(); - -class JSITestBase : public ::testing::TestWithParam { - public: - JSITestBase() : factory(GetParam()), runtime(factory()), rt(*runtime) {} - - Value eval(const char* code) { - return rt.global().getPropertyAsFunction(rt, "eval").call(rt, code); - } - - Function function(const std::string& code) { - return eval(("(" + code + ")").c_str()).getObject(rt).getFunction(rt); - } - - bool checkValue(const Value& value, const std::string& jsValue) { - return function("function(value) { return value == " + jsValue + "; }") - .call(rt, std::move(value)) - .getBool(); - } - - RuntimeFactory factory; - std::shared_ptr runtime; - Runtime& rt; -}; -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/threadsafe.h b/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/threadsafe.h deleted file mode 100644 index cb10a335..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/include_shermes/jsi/threadsafe.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include - -namespace facebook { -namespace jsi { - -class ThreadSafeRuntime : public Runtime { - public: - virtual void lock() const = 0; - virtual void unlock() const = 0; - virtual Runtime& getUnsafeRuntime() = 0; -}; - -namespace detail { - -template -struct WithLock { - L lock; - WithLock(R& r) : lock(r) {} - void before() { - lock.lock(); - } - void after() { - lock.unlock(); - } -}; - -// The actual implementation of a given ThreadSafeRuntime. It's parameterized -// by: -// -// - R: The actual Runtime type that this wraps -// - L: A lock type that has three members: -// - L(R& r) // ctor -// - void lock() -// - void unlock() -template -class ThreadSafeRuntimeImpl final - : public WithRuntimeDecorator, R, ThreadSafeRuntime> { - public: - template - ThreadSafeRuntimeImpl(Args&&... args) - : WithRuntimeDecorator, R, ThreadSafeRuntime>( - unsafe_, - lock_), - unsafe_(std::forward(args)...), - lock_(unsafe_) {} - - R& getUnsafeRuntime() override { - return WithRuntimeDecorator, R, ThreadSafeRuntime>::plain(); - } - - void lock() const override { - lock_.before(); - } - - void unlock() const override { - lock_.after(); - } - - private: - R unsafe_; - mutable WithLock lock_; -}; - -} // namespace detail - -} // namespace jsi -} // namespace facebook diff --git a/test-app/runtime/src/main/cpp/napi/hermes/js_runtime.h b/test-app/runtime/src/main/cpp/napi/hermes/js_runtime.h deleted file mode 100644 index 71bad7bd..00000000 --- a/test-app/runtime/src/main/cpp/napi/hermes/js_runtime.h +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef SRC_JS_RUNTIME_API_H_ -#define SRC_JS_RUNTIME_API_H_ - -#include "js_native_api.h" - -// -// Node-API extensions required for JavaScript engine hosting. -// -// It is a very early version of the APIs which we consider to be experimental. -// These APIs are not stable yet and are subject to change while we continue -// their development. After some time we will stabilize the APIs and make them -// "officially stable". -// - -#define JSR_API NAPI_EXTERN napi_status NAPI_CDECL - -EXTERN_C_START - -typedef struct jsr_runtime_s* jsr_runtime; -typedef struct jsr_config_s* jsr_config; -typedef struct jsr_prepared_script_s* jsr_prepared_script; -typedef struct jsr_napi_env_scope_s* jsr_napi_env_scope; - -typedef void(NAPI_CDECL* jsr_data_delete_cb)(void* data, void* deleter_data); - -//============================================================================= -// jsr_runtime -//============================================================================= - -JSR_API jsr_create_runtime(jsr_config config, jsr_runtime* runtime); -JSR_API jsr_delete_runtime(jsr_runtime runtime); -JSR_API jsr_runtime_get_node_api_env(jsr_runtime runtime, napi_env* env); - -//============================================================================= -// jsr_config -//============================================================================= - -JSR_API jsr_create_config(jsr_config* config); -JSR_API jsr_delete_config(jsr_config config); - -JSR_API jsr_config_enable_inspector(jsr_config config, bool value); -JSR_API jsr_config_set_inspector_runtime_name(jsr_config config, - const char* name); -JSR_API jsr_config_set_inspector_port(jsr_config config, uint16_t port); -JSR_API jsr_config_set_inspector_break_on_start(jsr_config config, bool value); - -JSR_API jsr_config_enable_gc_api(jsr_config config, bool value); - -//============================================================================= -// jsr_config task runner -//============================================================================= - -// A callback to run task -typedef void(NAPI_CDECL* jsr_task_run_cb)(void* task_data); - -// A callback to post task to the task runner -typedef void(NAPI_CDECL* jsr_task_runner_post_task_cb)( -void* task_runner_data, -void* task_data, - jsr_task_run_cb task_run_cb, -jsr_data_delete_cb task_data_delete_cb, -void* deleter_data); - -JSR_API jsr_config_set_task_runner( - jsr_config config, - void* task_runner_data, - jsr_task_runner_post_task_cb task_runner_post_task_cb, - jsr_data_delete_cb task_runner_data_delete_cb, - void* deleter_data); - -//============================================================================= -// jsr_config script cache -//============================================================================= - -typedef void(NAPI_CDECL* jsr_script_cache_load_cb)( -void* script_cache_data, -const char* source_url, - uint64_t source_hash, -const char* runtime_name, - uint64_t runtime_version, -const char* cache_tag, -const uint8_t** buffer, - size_t* buffer_size, -jsr_data_delete_cb* buffer_delete_cb, -void** deleter_data); - -typedef void(NAPI_CDECL* jsr_script_cache_store_cb)( -void* script_cache_data, -const char* source_url, - uint64_t source_hash, -const char* runtime_name, - uint64_t runtime_version, -const char* cache_tag, -const uint8_t* buffer, - size_t buffer_size, -jsr_data_delete_cb buffer_delete_cb, -void* deleter_data); - -JSR_API jsr_config_set_script_cache( - jsr_config config, - void* script_cache_data, - jsr_script_cache_load_cb script_cache_load_cb, - jsr_script_cache_store_cb script_cache_store_cb, - jsr_data_delete_cb script_cache_data_delete_cb, - void* deleter_data); - -//============================================================================= -// napi_env scope -//============================================================================= - -// Opens the napi_env scope in the current thread. -// Calling Node-API functions without the opened scope may cause a failure. -// The scope must be closed by the jsr_close_napi_env_scope call. -JSR_API jsr_open_napi_env_scope(napi_env env, jsr_napi_env_scope* scope); - -// Closes the napi_env scope in the current thread. It must match to the -// jsr_open_napi_env_scope call. -JSR_API jsr_close_napi_env_scope(napi_env env, jsr_napi_env_scope scope); - -//============================================================================= -// Additional functions to implement JSI -//============================================================================= - -// To implement JSI description() -JSR_API jsr_get_description(napi_env env, const char** result); - -// To implement JSI drainMicrotasks() -JSR_API -jsr_drain_microtasks(napi_env env, int32_t max_count_hint, bool* result); - -// To implement JSI isInspectable() -JSR_API jsr_is_inspectable(napi_env env, bool* result); - -//============================================================================= -// Script preparing and running. -// -// Script is usually converted to byte code, or in other words - prepared - for -// execution. Then, we can run the prepared script. -//============================================================================= - -// Run script with source URL. -JSR_API jsr_run_script(napi_env env, - napi_value source, - const char* source_url, - napi_value* result); - -// Prepare the script for running. -JSR_API jsr_create_prepared_script(napi_env env, - const uint8_t* script_data, - size_t script_length, - jsr_data_delete_cb script_delete_cb, - void* deleter_data, - const char* source_url, - jsr_prepared_script* result); - -// Delete the prepared script. -JSR_API jsr_delete_prepared_script(napi_env env, - jsr_prepared_script prepared_script); - -// Run the prepared script. -JSR_API jsr_prepared_script_run(napi_env env, - jsr_prepared_script prepared_script, - napi_value* result); - -//============================================================================= -// Functions to support unit tests. -//============================================================================= - -// Provides a hint to run garbage collection. -// It is typically used for unit tests. -// It requires enabling GC by calling jsr_config_enable_gc_api. -JSR_API jsr_collect_garbage(napi_env env); - -// Checks if the environment has an unhandled promise rejection. -JSR_API jsr_has_unhandled_promise_rejection(napi_env env, bool* result); - -// Gets and clears the last unhandled promise rejection. -JSR_API jsr_get_and_clear_last_unhandled_promise_rejection(napi_env env, - napi_value* result); - -EXTERN_C_END - -#endif // !SRC_JS_RUNTIME_API_H_ diff --git a/test-app/runtime/src/main/cpp/napi/hermes/jsr.cpp b/test-app/runtime/src/main/cpp/napi/hermes/jsr.cpp index ac10bb05..dfbcf404 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/jsr.cpp +++ b/test-app/runtime/src/main/cpp/napi/hermes/jsr.cpp @@ -1,40 +1,32 @@ #include "jsr.h" -#include "js_runtime.h" #include "File.h" +#include "bytecode_container.h" +#include "NativeScriptAssert.h" +#include +#include -using namespace facebook::jsi; std::unordered_map JSR::env_to_jsr_cache; -typedef struct napi_runtime__ { +typedef struct jsr_ns_runtime__ { JSR *hermes; -} napi_runtime__; - - -#ifdef __SHERMES__ -class TaskRunner : public ::hermes::node_api::TaskRunner { -public: - void post(std::unique_ptr<::hermes::node_api::Task> task) noexcept override { - printf("%s", "HERMES NAPI CALLBACK POSTED"); - } -}; -#endif +} jsr_ns_runtime__; JSR::JSR() { - #ifdef __SHERMES__ - hermes::vm::RuntimeConfig config = - hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue(true).withES6BlockScoping(true).withEnableAsyncGenerators(true).build(); - #else - hermes::vm::RuntimeConfig config = - hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue(true).build(); - #endif - + hermes::vm::RuntimeConfig config = + hermes::vm::RuntimeConfig::Builder() + .withMicrotaskQueue(true) + .withES6BlockScoping(true) + .withEnableAsyncGenerators(true) + .withAsyncBreakCheckInEval(true) + .build(); + threadSafeRuntime = facebook::hermes::makeThreadSafeHermesRuntime(config); rt = (facebook::hermes::HermesRuntime *) &threadSafeRuntime->getUnsafeRuntime(); } -napi_status js_create_runtime(napi_runtime *runtime) { +napi_status js_create_runtime(jsr_ns_runtime *runtime) { if (runtime == nullptr) return napi_invalid_arg; - *runtime = new napi_runtime__(); + *runtime = new jsr_ns_runtime__(); (*runtime)->hermes = new JSR(); return napi_ok; @@ -60,13 +52,18 @@ napi_status js_unlock_env(napi_env env) { return napi_ok; } -napi_status js_create_napi_env(napi_env *env, napi_runtime runtime) { +napi_status js_create_napi_env(napi_env *env, jsr_ns_runtime runtime) { if (env == nullptr) return napi_invalid_arg; - #ifdef __SHERMES__ - *env = ::hermes::node_api::createNodeApiEnv(runtime->hermes->rt->getVMRuntimeUnsafe(), std::make_shared(), [](napi_env env, napi_value value) {}, 8); - #else - runtime->hermes->rt->createNapiEnv(env); - #endif + + // Extract the underlying hermes::vm::Runtime from the JSI HermesRuntime via + // the IHermes interface, then create the Node-API env on top of it. This is + // the same path Hermes' own tools (repl, test-runner, napi-runner) use and + // relies only on symbols exported by libhermesvm.so. + void *vmRuntime = + facebook::jsi::castInterface(runtime->hermes->rt) + ->getVMRuntimeUnsafe(); + *env = hermes_napi_create_env(vmRuntime); + JSR::env_to_jsr_cache.insert(std::make_pair(*env, runtime->hermes)); return napi_ok; } @@ -76,11 +73,13 @@ napi_status js_set_runtime_flags(const char *flags) { } napi_status js_free_napi_env(napi_env env) { + // The env is owned by the hermes::vm::Runtime and is torn down when the + // Runtime is destroyed (see js_free_runtime); we only drop our cache entry. JSR::env_to_jsr_cache.erase(env); return napi_ok; } -napi_status js_free_runtime(napi_runtime runtime) { +napi_status js_free_runtime(jsr_ns_runtime runtime) { if (runtime == nullptr) return napi_invalid_arg; runtime->hermes->threadSafeRuntime.reset(); runtime->hermes->rt = nullptr; @@ -91,29 +90,79 @@ napi_status js_free_runtime(napi_runtime runtime) { } +// Hermes bytecode (HBC) magic, first 8 bytes little-endian (0x1F1903C103BC1FC6). +// Hermes stores raw HBC (no NativeScript container), so the whole file is the +// bytecode buffer. +static const uint8_t kHermesMagic[8] = {0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f}; + +napi_status js_run_bytecode_file(napi_env env, const char *file, napi_value *result) { + std::string path; + if (!nsbc::ResolvePath(file, path)) { + DEBUG_WRITE("[bytecode] Unable to resolve file: %s", path.c_str()); + return napi_cannot_run_js; + } + if (!nsbc::HasMagic(path, reinterpret_cast(kHermesMagic))) { + DEBUG_WRITE("[bytecode] Unable to find hermes header: %s", path.c_str()); + return napi_cannot_run_js; + } + +int length = 0; + auto data = tns::File::ReadBinary(path, length); + if (!data) return napi_cannot_run_js; + + DEBUG_WRITE("[bytecode] loading Hermes HBC bytecode: %s (%d bytes)", file, length); + + hermes_bytecode_flags flags{}; + flags.struct_size = sizeof(flags); + // App modules live for the whole runtime lifetime, so keep the bytecode + // resident and let Hermes reference it zero-copy for faster loads. + flags.persistent = true; + // Hermes takes ownership of the buffer and frees it via the finalizer. + return hermes_run_bytecode( + env, static_cast(data), static_cast(length), + [](const uint8_t *d, size_t, void *) { delete[] const_cast(d); }, + nullptr, file, &flags, result); +} + napi_status js_execute_script(napi_env env, napi_value script, const char *file, napi_value *result) { - #ifdef __SHERMES__ - return napi_run_script_source(env, script, file, result);; - #else - return jsr_run_script(env, script, file, result); - #endif + // Pull the UTF-8 source out of the napi string value and compile+run it via + // the Hermes NAPI entry point so we can attach the source URL for stack + // traces. + size_t len = 0; + napi_status status = napi_get_value_string_utf8(env, script, nullptr, 0, &len); + if (status != napi_ok) return status; + + DEBUG_WRITE("[script] loading script: %s", file); + + uint8_t *source = new uint8_t[len + 1]; + status = napi_get_value_string_utf8(env, script, reinterpret_cast(source), + len + 1, &len); + if (status != napi_ok) { + delete[] source; + return status; + } + + hermes_run_script_flags flags{}; + flags.struct_size = sizeof(flags); + // Pass size = len + 1 so the trailing '\0' lets Hermes run the source + // zero-copy. Hermes takes ownership of the buffer and frees it via the + // finalizer below. + return hermes_run_script( + env, source, len + 1, + [](const uint8_t *data, size_t, void *) { delete[] const_cast(data); }, + nullptr, file, &flags, result); } napi_status js_execute_pending_jobs(napi_env env) { - #ifdef __SHERMES__ auto itFound = JSR::env_to_jsr_cache.find(env); if (itFound == JSR::env_to_jsr_cache.end()) { return napi_invalid_arg; } itFound->second->rt->drainMicrotasks(); return napi_ok; - #else - bool result; - return jsr_drain_microtasks(env, 0, &result); - #endif } napi_status js_get_engine_ptr(napi_env env, int64_t *engine_ptr) { @@ -133,16 +182,22 @@ napi_status js_cache_script(napi_env env, const char *source, const char *file) napi_status js_run_cached_script(napi_env env, const char *file, napi_value script, void *cache, napi_value *result) { int length = 0; + // tns::File::ReadBinary allocates with new uint8_t[length]. auto data = tns::File::ReadBinary(file, length); if (!data) { return napi_cannot_run_js; } - return napi_run_bytecode(env, data, length, file, result); + hermes_bytecode_flags flags{}; + flags.struct_size = sizeof(flags); + // Hermes takes ownership of the buffer and frees it via the finalizer. + return hermes_run_bytecode( + env, static_cast(data), static_cast(length), + [](const uint8_t *d, size_t, void *) { delete[] const_cast(d); }, + nullptr, file, &flags, result); } napi_status js_get_runtime_version(napi_env env, napi_value *version) { napi_create_string_utf8(env, "Hermes", NAPI_AUTO_LENGTH, version); return napi_ok; } - diff --git a/test-app/runtime/src/main/cpp/napi/hermes/jsr.h b/test-app/runtime/src/main/cpp/napi/hermes/jsr.h index 62d39124..d862174e 100644 --- a/test-app/runtime/src/main/cpp/napi/hermes/jsr.h +++ b/test-app/runtime/src/main/cpp/napi/hermes/jsr.h @@ -5,14 +5,22 @@ #ifndef TEST_APP_JSR_H #define TEST_APP_JSR_H -#include "hermes/hermes.h" -#ifdef __SHERMES__ -#include "hermes/hermes_node_api.h" -#else -#include "hermes/hermes_api.h" -#endif +#include +#include +#include +// hermes.h transitively provides everything we need on the runtime side: +// - facebook::hermes::makeThreadSafeHermesRuntime / HermesRuntime +// - facebook::hermes::IHermes (via ) +// - facebook::jsi::castInterface (via ) +// - hermes::vm::RuntimeConfig (via ) +#include "hermes/hermes.h" #include "jsi/threadsafe.h" + +// Node-API surface exported by libhermesvm.so: hermes_napi_create_env, +// hermes_run_script, hermes_run_bytecode plus the standard napi_* functions. +#include "napi/hermes_napi.h" + #include "jsr_common.h" class JSR { @@ -21,6 +29,11 @@ class JSR { std::unique_ptr threadSafeRuntime; facebook::hermes::HermesRuntime* rt; std::recursive_mutex js_mutex; + // Depth of nested JS scopes entered from the host (see NapiScope). Hermes is + // configured with an explicit microtask queue, so promise jobs only run when + // we drain them; we drain once this returns to 0, i.e. when the native call + // stack has fully unwound back out of JS. + int jsEnterState = 0; void lock() { threadSafeRuntime->lock(); js_mutex.lock(); @@ -39,6 +52,11 @@ class NapiScope { : env_(env) { js_lock_env(env_); + auto it = JSR::env_to_jsr_cache.find(env_); + jsr_ = it != JSR::env_to_jsr_cache.end() ? it->second : nullptr; + if (jsr_) { + jsr_->jsEnterState++; + } if (openHandle) { napi_open_handle_scope(env_, &napiHandleScope_); } else { @@ -47,6 +65,18 @@ class NapiScope { } ~NapiScope() { + // Drain the microtask queue only when the outermost JS scope unwinds so + // that promise continuations (async/await) run — mirroring how a JS + // engine empties its job queue once control returns to the host. Draining + // at a nested depth would run continuations while JS is still on the + // stack. A throwing microtask must never escape a destructor. + if (jsr_ && --jsr_->jsEnterState <= 0) { + jsr_->jsEnterState = 0; + try { + js_execute_pending_jobs(env_); + } catch (...) { + } + } if (napiHandleScope_) { napi_close_handle_scope(env_, napiHandleScope_); } @@ -56,6 +86,7 @@ class NapiScope { private: napi_env env_; napi_handle_scope napiHandleScope_; + JSR* jsr_ = nullptr; }; #define JSEnterScope diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APICallbackFunction.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APICallbackFunction.h new file mode 100644 index 00000000..05ee1410 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APICallbackFunction.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013-2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef APICallbackFunction_h +#define APICallbackFunction_h + +#include "APICast.h" +#include "Error.h" +#include "JSCallbackConstructor.h" +#include "JSLock.h" +#include + +namespace JSC { + +struct APICallbackFunction { + template static EncodedJSValue callImpl(JSGlobalObject*, CallFrame*); + template static EncodedJSValue constructImpl(JSGlobalObject*, CallFrame*); +}; + +template +EncodedJSValue APICallbackFunction::callImpl(JSGlobalObject* globalObject, CallFrame* callFrame) +{ + VM& vm = getVM(globalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + JSContextRef execRef = toRef(globalObject); + JSObjectRef functionRef = toRef(callFrame->jsCallee()); + JSObjectRef thisObjRef = toRef(jsCast(callFrame->thisValue().toThis(globalObject, ECMAMode::sloppy()))); + + int argumentCount = static_cast(callFrame->argumentCount()); + Vector arguments(argumentCount, [&](size_t i) { + return toRef(globalObject, callFrame->uncheckedArgument(i)); + }); + + JSValueRef exception = nullptr; + JSValueRef result; + { + JSLock::DropAllLocks dropAllLocks(globalObject); + result = jsCast(toJS(functionRef))->functionCallback()(execRef, functionRef, thisObjRef, argumentCount, arguments.span().data(), &exception); + } + if (exception) { + throwException(globalObject, scope, toJS(globalObject, exception)); + return JSValue::encode(jsUndefined()); + } + + // result must be a valid JSValue. + if (!result) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(toJS(globalObject, result)); +} + +template +EncodedJSValue APICallbackFunction::constructImpl(JSGlobalObject* globalObject, CallFrame* callFrame) +{ + VM& vm = getVM(globalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue callee = callFrame->jsCallee(); + T* constructor = jsCast(callFrame->jsCallee()); + JSContextRef ctx = toRef(globalObject); + JSObjectRef constructorRef = toRef(constructor); + + JSObjectCallAsConstructorCallback callback = constructor->constructCallback(); + if (callback) { + JSValue prototype; + JSValue newTarget = callFrame->newTarget(); + // If we are doing a derived class construction get the .prototype property off the new target first so we behave closer to normal JS. + if (newTarget != constructor) { + prototype = newTarget.get(globalObject, vm.propertyNames->prototype); + RETURN_IF_EXCEPTION(scope, { }); + } + + size_t argumentCount = callFrame->argumentCount(); + Vector arguments(argumentCount, [&](size_t i) { + return toRef(globalObject, callFrame->uncheckedArgument(i)); + }); + + JSValueRef exception = nullptr; + JSObjectRef result; + { + JSLock::DropAllLocks dropAllLocks(globalObject); + result = callback(ctx, constructorRef, argumentCount, arguments.span().data(), &exception); + } + + if (exception) { + throwException(globalObject, scope, toJS(globalObject, exception)); + return JSValue::encode(jsUndefined()); + } + // result must be a valid JSValue. + if (!result) + return throwVMTypeError(globalObject, scope); + + JSObject* newObject = toJS(result); + // This won't trigger proxy traps on newObject's prototype handler but that's probably desirable here anyway. + if (newTarget != constructor && newObject->getPrototypeDirect() == constructor->get(globalObject, vm.propertyNames->prototype)) { + RETURN_IF_EXCEPTION(scope, { }); + newObject->setPrototype(vm, globalObject, prototype); + RETURN_IF_EXCEPTION(scope, { }); + } + + return JSValue::encode(newObject); + } + + return JSValue::encode(toJS(JSObjectMake(ctx, jsCast(callee)->classRef(), nullptr))); +} + +} // namespace JSC + +#endif // APICallbackFunction_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APICast.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APICast.h similarity index 59% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APICast.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APICast.h index b2f3888b..69bea2f9 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APICast.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APICast.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2006-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,17 +23,22 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef APICast_h -#define APICast_h +#pragma once +#include + +WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN + +#include "HeapCellInlines.h" +#include "Integrity.h" #include "JSAPIValueWrapper.h" #include "JSCJSValue.h" #include "JSCJSValueInlines.h" -#include "JSGlobalObject.h" -#include "HeapCellInlines.h" + +WTF_ALLOW_UNSAFE_BUFFER_USAGE_END namespace JSC { - class ExecState; + class CallFrame; class PropertyNameArray; class VM; class JSObject; @@ -49,26 +54,26 @@ typedef struct OpaqueJSValue* JSObjectRef; /* Opaque typing convenience methods */ -inline JSC::ExecState* toJS(JSContextRef c) +inline JSC::JSGlobalObject* toJS(JSContextRef context) { - ASSERT(c); - return reinterpret_cast(const_cast(c)); + ASSERT(context); + return JSC::Integrity::audit(reinterpret_cast(const_cast(context))); } -inline JSC::ExecState* toJS(JSGlobalContextRef c) +inline JSC::JSGlobalObject* toJS(JSGlobalContextRef context) { - ASSERT(c); - return reinterpret_cast(c); + ASSERT(context); + return JSC::Integrity::audit(reinterpret_cast(context)); } inline JSC::JSGlobalObject* toJSGlobalObject(JSGlobalContextRef context) { - return toJS(context)->lexicalGlobalObject(); + return toJS(context); } -inline JSC::JSValue toJS(JSC::ExecState* exec, JSValueRef v) +inline JSC::JSValue toJS(JSC::JSGlobalObject* globalObject, JSValueRef v) { - ASSERT_UNUSED(exec, exec); + ASSERT_UNUSED(globalObject, globalObject); #if !CPU(ADDRESS64) JSC::JSCell* jsCell = reinterpret_cast(const_cast(v)); if (!jsCell) @@ -79,42 +84,53 @@ inline JSC::JSValue toJS(JSC::ExecState* exec, JSValueRef v) else result = jsCell; #else - JSC::JSValue result = bitwise_cast(v); + JSC::JSValue result = std::bit_cast(v); #endif if (!result) return JSC::jsNull(); - if (result.isCell()) - RELEASE_ASSERT(result.asCell()->methodTable(exec->vm())); + if (result.isCell()) { + JSC::Integrity::audit(result.asCell()); + RELEASE_ASSERT(result.asCell()->methodTable()); + } return result; } -inline JSC::JSValue toJSForGC(JSC::ExecState* exec, JSValueRef v) +#if CPU(ADDRESS64) +inline JSC::JSValue toJS(JSValueRef value) +{ + return JSC::Integrity::audit(std::bit_cast(value)); +} +#endif + +inline JSC::JSValue toJSForGC(JSC::JSGlobalObject* globalObject, JSValueRef v) { - ASSERT_UNUSED(exec, exec); + ASSERT_UNUSED(globalObject, globalObject); #if !CPU(ADDRESS64) JSC::JSCell* jsCell = reinterpret_cast(const_cast(v)); if (!jsCell) return JSC::JSValue(); JSC::JSValue result = jsCell; #else - JSC::JSValue result = bitwise_cast(v); + JSC::JSValue result = std::bit_cast(v); #endif - if (result && result.isCell()) - RELEASE_ASSERT(result.asCell()->methodTable(exec->vm())); + if (result && result.isCell()) { + JSC::Integrity::audit(result.asCell()); + RELEASE_ASSERT(result.asCell()->methodTable()); + } return result; } // Used in JSObjectGetPrivate as that may be called during finalization inline JSC::JSObject* uncheckedToJS(JSObjectRef o) { - return reinterpret_cast(o); + return JSC::Integrity::audit(reinterpret_cast(o)); } inline JSC::JSObject* toJS(JSObjectRef o) { JSC::JSObject* object = uncheckedToJS(o); if (object) - RELEASE_ASSERT(object->methodTable(object->vm())); + RELEASE_ASSERT(object->methodTable()); return object; } @@ -125,7 +141,7 @@ inline JSC::PropertyNameArray* toJS(JSPropertyNameAccumulatorRef a) inline JSC::VM* toJS(JSContextGroupRef g) { - return reinterpret_cast(const_cast(g)); + return JSC::Integrity::audit(reinterpret_cast(const_cast(g))); } inline JSValueRef toRef(JSC::VM& vm, JSC::JSValue v) @@ -139,34 +155,40 @@ inline JSValueRef toRef(JSC::VM& vm, JSC::JSValue v) return reinterpret_cast(v.asCell()); #else UNUSED_PARAM(vm); - return bitwise_cast(v); + return std::bit_cast(JSC::Integrity::audit(v)); #endif } -inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v) +inline JSValueRef toRef(JSC::JSGlobalObject* globalObject, JSC::JSValue v) { - return toRef(exec->vm(), v); + return toRef(getVM(globalObject), v); } +#if CPU(ADDRESS64) +inline JSValueRef toRefWithoutGlobalObject(JSC::JSValue v) +{ + return std::bit_cast(JSC::Integrity::audit(v)); +} +#endif + inline JSObjectRef toRef(JSC::JSObject* o) { - return reinterpret_cast(o); + return reinterpret_cast(JSC::Integrity::audit(o)); } inline JSObjectRef toRef(const JSC::JSObject* o) { - return reinterpret_cast(const_cast(o)); + return reinterpret_cast(JSC::Integrity::audit(const_cast(o))); } -inline JSContextRef toRef(JSC::ExecState* e) +inline JSContextRef toRef(JSC::JSGlobalObject* globalObject) { - return reinterpret_cast(e); + return reinterpret_cast(JSC::Integrity::audit(globalObject)); } -inline JSGlobalContextRef toGlobalRef(JSC::ExecState* e) +inline JSGlobalContextRef toGlobalRef(JSC::JSGlobalObject* globalObject) { - ASSERT(e == e->lexicalGlobalObject()->globalExec()); - return reinterpret_cast(e); + return reinterpret_cast(JSC::Integrity::audit(globalObject)); } inline JSPropertyNameAccumulatorRef toRef(JSC::PropertyNameArray* l) @@ -176,7 +198,5 @@ inline JSPropertyNameAccumulatorRef toRef(JSC::PropertyNameArray* l) inline JSContextGroupRef toRef(JSC::VM* g) { - return reinterpret_cast(g); + return reinterpret_cast(JSC::Integrity::audit(g)); } - -#endif // APICast_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APIIntegrityPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APIIntegrityPrivate.h new file mode 100644 index 00000000..38e7e3f4 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APIIntegrityPrivate.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSIntegrityPrivate_h +#define JSIntegrityPrivate_h + +#include "JSContextRef.h" +#include "JSObjectRef.h" +#include "JSValueRef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Audits the integrity of the JSContextRef. +@param ctx The JSContext you want to audit. +@result JSContextRef that was passed in. +@discussion This function will crash if the audit detects any errors. + */ +JS_EXPORT JSContextRef jsAuditJSContextRef(JSContextRef ctx) JSC_API_AVAILABLE(macos(13.0), ios(16.1)); + +/*! +@function +@abstract Audits the integrity of the JSGlobalContextRef. +@param ctx The JSGlobalContextRef you want to audit. +@result JSGlobalContextRef that was passed in. +@discussion This function will crash if the audit detects any errors. + */ +JS_EXPORT JSGlobalContextRef jsAuditJSGlobalContextRef(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(13.0), ios(16.1)); + +/*! +@function +@abstract Audits the integrity of the JSObjectRef. +@param obj The JSObjectRef you want to audit. +@result JSObjectRef that was passed in. +@discussion This function will crash if the audit detects any errors. + */ +JS_EXPORT JSObjectRef jsAuditJSObjectRef(JSObjectRef obj) JSC_API_AVAILABLE(macos(13.0), ios(16.1)); + +/*! +@function +@abstract Audits the integrity of the JSValueRef. +@param value The JSValueRef you want to audit. +@result JSValueRef that was passed in. +@discussion This function will crash if the audit detects any errors. + */ +JS_EXPORT JSValueRef jsAuditJSValueRef(JSValueRef value) JSC_API_AVAILABLE(macos(13.0), ios(16.1)); + +#ifdef __cplusplus +} +#endif + +#endif /* JSIntegrityPrivate_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APIUtils.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APIUtils.h similarity index 74% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APIUtils.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APIUtils.h index 7a5e8ba8..4d0233b2 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APIUtils.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/APIUtils.h @@ -37,28 +37,30 @@ enum class ExceptionStatus { DidNotThrow }; -inline ExceptionStatus handleExceptionIfNeeded(JSC::CatchScope& scope, JSC::ExecState* exec, JSValueRef* returnedExceptionRef) +inline ExceptionStatus handleExceptionIfNeeded(JSC::CatchScope& scope, JSContextRef ctx, JSValueRef* returnedExceptionRef) { - if (UNLIKELY(scope.exception())) { + JSC::JSGlobalObject* globalObject = toJS(ctx); + if (scope.exception()) [[unlikely]] { JSC::Exception* exception = scope.exception(); if (returnedExceptionRef) - *returnedExceptionRef = toRef(exec, exception->value()); + *returnedExceptionRef = toRef(globalObject, exception->value()); scope.clearException(); #if ENABLE(REMOTE_INSPECTOR) - scope.vm().vmEntryGlobalObject(exec)->inspectorController().reportAPIException(exec, exception); + globalObject->inspectorController().reportAPIException(globalObject, exception); #endif return ExceptionStatus::DidThrow; } return ExceptionStatus::DidNotThrow; } -inline void setException(JSC::ExecState* exec, JSValueRef* returnedExceptionRef, JSC::JSValue exception) +inline void setException(JSContextRef ctx, JSValueRef* returnedExceptionRef, JSC::JSValue exception) { + JSC::JSGlobalObject* globalObject = toJS(ctx); if (returnedExceptionRef) - *returnedExceptionRef = toRef(exec, exception); + *returnedExceptionRef = toRef(globalObject, exception); #if ENABLE(REMOTE_INSPECTOR) - JSC::VM& vm = exec->vm(); - vm.vmEntryGlobalObject(exec)->inspectorController().reportAPIException(exec, JSC::Exception::create(vm, exception)); + JSC::VM& vm = getVM(globalObject); + globalObject->inspectorController().reportAPIException(globalObject, JSC::Exception::create(vm, exception)); #endif } diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ExtraSymbolsForTAPI.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ExtraSymbolsForTAPI.h new file mode 100644 index 00000000..88e1dabe --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ExtraSymbolsForTAPI.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "JSContextRef.h" +#include + +// MARK: JavaScriptCore + +extern "C" JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef); +extern "C" JS_EXPORT void JSSynchronousEdenCollectForDebugging(JSContextRef); + +// MARK: WTF + +namespace WTF { +// Can be removed when https://commits.webkit.org/256555@main is sufficiently +// old that all supported versions of Safari have the change. +WTF_EXPORT_PRIVATE unsigned weakRandomUint32(); +} diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIGlobalObject.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIGlobalObject.h similarity index 56% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIGlobalObject.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIGlobalObject.h index 339e5e27..cdda1fad 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIGlobalObject.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIGlobalObject.h @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Apple Inc. All rights reserved. +/** + * Copyright (C) 2019-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,39 +31,35 @@ OBJC_CLASS JSScript; namespace JSC { -class JSAPIGlobalObject : public JSGlobalObject { +class JSAPIGlobalObject final : public JSGlobalObject { public: using Base = JSGlobalObject; DECLARE_EXPORT_INFO; - static const GlobalObjectMethodTable s_globalObjectMethodTable; - static JSAPIGlobalObject* create(VM& vm, Structure* structure) + static constexpr DestructionMode needsDestruction = NeedsDestruction; + template + static GCClient::IsoSubspace* subspaceFor(VM& vm) { - auto* object = new (NotNull, allocateCell(vm.heap)) JSAPIGlobalObject(vm, structure); - object->finishCreation(vm); - return object; + return vm.apiGlobalObjectSpace(); } - static Structure* createStructure(VM& vm, JSValue prototype) - { - auto* result = Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info()); - result->setTransitionWatchpointIsLikelyToBeFired(true); - return result; - } + static JSAPIGlobalObject* create(VM&, Structure*); + static Structure* createStructure(VM&, JSValue prototype); - static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString* moduleNameValue, JSValue parameters, const SourceOrigin&); - static Identifier moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue); - static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue); - static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue); - static JSValue moduleLoaderEvaluate(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue); + static void reportUncaughtExceptionAtEventLoop(JSGlobalObject*, Exception*); JSValue loadAndEvaluateJSScriptModule(const JSLockHolder&, JSScript *); private: - JSAPIGlobalObject(VM& vm, Structure* structure) - : Base(vm, structure, &s_globalObjectMethodTable) - { } + static const GlobalObjectMethodTable* globalObjectMethodTable(); + JSAPIGlobalObject(VM&, Structure*); + + static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, JSModuleLoader*, JSString* moduleNameValue, JSValue parameters, const SourceOrigin&); + static Identifier moduleLoaderResolve(JSGlobalObject*, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue); + static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, JSModuleLoader*, JSValue, JSValue, JSValue); + static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue); + static JSValue moduleLoaderEvaluate(JSGlobalObject*, JSModuleLoader*, JSValue, JSValue, JSValue, JSValue, JSValue); }; } diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIValueWrapper.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIValueWrapper.h similarity index 67% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIValueWrapper.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIValueWrapper.h index aa26082f..b0ce82c3 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIValueWrapper.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIValueWrapper.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003-2019 Apple Inc. All rights reserved. + * Copyright (C) 2003-2022 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,41 +30,40 @@ namespace JSC { class JSAPIValueWrapper final : public JSCell { - friend JSValue jsAPIValueWrapper(ExecState*, JSValue); + friend JSValue jsAPIValueWrapper(JSGlobalObject*, JSValue); public: - typedef JSCell Base; - static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + using Base = JSCell; + static constexpr unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; - JSValue value() const { return m_value.get(); } - - static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + template + static GCClient::IsoSubspace* subspaceFor(VM& vm) { - return Structure::create(vm, globalObject, prototype, TypeInfo(APIValueWrapperType, OverridesGetPropertyNames), info()); + return vm.apiValueWrapperSpace(); } + JSValue value() const { return m_value.get(); } + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + DECLARE_EXPORT_INFO; static JSAPIValueWrapper* create(VM& vm, JSValue value) { - JSAPIValueWrapper* wrapper = new (NotNull, allocateCell(vm.heap)) JSAPIValueWrapper(vm); - wrapper->finishCreation(vm, value); - return wrapper; - } - -protected: - void finishCreation(VM& vm, JSValue value) - { - Base::finishCreation(vm); - m_value.set(vm, this, value); + JSAPIValueWrapper* wrapper = new (NotNull, allocateCell(vm)) JSAPIValueWrapper(vm, value); + wrapper->finishCreation(vm); ASSERT(!value.isCell()); + return wrapper; } private: - JSAPIValueWrapper(VM& vm) + JSAPIValueWrapper(VM& vm, JSValue value) : JSCell(vm, vm.apiWrapperStructure.get()) + , m_value(value, WriteBarrierEarlyInit) { } + DECLARE_DEFAULT_FINISH_CREATION; + WriteBarrier m_value; }; diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIWrapperObject.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIWrapperObject.h similarity index 85% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIWrapperObject.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIWrapperObject.h index dd874dc6..947eff22 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSAPIWrapperObject.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSAPIWrapperObject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * Copyright (C) 2013-2021 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,12 +33,15 @@ namespace JSC { -class JSAPIWrapperObject : public JSDestructibleObject { +class JSAPIWrapperObject : public JSNonFinalObject { public: - typedef JSDestructibleObject Base; + using Base = JSNonFinalObject; + + template + static void subspaceFor(VM&) { RELEASE_ASSERT_NOT_REACHED(); } void finishCreation(VM&); - static void visitChildren(JSCell*, JSC::SlotVisitor&); + DECLARE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE); void* wrappedObject() { return m_wrappedObject; } void setWrappedObject(void*); diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBase.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBase.h similarity index 88% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBase.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBase.h index 01c1b286..2eaed1ff 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBase.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -145,11 +145,41 @@ JS_EXPORT void JSGarbageCollect(JSContextRef ctx); /* Enable the Objective-C API for platforms with a modern runtime. NOTE: This is duplicated in VM.h. */ #if !defined(JSC_OBJC_API_ENABLED) -#if (defined(__clang__) && defined(__APPLE__) && ((defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && !defined(__i386__)) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE))) +#if (defined(__clang__) && defined(__APPLE__) && (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE))) #define JSC_OBJC_API_ENABLED 1 #else #define JSC_OBJC_API_ENABLED 0 #endif #endif +#if JSC_OBJC_API_ENABLED +#define JSC_CF_ENUM(enumName, ...) \ + typedef CF_ENUM(uint32_t, enumName) { \ + __VA_ARGS__ \ + } +#else +#define JSC_CF_ENUM(enumName, ...) \ + typedef enum { \ + __VA_ARGS__ \ + } enumName +#endif + +#if JSC_OBJC_API_ENABLED +#define JSC_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define JSC_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +#else +#define JSC_ASSUME_NONNULL_BEGIN +#define JSC_ASSUME_NONNULL_END +#endif + +#if JSC_OBJC_API_ENABLED +#define JSC_NULL_UNSPECIFIED _Null_unspecified +#define JSC_NULLABLE _Nullable +#define JSC_NONNULL _Nonnull +#else +#define JSC_NULL_UNSPECIFIED +#define JSC_NULLABLE +#define JSC_NONNULL +#endif + #endif /* JSBase_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBaseInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBaseInternal.h similarity index 84% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBaseInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBaseInternal.h index a274af96..d4559a50 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBaseInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBaseInternal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Apple Inc. All rights reserved. + * Copyright (C) 2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,13 +25,13 @@ #pragma once -#include -#include +#include "JSBase.h" +#include "WebKitAvailability.h" namespace JSC { +class CallFrame; class JSLockHolder; -class ExecState; class SourceCode; } -extern "C" JSValueRef JSEvaluateScriptInternal(const JSC::JSLockHolder&, JSC::ExecState*, JSContextRef, JSObjectRef thisObject, const JSC::SourceCode&, JSValueRef* exception); +extern "C" JSValueRef JSEvaluateScriptInternal(const JSC::JSLockHolder&, JSContextRef, JSObjectRef thisObject, const JSC::SourceCode&, JSValueRef* exception); diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBasePrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBasePrivate.h new file mode 100644 index 00000000..64f0b72a --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBasePrivate.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSBasePrivate_h +#define JSBasePrivate_h + +#include "JSBase.h" +#include "WebKitAvailability.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Reports an object's non-GC memory payload to the garbage collector. +@param ctx The execution context to use. +@param size The payload's size, in bytes. +@discussion Use this function to notify the garbage collector that a GC object +owns a large non-GC memory region. Calling this function will encourage the +garbage collector to collect soon, hoping to reclaim that large non-GC memory +region. +*/ +JS_EXPORT void JSReportExtraMemoryCost(JSContextRef ctx, size_t size) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +JS_EXPORT void JSDisableGCTimer(void); + +#if !defined(__APPLE__) && !defined(WIN32) && !defined(_WIN32) +/*! +@function JSConfigureSignalForGC +@abstract Configure signals for GC in non-Apple and non-Windows platforms. +@param signal The signal number to use. +@result true if the signal is successfully configured, otherwise false. +@discussion Call this function before any of JSC initialization starts. Otherwise, it fails. +*/ +JS_EXPORT bool JSConfigureSignalForGC(int signal); +#endif + +/*! +@function +@abstract Produces an object with various statistics about current memory usage. +@param ctx The execution context to use. +@result An object containing GC heap status data. +@discussion Specifically, the result object has the following integer-valued fields: + heapSize: current size of heap + heapCapacity: current capacity of heap + extraMemorySize: amount of non-GC memory referenced by GC objects (included in heap size / capacity) + objectCount: current count of GC objects + protectedObjectCount: current count of protected GC objects + globalObjectCount: current count of global GC objects + protectedGlobalObjectCount: current count of protected global GC objects + objectTypeCounts: object with GC object types as keys and their current counts as values +*/ +JS_EXPORT JSObjectRef JSGetMemoryUsageStatistics(JSContextRef ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* JSBasePrivate_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBytecodeCache.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBytecodeCache.h new file mode 100644 index 00000000..912b9f19 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSBytecodeCache.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2026 Software Mansion. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSBytecodeCache_h +#define JSBytecodeCache_h + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + @function + @abstract Serialize a top-level program to a JSC bytecode cache on disk. + @discussion Parses and byte-compiles @p source (a classic, non-module program + identified by @p sourceURL) and writes the resulting bytecode cache blob to + @p outputFilePath, truncating any existing contents. The caller owns file + publication semantics: write to a temporary path and rename it into place, and + track staleness (e.g. via the source file's modification time) however it sees + fit. The blob is only meaningful to JSEvaluateProgramWithBytecodeCacheFile and + is validated there before use. + @param ctx The execution context (its VM is used for compilation). + @param source The program source. Must be identical to the source later passed + to JSEvaluateProgramWithBytecodeCacheFile for the cache to be accepted. + @param sourceURL The source URL reported in errors; may be NULL. + @param outputFilePath A NUL-terminated UTF-8 filesystem path to write to. + @param errorMessage On failure, if non-NULL, receives a retained JSStringRef + describing the failure (release it with JSStringRelease). + @result true if the cache was written, false otherwise. + */ +JS_EXPORT bool JSWriteBytecodeCacheForProgram(JSContextRef ctx, JSStringRef source, JSStringRef sourceURL, const char* outputFilePath, JSStringRef* errorMessage); + +/*! + @function + @abstract Evaluate a top-level program, reusing a previously written bytecode + cache when it is still valid. + @discussion Memory-maps the cache blob at @p cacheFilePath and, if it is valid + for this exact @p source, evaluates the program directly from the cached + bytecode, skipping parsing and byte-compilation. If the file is missing, + stale, or incompatible (e.g. produced by a different JSC build), nothing is + evaluated, @p cacheRejected is set to true and NULL is returned; the caller + should then run the source normally and refresh the cache. A genuine runtime + error during evaluation is reported via @p exception (and NULL is returned) + while leaving @p cacheRejected false. + @param ctx The execution context to evaluate in. + @param source The program source (must match the source the cache was made from). + @param sourceURL The source URL reported in errors and stack traces; may be NULL. + @param cacheFilePath A NUL-terminated UTF-8 path to a blob previously produced + by JSWriteBytecodeCacheForProgram. + @param thisObject The object to use as "this", or NULL for undefined. + @param cacheRejected If non-NULL, set to true when the cache could not be used + (miss/stale/incompatible) and false otherwise. + @param exception If non-NULL, receives an exception thrown while running. + @result The program's completion value, or NULL on a cache miss or an exception. + */ +JS_EXPORT JSValueRef JSEvaluateProgramWithBytecodeCacheFile(JSContextRef ctx, JSStringRef source, JSStringRef sourceURL, const char* cacheFilePath, JSValueRef thisObject, bool* cacheRejected, JSValueRef* exception); + +#ifdef __cplusplus +} +#endif + +#endif /* JSBytecodeCache_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCTestRunnerUtils.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCTestRunnerUtils.h similarity index 95% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCTestRunnerUtils.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCTestRunnerUtils.h index c52da524..01c2a80e 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCTestRunnerUtils.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCTestRunnerUtils.h @@ -26,8 +26,8 @@ #ifndef JSCTestRunnerUtils_h #define JSCTestRunnerUtils_h -#include -#include +#include "JSContextRef.h" +#include "JSValueRef.h" namespace JSC { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackConstructor.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackConstructor.h similarity index 66% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackConstructor.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackConstructor.h index 3c31e07f..b6eb7238 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackConstructor.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackConstructor.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,20 +26,30 @@ #ifndef JSCallbackConstructor_h #define JSCallbackConstructor_h -#include "JSDestructibleObject.h" +#include "JSObject.h" #include "JSObjectRef.h" namespace JSC { -class JSCallbackConstructor final : public JSDestructibleObject { +#define JSCALLBACK_CONSTRUCTOR_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("JSCallbackConstructor." #method) method + +class JSCallbackConstructor final : public JSNonFinalObject { public: - typedef JSDestructibleObject Base; - static const unsigned StructureFlags = Base::StructureFlags | ImplementsHasInstance | ImplementsDefaultHasInstance; + using Base = JSNonFinalObject; + static constexpr unsigned StructureFlags = Base::StructureFlags | ImplementsHasInstance | ImplementsDefaultHasInstance; + static constexpr DestructionMode needsDestruction = NeedsDestruction; + + template + static GCClient::IsoSubspace* subspaceFor(VM& vm) + { + return vm.callbackConstructorSpace(); + } - static JSCallbackConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSClassRef classRef, JSObjectCallAsConstructorCallback callback) + static JSCallbackConstructor* create(JSGlobalObject* globalObject, Structure* structure, JSClassRef classRef, JSObjectCallAsConstructorCallback callback) { - VM& vm = exec->vm(); - JSCallbackConstructor* constructor = new (NotNull, allocateCell(vm.heap)) JSCallbackConstructor(globalObject, structure, classRef, callback); + VM& vm = getVM(globalObject); + JSCallbackConstructor* constructor = new (NotNull, allocateCell(vm)) JSCallbackConstructor(globalObject, structure, classRef, callback); constructor->finishCreation(globalObject, classRef); return constructor; } @@ -50,26 +60,27 @@ class JSCallbackConstructor final : public JSDestructibleObject { JSObjectCallAsConstructorCallback callback() const { return m_callback; } DECLARE_INFO; - static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), info()); } -protected: +private: JSCallbackConstructor(JSGlobalObject*, Structure*, JSClassRef, JSObjectCallAsConstructorCallback); void finishCreation(JSGlobalObject*, JSClassRef); -private: friend struct APICallbackFunction; - static ConstructType getConstructData(JSCell*, ConstructData&); + static CallData getConstructData(JSCell*); JSObjectCallAsConstructorCallback constructCallback() { return m_callback; } JSClassRef m_class; - JSObjectCallAsConstructorCallback m_callback; + JSObjectCallAsConstructorCallback JSCALLBACK_CONSTRUCTOR_METHOD(m_callback); }; +#undef JSCALLBACK_CONSTRUCTOR_METHOD + } // namespace JSC #endif // JSCallbackConstructor_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackFunction.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackFunction.h similarity index 87% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackFunction.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackFunction.h index f9e4f963..4c59e260 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackFunction.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackFunction.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2006-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,13 +31,16 @@ namespace JSC { +#define JSCALLBACK_FUNCTION_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("JSCallbackFunction." #method) method + class JSCallbackFunction final : public InternalFunction { friend struct APICallbackFunction; public: typedef InternalFunction Base; template - static IsoSubspace* subspaceFor(VM& vm) + static GCClient::IsoSubspace* subspaceFor(VM& vm) { return vm.callbackFunctionSpace(); } @@ -59,9 +62,11 @@ class JSCallbackFunction final : public InternalFunction { JSObjectCallAsFunctionCallback functionCallback() { return m_callback; } - JSObjectCallAsFunctionCallback m_callback { nullptr }; + JSObjectCallAsFunctionCallback JSCALLBACK_FUNCTION_METHOD(m_callback) { nullptr }; }; +#undef JSCALLBACK_FUNCTION_METHOD + } // namespace JSC #endif // JSCallbackFunction_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackObject.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackObject.h similarity index 55% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackObject.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackObject.h index d0e62528..8baf1e71 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackObject.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackObject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2006-2022 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel * * Redistribution and use in source and binary forms, with or without @@ -27,14 +27,21 @@ #ifndef JSCallbackObject_h #define JSCallbackObject_h +#if JSC_OBJC_API_ENABLED || defined(JSC_GLIB_API_ENABLED) +#include "JSAPIWrapperObject.h" +#endif +#if defined(JSC_GLIB_API_ENABLED) +#include "JSAPIWrapperGlobalObject.h" +#endif #include "JSObjectRef.h" #include "JSValueRef.h" #include "JSObject.h" +#include namespace JSC { struct JSCallbackObjectData { - WTF_MAKE_FAST_ALLOCATED; + WTF_DEPRECATED_MAKE_FAST_ALLOCATED(JSCallbackObjectData); public: JSCallbackObjectData(void* privateData, JSClassRef jsClass) : privateData(privateData) @@ -69,7 +76,10 @@ struct JSCallbackObjectData { m_privateProperties->deletePrivateProperty(propertyName); } - void visitChildren(SlotVisitor& visitor) + DECLARE_VISIT_CHILDREN; + + template + void visitChildren(Visitor& visitor) { JSPrivatePropertyMap* properties = m_privateProperties.get(); if (!properties) @@ -80,7 +90,7 @@ struct JSCallbackObjectData { void* privateData; JSClassRef jsClass; struct JSPrivatePropertyMap { - WTF_MAKE_FAST_ALLOCATED; + WTF_DEPRECATED_MAKE_FAST_ALLOCATED(JSPrivatePropertyMap); public: JSValue getPrivateProperty(const Identifier& propertyName) const { @@ -92,20 +102,21 @@ struct JSCallbackObjectData { void setPrivateProperty(VM& vm, JSCell* owner, const Identifier& propertyName, JSValue value) { - LockHolder locker(m_lock); + Locker locker { m_lock }; WriteBarrier empty; m_propertyMap.add(propertyName.impl(), empty).iterator->value.set(vm, owner, value); } void deletePrivateProperty(const Identifier& propertyName) { - LockHolder locker(m_lock); + Locker locker { m_lock }; m_propertyMap.remove(propertyName.impl()); } - void visitChildren(SlotVisitor& visitor) + template + void visitChildren(Visitor& visitor) { - LockHolder locker(m_lock); + Locker locker { m_lock }; for (auto& pair : m_propertyMap) { if (pair.value) visitor.append(pair.value); @@ -113,7 +124,7 @@ struct JSCallbackObjectData { } private: - typedef HashMap, WriteBarrier, IdentifierRepHash> PrivatePropertyMap; + typedef UncheckedKeyHashMap, WriteBarrier, IdentifierRepHash> PrivatePropertyMap; PrivatePropertyMap m_propertyMap; Lock m_lock; }; @@ -123,43 +134,39 @@ struct JSCallbackObjectData { template class JSCallbackObject final : public Parent { -protected: - JSCallbackObject(ExecState*, Structure*, JSClassRef, void* data); - JSCallbackObject(VM&, JSClassRef, Structure*); - - void finishCreation(ExecState*); - void finishCreation(VM&); - public: - typedef Parent Base; - static const unsigned StructureFlags = Base::StructureFlags | ProhibitsPropertyCaching | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | ImplementsHasInstance | OverridesGetPropertyNames | OverridesGetCallData; + using Base = Parent; + static constexpr unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetOwnSpecialPropertyNames | OverridesGetCallData | OverridesPut | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | ImplementsHasInstance | ProhibitsPropertyCaching | GetOwnPropertySlotMayBeWrongAboutDontEnum; static_assert(!(StructureFlags & ImplementsDefaultHasInstance), "using customHasInstance"); ~JSCallbackObject(); - static JSCallbackObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSClassRef classRef, void* data) + static JSCallbackObject* create(JSGlobalObject* globalObject, Structure* structure, JSClassRef classRef, void* data) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); ASSERT_UNUSED(globalObject, !structure->globalObject() || structure->globalObject() == globalObject); - JSCallbackObject* callbackObject = new (NotNull, allocateCell(vm.heap)) JSCallbackObject(exec, structure, classRef, data); - callbackObject->finishCreation(exec); + JSCallbackObject* callbackObject = new (NotNull, allocateCell(vm)) JSCallbackObject(globalObject, structure, classRef, data); + callbackObject->finishCreation(globalObject); return callbackObject; } static JSCallbackObject* create(VM&, JSClassRef, Structure*); - static const bool needsDestruction; + static const DestructionMode needsDestruction; static void destroy(JSCell* cell) { static_cast(cell)->JSCallbackObject::~JSCallbackObject(); } + template + static GCClient::IsoSubspace* subspaceFor(VM& vm) + { + return subspaceForImpl(vm, mode); + } + void setPrivate(void* data); void* getPrivate(); - // FIXME: We should fix the warnings for extern-template in JSObject template classes: https://bugs.webkit.org/show_bug.cgi?id=161979 - IGNORE_CLANG_WARNINGS_BEGIN("undefined-var-template") DECLARE_INFO; - IGNORE_CLANG_WARNINGS_END JSClassRef classRef() const { return m_callbackObjectData->jsClass; } bool inherits(JSClassRef) const; @@ -183,52 +190,81 @@ class JSCallbackObject final : public Parent { using Parent::methodTable; + static EncodedJSValue callImpl(JSGlobalObject*, CallFrame*); + static EncodedJSValue constructImpl(JSGlobalObject*, CallFrame*); + static EncodedJSValue staticFunctionGetterImpl(JSGlobalObject*, EncodedJSValue, PropertyName); + static EncodedJSValue callbackGetterImpl(JSGlobalObject*, EncodedJSValue, PropertyName); + + DECLARE_VISIT_CHILDREN; + private: - static String className(const JSObject*, VM&); - static String toStringName(const JSObject*, ExecState*); + JSCallbackObject(JSGlobalObject*, Structure*, JSClassRef, void* data); + JSCallbackObject(VM&, JSClassRef, Structure*); - static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + void finishCreation(JSGlobalObject*); + void finishCreation(VM&); - static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); - - static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); - static bool putByIndex(JSCell*, ExecState*, unsigned, JSValue, bool shouldThrow); + static GCClient::IsoSubspace* subspaceForImpl(VM&, SubspaceAccess); + static EncodedJSValue JSC_HOST_CALL_ATTRIBUTES customToPrimitive(JSGlobalObject*, CallFrame*); - static bool deleteProperty(JSCell*, ExecState*, PropertyName); - static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned); + static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSObject*, JSGlobalObject*, unsigned propertyName, PropertySlot&); + + static bool put(JSCell*, JSGlobalObject*, PropertyName, JSValue, PutPropertySlot&); + static bool putByIndex(JSCell*, JSGlobalObject*, unsigned, JSValue, bool shouldThrow); - static bool customHasInstance(JSObject*, ExecState*, JSValue); + static bool deleteProperty(JSCell*, JSGlobalObject*, PropertyName, DeletePropertySlot&); + static bool deletePropertyByIndex(JSCell*, JSGlobalObject*, unsigned); - static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static bool customHasInstance(JSObject*, JSGlobalObject*, JSValue); - static ConstructType getConstructData(JSCell*, ConstructData&); - static CallType getCallData(JSCell*, CallData&); + static void getOwnSpecialPropertyNames(JSObject*, JSGlobalObject*, PropertyNameArray&, DontEnumPropertiesMode); - static void visitChildren(JSCell* cell, SlotVisitor& visitor) - { - JSCallbackObject* thisObject = jsCast(cell); - ASSERT_GC_OBJECT_INHERITS((static_cast(thisObject)), JSCallbackObject::info()); - Parent::visitChildren(thisObject, visitor); - thisObject->m_callbackObjectData->visitChildren(visitor); - } + static CallData getConstructData(JSCell*); + static CallData getCallData(JSCell*); - void init(ExecState*); + void init(JSGlobalObject*); static JSCallbackObject* asCallbackObject(JSValue); static JSCallbackObject* asCallbackObject(EncodedJSValue); + + using RawNativeFunction = EncodedJSValue(JSC_HOST_CALL_ATTRIBUTES*)(JSGlobalObject*, CallFrame*); + + static RawNativeFunction getCallFunction(); + static RawNativeFunction getConstructFunction(); + + static GetValueFunc getStaticFunctionGetter(); + static GetValueFunc getCallbackGetter(); - static EncodedJSValue JSC_HOST_CALL call(ExecState*); - static EncodedJSValue JSC_HOST_CALL construct(ExecState*); - - JSValue getStaticValue(ExecState*, PropertyName); - static EncodedJSValue staticFunctionGetter(ExecState*, EncodedJSValue, PropertyName); - static EncodedJSValue callbackGetter(ExecState*, EncodedJSValue, PropertyName); + JSValue getStaticValue(JSGlobalObject*, PropertyName); std::unique_ptr m_callbackObjectData; const ClassInfo* m_classInfo { nullptr }; }; +template +template +void JSCallbackObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSCallbackObject* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS((static_cast(thisObject)), JSCallbackObject::info()); + Parent::visitChildren(thisObject, visitor); + thisObject->m_callbackObjectData->visitChildren(visitor); +} + +// JSCallbackObject::info()'s forward definition triggers an undefined template var warning +// without these declarations in the same translation unit. +template<> const ClassInfo JSCallbackObject::s_info; +template<> const ClassInfo JSCallbackObject::s_info; + +#if defined(JSC_GLIB_API_ENABLED) +template<> const ClassInfo JSCallbackObject::s_info; +#endif + +#if JSC_OBJC_API_ENABLED || defined(JSC_GLIB_API_ENABLED) +template<> const ClassInfo JSCallbackObject::s_info; +#endif + } // namespace JSC // include the actual template class implementation diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackObjectFunctions.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackObjectFunctions.h similarity index 56% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackObjectFunctions.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackObjectFunctions.h index c7236339..cc6e75f1 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSCallbackObjectFunctions.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSCallbackObjectFunctions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2006-2020 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel * * Redistribution and use in source and binary forms, with or without @@ -24,13 +24,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + #include "APICast.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSCallbackFunction.h" #include "JSClassRef.h" #include "JSFunction.h" -#include "JSGlobalObject.h" #include "JSLock.h" #include "JSObjectRef.h" #include "JSString.h" @@ -43,7 +44,7 @@ namespace JSC { template inline JSCallbackObject* JSCallbackObject::asCallbackObject(JSValue value) { - ASSERT(asObject(value)->inherits(value.getObject()->vm(), info())); + ASSERT(asObject(value)->inherits(info())); return jsCast(asObject(value)); } @@ -51,13 +52,13 @@ template inline JSCallbackObject* JSCallbackObject::asCallbackObject(EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); - ASSERT(asObject(value)->inherits(value.getObject()->vm(), info())); + ASSERT(asObject(value)->inherits(info())); return jsCast(asObject(value)); } template -JSCallbackObject::JSCallbackObject(ExecState* exec, Structure* structure, JSClassRef jsClass, void* data) - : Parent(exec->vm(), structure) +JSCallbackObject::JSCallbackObject(JSGlobalObject* globalObject, Structure* structure, JSClassRef jsClass, void* data) + : Parent(getVM(globalObject), structure) , m_callbackObjectData(makeUnique(data, jsClass)) { } @@ -88,217 +89,235 @@ JSCallbackObject::~JSCallbackObject() } template -void JSCallbackObject::finishCreation(ExecState* exec) +void JSCallbackObject::finishCreation(JSGlobalObject* globalObject) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); Base::finishCreation(vm); - ASSERT(Parent::inherits(vm, info())); - init(exec); + ASSERT(Parent::inherits(info())); + init(globalObject); } // This is just for Global object, so we can assume that Base::finishCreation is JSGlobalObject::finishCreation. template void JSCallbackObject::finishCreation(VM& vm) { - ASSERT(Parent::inherits(vm, info())); + ASSERT(Parent::inherits(info())); ASSERT(Parent::isGlobalObject()); Base::finishCreation(vm); - init(jsCast(this)->globalExec()); + init(jsCast(this)); } template -void JSCallbackObject::init(ExecState* exec) +void JSCallbackObject::init(JSGlobalObject* globalObject) { - ASSERT(exec); + ASSERT(globalObject); + VM& vm = getVM(globalObject); + bool hasConvertToType = false; Vector initRoutines; JSClassRef jsClass = classRef(); do { + if (jsClass->convertToType) + hasConvertToType = true; if (JSObjectInitializeCallback initialize = jsClass->initialize) initRoutines.append(initialize); } while ((jsClass = jsClass->parentClass)); + + if (hasConvertToType) { + this->putDirect(vm, vm.propertyNames->toPrimitiveSymbol, + JSFunction::create(vm, globalObject, 1, "[Symbol.toPrimitive]"_s, customToPrimitive, ImplementationVisibility::Public), + static_cast(PropertyAttribute::DontEnum)); + } // initialize from base to derived for (int i = static_cast(initRoutines.size()) - 1; i >= 0; i--) { - JSLock::DropAllLocks dropAllLocks(exec); + // JSLock::DropAllLocks dropAllLocks(globalObject); JSObjectInitializeCallback initialize = initRoutines[i]; - initialize(toRef(exec), toRef(this)); + initialize(toRef(globalObject), toRef(jsCast(this))); } m_classInfo = this->classInfo(); } template -String JSCallbackObject::className(const JSObject* object, VM& vm) -{ - const JSCallbackObject* thisObject = jsCast(object); - String thisClassName = thisObject->classRef()->className(); - if (!thisClassName.isEmpty()) - return thisClassName; - - return Parent::className(object, vm); -} - -template -String JSCallbackObject::toStringName(const JSObject* object, ExecState* exec) +bool JSCallbackObject::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) { - VM& vm = exec->vm(); - const ClassInfo* info = object->classInfo(vm); - ASSERT(info); - return info->methodTable.className(object, vm); -} - -template -bool JSCallbackObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) -{ - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObject = jsCast(object); - JSContextRef ctx = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSContextRef ctx = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); RefPtr propertyNameRef; if (StringImpl* name = propertyName.uid()) { + unsigned attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum; for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { // optional optimization to bypass getProperty in cases when we only need to know if the property exists if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) { if (!propertyNameRef) propertyNameRef = OpaqueJSString::tryCreate(name); - JSLock::DropAllLocks dropAllLocks(exec); + // JSLock::DropAllLocks dropAllLocks(globalObject); if (hasProperty(ctx, thisRef, propertyNameRef.get())) { - slot.setCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, callbackGetter); + slot.setCustom(thisObject, attributes, getCallbackGetter()); return true; } } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { if (!propertyNameRef) propertyNameRef = OpaqueJSString::tryCreate(name); - JSValueRef exception = 0; + JSValueRef exception = nullptr; JSValueRef value; - { - JSLock::DropAllLocks dropAllLocks(exec); + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); value = getProperty(ctx, thisRef, propertyNameRef.get(), &exception); - } + // } if (exception) { - throwException(exec, scope, toJS(exec, exception)); - slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, jsUndefined()); + throwException(globalObject, scope, toJS(globalObject, exception)); + slot.setValue(thisObject, attributes, jsUndefined()); return true; } if (value) { - slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, toJS(exec, value)); + slot.setValue(thisObject, attributes, toJS(globalObject, value)); return true; } } - if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { if (staticValues->contains(name)) { - JSValue value = thisObject->getStaticValue(exec, propertyName); + JSValue value = thisObject->getStaticValue(globalObject, propertyName); + RETURN_IF_EXCEPTION(scope, false); if (value) { - slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, value); + slot.setValue(thisObject, attributes, value); return true; } } } - if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { if (staticFunctions->contains(name)) { - slot.setCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, staticFunctionGetter); + slot.setCustom(thisObject, attributes, getStaticFunctionGetter()); return true; } } } } - return Parent::getOwnPropertySlot(thisObject, exec, propertyName, slot); + bool found = Parent::getOwnPropertySlot(thisObject, globalObject, propertyName, slot); + RETURN_IF_EXCEPTION(scope, false); + if (found) + return true; + + if (propertyName.uid() == vm.propertyNames->toStringTagSymbol.impl()) { + String className = thisObject->classRef()->className(); + if (className.isEmpty()) + className = thisObject->className(); + slot.setValue(thisObject, static_cast(PropertyAttribute::DontEnum), jsString(vm, WTFMove(className))); + return true; + } + + return false; } template -bool JSCallbackObject::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot) +bool JSCallbackObject::getOwnPropertySlotByIndex(JSObject* object, JSGlobalObject* globalObject, unsigned propertyName, PropertySlot& slot) { - VM& vm = exec->vm(); - return object->methodTable(vm)->getOwnPropertySlot(object, exec, Identifier::from(vm, propertyName), slot); + VM& vm = getVM(globalObject); + return object->methodTable()->getOwnPropertySlot(object, globalObject, Identifier::from(vm, propertyName), slot); } template -JSValue JSCallbackObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint) +EncodedJSValue JSCallbackObject::customToPrimitive(JSGlobalObject* globalObject, CallFrame* callFrame) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); - const JSCallbackObject* thisObject = jsCast(object); - JSContextRef ctx = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSCallbackObject* thisObject = jsDynamicCast(callFrame->thisValue()); + if (!thisObject) + return throwVMTypeError(globalObject, scope, "JSCallbackObject[Symbol.toPrimitive] method called on incompatible |this| value."_s); + PreferredPrimitiveType hint = toPreferredPrimitiveType(globalObject, callFrame->argument(0)); + RETURN_IF_EXCEPTION(scope, { }); + + JSContextRef ctx = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); ::JSType jsHint = hint == PreferString ? kJSTypeString : kJSTypeNumber; for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { - JSValueRef exception = 0; + JSValueRef exception = nullptr; JSValueRef result = convertToType(ctx, thisRef, jsHint, &exception); - if (exception) { - throwException(exec, scope, toJS(exec, exception)); - return jsUndefined(); + if (exception) + return throwVMError(globalObject, scope, toJS(globalObject, exception)); + if (result) { + JSValue jsResult = toJS(globalObject, result); + if (jsResult.isObject()) [[unlikely]] + return JSValue::encode(asObject(jsResult)->ordinaryToPrimitive(globalObject, hint)); + return JSValue::encode(jsResult); } - if (result) - return toJS(exec, result); } } - return Parent::defaultValue(object, exec, hint); + RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->ordinaryToPrimitive(globalObject, hint))); } template -bool JSCallbackObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +bool JSCallbackObject::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObject = jsCast(cell); - JSContextRef ctx = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSContextRef ctx = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); RefPtr propertyNameRef; - JSValueRef valueRef = toRef(exec, value); + JSValueRef valueRef = toRef(globalObject, value); + + if (isThisValueAltered(slot, thisObject)) [[unlikely]] + RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot)); if (StringImpl* name = propertyName.uid()) { for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { if (!propertyNameRef) propertyNameRef = OpaqueJSString::tryCreate(name); - JSValueRef exception = 0; + JSValueRef exception = nullptr; bool result; - { - JSLock::DropAllLocks dropAllLocks(exec); + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); - } + // } if (exception) - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); if (result || exception) return result; } - if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { if (StaticValueEntry* entry = staticValues->get(name)) { if (entry->attributes & kJSPropertyAttributeReadOnly) return false; if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { - JSValueRef exception = 0; + JSValueRef exception = nullptr; bool result; - { - JSLock::DropAllLocks dropAllLocks(exec); + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception); - } + // } if (exception) - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); if (result || exception) return result; } } } - if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { if (StaticFunctionEntry* entry = staticFunctions->get(name)) { - PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry); - if (Parent::getOwnPropertySlot(thisObject, exec, propertyName, getSlot)) - return Parent::put(thisObject, exec, propertyName, value, slot); + PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry, &vm); + bool found = Parent::getOwnPropertySlot(thisObject, globalObject, propertyName, getSlot); + RETURN_IF_EXCEPTION(scope, false); + getSlot.disallowVMEntry.reset(); + if (found) + RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot)); if (entry->attributes & kJSPropertyAttributeReadOnly) return false; return thisObject->JSCallbackObject::putDirect(vm, propertyName, value); // put as override property @@ -307,58 +326,58 @@ bool JSCallbackObject::put(JSCell* cell, ExecState* exec, PropertyName p } } - return Parent::put(thisObject, exec, propertyName, value, slot); + RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot)); } template -bool JSCallbackObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyIndex, JSValue value, bool shouldThrow) +bool JSCallbackObject::putByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyIndex, JSValue value, bool shouldThrow) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObject = jsCast(cell); - JSContextRef ctx = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSContextRef ctx = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); RefPtr propertyNameRef; - JSValueRef valueRef = toRef(exec, value); + JSValueRef valueRef = toRef(globalObject, value); Identifier propertyName = Identifier::from(vm, propertyIndex); for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { if (!propertyNameRef) propertyNameRef = OpaqueJSString::tryCreate(propertyName.impl()); - JSValueRef exception = 0; + JSValueRef exception = nullptr; bool result; - { - JSLock::DropAllLocks dropAllLocks(exec); + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); - } + // } if (exception) - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); if (result || exception) return result; } - if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { if (StaticValueEntry* entry = staticValues->get(propertyName.impl())) { if (entry->attributes & kJSPropertyAttributeReadOnly) return false; if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { - JSValueRef exception = 0; + JSValueRef exception = nullptr; bool result; - { - JSLock::DropAllLocks dropAllLocks(exec); + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception); - } + // } if (exception) - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); if (result || exception) return result; } } } - if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.impl())) { if (entry->attributes & kJSPropertyAttributeReadOnly) return false; @@ -367,18 +386,18 @@ bool JSCallbackObject::putByIndex(JSCell* cell, ExecState* exec, unsigne } } - return Parent::putByIndex(thisObject, exec, propertyIndex, value, shouldThrow); + RELEASE_AND_RETURN(scope, Parent::putByIndex(thisObject, globalObject, propertyIndex, value, shouldThrow)); } template -bool JSCallbackObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +bool JSCallbackObject::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, DeletePropertySlot& slot) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObject = jsCast(cell); - JSContextRef ctx = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSContextRef ctx = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); RefPtr propertyNameRef; if (StringImpl* name = propertyName.uid()) { @@ -386,19 +405,19 @@ bool JSCallbackObject::deleteProperty(JSCell* cell, ExecState* exec, Pro if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) { if (!propertyNameRef) propertyNameRef = OpaqueJSString::tryCreate(name); - JSValueRef exception = 0; + JSValueRef exception = nullptr; bool result; - { - JSLock::DropAllLocks dropAllLocks(exec); + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); result = deleteProperty(ctx, thisRef, propertyNameRef.get(), &exception); - } + // } if (exception) - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); if (result || exception) return true; } - if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { if (StaticValueEntry* entry = staticValues->get(name)) { if (entry->attributes & kJSPropertyAttributeDontDelete) return false; @@ -406,7 +425,7 @@ bool JSCallbackObject::deleteProperty(JSCell* cell, ExecState* exec, Pro } } - if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { if (StaticFunctionEntry* entry = staticFunctions->get(name)) { if (entry->attributes & kJSPropertyAttributeDontDelete) return false; @@ -416,55 +435,63 @@ bool JSCallbackObject::deleteProperty(JSCell* cell, ExecState* exec, Pro } } - return Parent::deleteProperty(thisObject, exec, propertyName); + static_assert(std::is_final_v>, "Ensure no derived classes have custom deletePropertyByIndex implementation"); + if (std::optional index = parseIndex(propertyName)) + RELEASE_AND_RETURN(scope, Parent::deletePropertyByIndex(thisObject, globalObject, index.value())); + RELEASE_AND_RETURN(scope, Parent::deleteProperty(thisObject, globalObject, propertyName, slot)); } template -bool JSCallbackObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) +bool JSCallbackObject::deletePropertyByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyName) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); JSCallbackObject* thisObject = jsCast(cell); - return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(vm, propertyName)); + return JSCell::deleteProperty(thisObject, globalObject, Identifier::from(vm, propertyName)); } template -ConstructType JSCallbackObject::getConstructData(JSCell* cell, ConstructData& constructData) +CallData JSCallbackObject::getConstructData(JSCell* cell) { + CallData constructData; JSCallbackObject* thisObject = jsCast(cell); for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (jsClass->callAsConstructor) { - constructData.native.function = construct; - return ConstructType::Host; + constructData.type = CallData::Type::Native; + constructData.native.function = getConstructFunction(); + constructData.native.isBoundFunction = false; + constructData.native.isWasm = false; + break; } } - return ConstructType::None; + return constructData; } template -EncodedJSValue JSCallbackObject::construct(ExecState* exec) +EncodedJSValue JSCallbackObject::constructImpl(JSGlobalObject* globalObject, CallFrame* callFrame) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); - JSObject* constructor = exec->jsCallee(); - JSContextRef execRef = toRef(exec); + JSObject* constructor = callFrame->jsCallee(); + JSContextRef execRef = toRef(globalObject); JSObjectRef constructorRef = toRef(constructor); for (JSClassRef jsClass = jsCast*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) { - size_t argumentCount = exec->argumentCount(); - Vector arguments; - arguments.reserveInitialCapacity(argumentCount); - for (size_t i = 0; i < argumentCount; ++i) - arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); - JSValueRef exception = 0; + size_t argumentCount = callFrame->argumentCount(); + Vector arguments(argumentCount, [&](size_t i) { + return toRef(globalObject, callFrame->uncheckedArgument(i)); + }); + JSValueRef exception = nullptr; JSObject* result; { - JSLock::DropAllLocks dropAllLocks(exec); - result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), &exception)); + JSLock::DropAllLocks dropAllLocks(globalObject); + result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.span().data(), &exception)); + } + if (exception) { + throwException(globalObject, scope, toJS(globalObject, exception)); + return JSValue::encode(jsUndefined()); } - if (exception) - throwException(exec, scope, toJS(exec, exception)); return JSValue::encode(result); } } @@ -474,26 +501,24 @@ EncodedJSValue JSCallbackObject::construct(ExecState* exec) } template -bool JSCallbackObject::customHasInstance(JSObject* object, ExecState* exec, JSValue value) +bool JSCallbackObject::customHasInstance(JSObject* object, JSGlobalObject* globalObject, JSValue value) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObject = jsCast(object); - JSContextRef execRef = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSContextRef execRef = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) { - JSValueRef valueRef = toRef(exec, value); - JSValueRef exception = 0; + JSValueRef valueRef = toRef(globalObject, value); + JSValueRef exception = nullptr; bool result; - { - JSLock::DropAllLocks dropAllLocks(exec); - result = hasInstance(execRef, thisRef, valueRef, &exception); - } + // https://www.mail-archive.com/webkit-dev@lists.webkit.org/msg28769.html + result = hasInstance(execRef, thisRef, valueRef, &exception); if (exception) - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); return result; } } @@ -501,43 +526,49 @@ bool JSCallbackObject::customHasInstance(JSObject* object, ExecState* ex } template -CallType JSCallbackObject::getCallData(JSCell* cell, CallData& callData) +CallData JSCallbackObject::getCallData(JSCell* cell) { + CallData callData; JSCallbackObject* thisObject = jsCast(cell); for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (jsClass->callAsFunction) { - callData.native.function = call; - return CallType::Host; + callData.type = CallData::Type::Native; + callData.native.function = getCallFunction(); + callData.native.isBoundFunction = false; + callData.native.isWasm = false; + break; } } - return CallType::None; + return callData; } template -EncodedJSValue JSCallbackObject::call(ExecState* exec) +EncodedJSValue JSCallbackObject::callImpl(JSGlobalObject* globalObject, CallFrame* callFrame) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); - JSContextRef execRef = toRef(exec); - JSObjectRef functionRef = toRef(exec->jsCallee()); - JSObjectRef thisObjRef = toRef(jsCast(exec->thisValue().toThis(exec, NotStrictMode))); + JSContextRef execRef = toRef(globalObject); + JSObjectRef functionRef = toRef(callFrame->jsCallee()); + JSObjectRef thisObjRef = toRef(jsCast(callFrame->thisValue().toThis(globalObject, ECMAMode::sloppy()))); for (JSClassRef jsClass = jsCast*>(toJS(functionRef))->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) { - size_t argumentCount = exec->argumentCount(); - Vector arguments; - arguments.reserveInitialCapacity(argumentCount); - for (size_t i = 0; i < argumentCount; ++i) - arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); - JSValueRef exception = 0; + size_t argumentCount = callFrame->argumentCount(); + Vector arguments(argumentCount, [&](size_t i) { + return toRef(globalObject, callFrame->uncheckedArgument(i)); + }); + + JSValueRef exception = nullptr; JSValue result; { - JSLock::DropAllLocks dropAllLocks(exec); - result = toJS(exec, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception)); + JSLock::DropAllLocks dropAllLocks(globalObject); + result = toJS(globalObject, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.span().data(), &exception)); + } + if (exception) { + throwException(globalObject, scope, toJS(globalObject, exception)); + return JSValue::encode(jsUndefined()); } - if (exception) - throwException(exec, scope, toJS(exec, exception)); return JSValue::encode(result); } } @@ -547,47 +578,45 @@ EncodedJSValue JSCallbackObject::call(ExecState* exec) } template -void JSCallbackObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +void JSCallbackObject::getOwnSpecialPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, DontEnumPropertiesMode mode) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); JSCallbackObject* thisObject = jsCast(object); - JSContextRef execRef = toRef(exec); - JSObjectRef thisRef = toRef(thisObject); + JSContextRef execRef = toRef(globalObject); + JSObjectRef thisRef = toRef(jsCast(thisObject)); for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) { - JSLock::DropAllLocks dropAllLocks(exec); + JSLock::DropAllLocks dropAllLocks(globalObject); getPropertyNames(execRef, thisRef, toRef(&propertyNames)); } - if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { typedef OpaqueJSClassStaticValuesTable::const_iterator iterator; iterator end = staticValues->end(); for (iterator it = staticValues->begin(); it != end; ++it) { StringImpl* name = it->key.get(); StaticValueEntry* entry = it->value.get(); - if (entry->getProperty && (!(entry->attributes & kJSPropertyAttributeDontEnum) || mode.includeDontEnumProperties())) { + if (entry->getProperty && (mode == DontEnumPropertiesMode::Include || !(entry->attributes & kJSPropertyAttributeDontEnum))) { ASSERT(!name->isSymbol()); propertyNames.add(Identifier::fromString(vm, String(name))); } } } - if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator; iterator end = staticFunctions->end(); for (iterator it = staticFunctions->begin(); it != end; ++it) { StringImpl* name = it->key.get(); StaticFunctionEntry* entry = it->value.get(); - if (!(entry->attributes & kJSPropertyAttributeDontEnum) || mode.includeDontEnumProperties()) { + if (mode == DontEnumPropertiesMode::Include || !(entry->attributes & kJSPropertyAttributeDontEnum)) { ASSERT(!name->isSymbol()); propertyNames.add(Identifier::fromString(vm, String(name))); } } } } - - Parent::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); } template @@ -613,30 +642,30 @@ bool JSCallbackObject::inherits(JSClassRef c) const } template -JSValue JSCallbackObject::getStaticValue(ExecState* exec, PropertyName propertyName) +JSValue JSCallbackObject::getStaticValue(JSGlobalObject* globalObject, PropertyName propertyName) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); - JSObjectRef thisRef = toRef(this); + JSObjectRef thisRef = toRef(jsCast(this)); if (StringImpl* name = propertyName.uid()) { for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { - if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { if (StaticValueEntry* entry = staticValues->get(name)) { if (JSObjectGetPropertyCallback getProperty = entry->getProperty) { - JSValueRef exception = 0; + JSValueRef exception = nullptr; JSValueRef value; - { - JSLock::DropAllLocks dropAllLocks(exec); - value = getProperty(toRef(exec), thisRef, entry->propertyNameRef.get(), &exception); - } + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); + value = getProperty(toRef(globalObject), thisRef, entry->propertyNameRef.get(), &exception); + // } if (exception) { - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); return jsUndefined(); } if (value) - return toJS(exec, value); + return toJS(globalObject, value); } } } @@ -647,24 +676,27 @@ JSValue JSCallbackObject::getStaticValue(ExecState* exec, PropertyName p } template -EncodedJSValue JSCallbackObject::staticFunctionGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName) +EncodedJSValue JSCallbackObject::staticFunctionGetterImpl(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName propertyName) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObj = asCallbackObject(thisValue); // Check for cached or override property. - PropertySlot slot2(thisObj, PropertySlot::InternalMethodType::VMInquiry); - if (Parent::getOwnPropertySlot(thisObj, exec, propertyName, slot2)) - return JSValue::encode(slot2.getValue(exec, propertyName)); + PropertySlot slot2(thisObj, PropertySlot::InternalMethodType::VMInquiry, &vm); + bool found = Parent::getOwnPropertySlot(thisObj, globalObject, propertyName, slot2); + RETURN_IF_EXCEPTION(scope, { }); + slot2.disallowVMEntry.reset(); + if (found) + return JSValue::encode(slot2.getValue(globalObject, propertyName)); if (StringImpl* name = propertyName.uid()) { for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) { - if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { if (StaticFunctionEntry* entry = staticFunctions->get(name)) { if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) { - JSObject* o = JSCallbackFunction::create(vm, thisObj->globalObject(vm), callAsFunction, name); + JSObject* o = JSCallbackFunction::create(vm, thisObj->globalObject(), callAsFunction, name); thisObj->putDirect(vm, propertyName, o, entry->attributes); return JSValue::encode(o); } @@ -673,18 +705,18 @@ EncodedJSValue JSCallbackObject::staticFunctionGetter(ExecState* exec, E } } - return JSValue::encode(throwException(exec, scope, createReferenceError(exec, "Static function property defined with NULL callAsFunction callback."_s))); + return JSValue::encode(throwException(globalObject, scope, createReferenceError(globalObject, "Static function property defined with NULL callAsFunction callback."_s))); } template -EncodedJSValue JSCallbackObject::callbackGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName) +EncodedJSValue JSCallbackObject::callbackGetterImpl(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName propertyName) { - VM& vm = exec->vm(); + VM& vm = getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSCallbackObject* thisObj = asCallbackObject(thisValue); - JSObjectRef thisRef = toRef(thisObj); + JSObjectRef thisRef = toRef(jsCast(thisObj)); RefPtr propertyNameRef; if (StringImpl* name = propertyName.uid()) { @@ -692,23 +724,23 @@ EncodedJSValue JSCallbackObject::callbackGetter(ExecState* exec, Encoded if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { if (!propertyNameRef) propertyNameRef = OpaqueJSString::tryCreate(name); - JSValueRef exception = 0; + JSValueRef exception = nullptr; JSValueRef value; - { - JSLock::DropAllLocks dropAllLocks(exec); - value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), &exception); - } + // { + // JSLock::DropAllLocks dropAllLocks(globalObject); + value = getProperty(toRef(globalObject), thisRef, propertyNameRef.get(), &exception); + // } if (exception) { - throwException(exec, scope, toJS(exec, exception)); + throwException(globalObject, scope, toJS(globalObject, exception)); return JSValue::encode(jsUndefined()); } if (value) - return JSValue::encode(toJS(exec, value)); + return JSValue::encode(toJS(globalObject, value)); } } } - return JSValue::encode(throwException(exec, scope, createReferenceError(exec, "hasProperty callback returned true for a property that doesn't exist."_s))); + return JSValue::encode(throwException(globalObject, scope, createReferenceError(globalObject, "hasProperty callback returned true for a property that doesn't exist."_s))); } } // namespace JSC diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSClassRef.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSClassRef.h similarity index 60% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSClassRef.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSClassRef.h index 0dd0dca5..203cf10f 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSClassRef.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSClassRef.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,15 +26,18 @@ #ifndef JSClassRef_h #define JSClassRef_h +#include "JSObjectRef.h" #include "OpaqueJSString.h" #include "Protect.h" #include "Weak.h" -#include #include #include +#define STATIC_VALUE_ENTRY_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("StaticValueEntry." #method) method + struct StaticValueEntry { - WTF_MAKE_FAST_ALLOCATED; + WTF_DEPRECATED_MAKE_FAST_ALLOCATED(StaticValueEntry); public: StaticValueEntry(JSObjectGetPropertyCallback _getProperty, JSObjectSetPropertyCallback _setProperty, JSPropertyAttributes _attributes, String& propertyName) : getProperty(_getProperty) @@ -44,33 +47,40 @@ struct StaticValueEntry { { } - JSObjectGetPropertyCallback getProperty; - JSObjectSetPropertyCallback setProperty; + JSObjectGetPropertyCallback STATIC_VALUE_ENTRY_METHOD(getProperty); + JSObjectSetPropertyCallback STATIC_VALUE_ENTRY_METHOD(setProperty); JSPropertyAttributes attributes; RefPtr propertyNameRef; }; +#undef STATIC_VALUE_ENTRY_METHOD + +#define STATIC_FUNCTION_ENTRY_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("StaticFunctionEntry." #method) method + struct StaticFunctionEntry { - WTF_MAKE_FAST_ALLOCATED; + WTF_DEPRECATED_MAKE_FAST_ALLOCATED(StaticFunctionEntry); public: StaticFunctionEntry(JSObjectCallAsFunctionCallback _callAsFunction, JSPropertyAttributes _attributes) : callAsFunction(_callAsFunction), attributes(_attributes) { } - JSObjectCallAsFunctionCallback callAsFunction; + JSObjectCallAsFunctionCallback STATIC_FUNCTION_ENTRY_METHOD(callAsFunction); JSPropertyAttributes attributes; }; -typedef HashMap, std::unique_ptr> OpaqueJSClassStaticValuesTable; -typedef HashMap, std::unique_ptr> OpaqueJSClassStaticFunctionsTable; +#undef STATIC_FUNCTION_ENTRY_METHOD + +typedef UncheckedKeyHashMap, std::unique_ptr> OpaqueJSClassStaticValuesTable; +typedef UncheckedKeyHashMap, std::unique_ptr> OpaqueJSClassStaticFunctionsTable; struct OpaqueJSClass; // An OpaqueJSClass (JSClass) is created without a context, so it can be used with any context, even across context groups. // This structure holds data members that vary across context groups. struct OpaqueJSClassContextData { - WTF_MAKE_NONCOPYABLE(OpaqueJSClassContextData); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(OpaqueJSClassContextData); WTF_DEPRECATED_MAKE_FAST_ALLOCATED(OpaqueJSClassContextData); public: OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass*); @@ -82,35 +92,38 @@ struct OpaqueJSClassContextData { // 4. When it is used, the old context data is found in VM and used. RefPtr m_class; - std::unique_ptr staticValues; - std::unique_ptr staticFunctions; + OpaqueJSClassStaticValuesTable staticValues; + OpaqueJSClassStaticFunctionsTable staticFunctions; JSC::Weak cachedPrototype; }; +#define OPAQUE_JSCLASS_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("OpaqueJSClass." #method) method + struct OpaqueJSClass : public ThreadSafeRefCounted { static Ref create(const JSClassDefinition*); static Ref createNoAutomaticPrototype(const JSClassDefinition*); JS_EXPORT_PRIVATE ~OpaqueJSClass(); String className(); - OpaqueJSClassStaticValuesTable* staticValues(JSC::ExecState*); - OpaqueJSClassStaticFunctionsTable* staticFunctions(JSC::ExecState*); - JSC::JSObject* prototype(JSC::ExecState*); + OpaqueJSClassStaticValuesTable* staticValues(JSC::JSGlobalObject*); + OpaqueJSClassStaticFunctionsTable* staticFunctions(JSC::JSGlobalObject*); + JSC::JSObject* prototype(JSC::JSGlobalObject*); OpaqueJSClass* parentClass; OpaqueJSClass* prototypeClass; - JSObjectInitializeCallback initialize; - JSObjectFinalizeCallback finalize; - JSObjectHasPropertyCallback hasProperty; - JSObjectGetPropertyCallback getProperty; - JSObjectSetPropertyCallback setProperty; - JSObjectDeletePropertyCallback deleteProperty; - JSObjectGetPropertyNamesCallback getPropertyNames; - JSObjectCallAsFunctionCallback callAsFunction; - JSObjectCallAsConstructorCallback callAsConstructor; - JSObjectHasInstanceCallback hasInstance; - JSObjectConvertToTypeCallback convertToType; + JSObjectInitializeCallback OPAQUE_JSCLASS_METHOD(initialize); + JSObjectFinalizeCallback OPAQUE_JSCLASS_METHOD(finalize); + JSObjectHasPropertyCallback OPAQUE_JSCLASS_METHOD(hasProperty); + JSObjectGetPropertyCallback OPAQUE_JSCLASS_METHOD(getProperty); + JSObjectSetPropertyCallback OPAQUE_JSCLASS_METHOD(setProperty); + JSObjectDeletePropertyCallback OPAQUE_JSCLASS_METHOD(deleteProperty); + JSObjectGetPropertyNamesCallback OPAQUE_JSCLASS_METHOD(getPropertyNames); + JSObjectCallAsFunctionCallback OPAQUE_JSCLASS_METHOD(callAsFunction); + JSObjectCallAsConstructorCallback OPAQUE_JSCLASS_METHOD(callAsConstructor); + JSObjectHasInstanceCallback OPAQUE_JSCLASS_METHOD(hasInstance); + JSObjectConvertToTypeCallback OPAQUE_JSCLASS_METHOD(convertToType); private: friend struct OpaqueJSClassContextData; @@ -119,12 +132,14 @@ struct OpaqueJSClass : public ThreadSafeRefCounted { OpaqueJSClass(const OpaqueJSClass&); OpaqueJSClass(const JSClassDefinition*, OpaqueJSClass* protoClass); - OpaqueJSClassContextData& contextData(JSC::ExecState*); + OpaqueJSClassContextData& contextData(JSC::JSGlobalObject*); // Strings in these data members should not be put into any AtomStringTable. String m_className; - std::unique_ptr m_staticValues; - std::unique_ptr m_staticFunctions; + OpaqueJSClassStaticValuesTable m_staticValues; + OpaqueJSClassStaticFunctionsTable m_staticFunctions; }; +#undef OPAQUE_JSCLASS_METHOD + #endif // JSClassRef_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContext.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContext.h similarity index 93% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContext.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContext.h index 6b9c5d41..8e744288 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContext.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContext.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * Copyright (C) 2013-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,10 +26,10 @@ #ifndef JSContext_h #define JSContext_h -#include -#include +#include "JavaScript.h" +#include "WebKitAvailability.h" -#if JSC_OBJC_API_ENABLED +#if defined(__OBJC__) && JSC_OBJC_API_ENABLED @class JSScript, JSVirtualMachine, JSValue, JSContext; @@ -174,9 +174,16 @@ JSC_CLASS_AVAILABLE(macos(10.9), ios(7.0)) /*! @property -@discussion Name of the JSContext. Exposed when remote debugging the context. +@discussion Name of the JSContext. Exposed when inspecting the context. */ @property (copy) NSString *name JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@property +@discussion Controls whether this @link JSContext @/link is inspectable in Web Inspector. The default value is NO. +*/ +@property (nonatomic, getter=isInspectable) BOOL inspectable JSC_API_AVAILABLE(macos(13.3), ios(16.4)) NS_SWIFT_NAME(isInspectable); + @end /*! @@ -235,4 +242,4 @@ JSC_CLASS_AVAILABLE(macos(10.9), ios(7.0)) #endif -#endif // JSContext_h +#endif /* JSContext_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextInternal.h similarity index 96% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextInternal.h index 958c479f..44c5f58e 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextInternal.h @@ -32,12 +32,12 @@ struct CallbackData { CallbackData* next; JSContext *context; - JSValue *preservedException; + RetainPtr preservedException; JSValueRef calleeValue; JSValueRef thisValue; size_t argumentCount; const JSValueRef *arguments; - NSArray *currentArguments; + RetainPtr currentArguments; }; @class JSWrapperMap; diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextPrivate.h similarity index 94% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextPrivate.h index 75f526b9..eafc8791 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextPrivate.h @@ -69,7 +69,7 @@ @property @discussion Remote inspection setting of the JSContext. Default value is YES. */ -@property (setter=_setRemoteInspectionEnabled:) BOOL _remoteInspectionEnabled JSC_API_AVAILABLE(macos(10.10), ios(8.0)); +@property (setter=_setRemoteInspectionEnabled:) BOOL _remoteInspectionEnabled JSC_API_DEPRECATED_WITH_REPLACEMENT("inspectable", macos(10.10, 13.3), ios(8.0, 16.4)); /*! @property @@ -106,6 +106,12 @@ */ - (JSValue *)dependencyIdentifiersForModuleJSScript:(JSScript *)script JSC_API_AVAILABLE(macos(10.15), ios(13.0)); +/*! + @method + @abstract Mark this JSContext as an ITMLKit context for the purposes of remote inspection capabilities. + */ +- (void)_setITMLDebuggableType JSC_API_AVAILABLE(macos(11.0), ios(14.0)); + @end #endif diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRef.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRef.h similarity index 84% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRef.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRef.h index 1ce74358..439cdd71 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRef.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRef.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,9 +26,9 @@ #ifndef JSContextRef_h #define JSContextRef_h -#include -#include -#include +#include "JSObjectRef.h" +#include "JSValueRef.h" +#include "WebKitAvailability.h" #ifndef __cplusplus #include @@ -143,19 +143,34 @@ JS_EXPORT JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx) JSC_API @abstract Gets a copy of the name of a context. @param ctx The JSGlobalContext whose name you want to get. @result The name for ctx. -@discussion A JSGlobalContext's name is exposed for remote debugging to make it -easier to identify the context you would like to attach to. +@discussion A JSGlobalContext's name is exposed when inspecting the context to make it easier to identify the context you would like to inspect. */ JS_EXPORT JSStringRef JSGlobalContextCopyName(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); /*! @function -@abstract Sets the remote debugging name for a context. +@abstract Sets the name exposed when inspecting a context. @param ctx The JSGlobalContext that you want to name. -@param name The remote debugging name to set on ctx. +@param name The name to set on the context. */ JS_EXPORT void JSGlobalContextSetName(JSGlobalContextRef ctx, JSStringRef name) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); +/*! +@function +@abstract Gets whether the context is inspectable in Web Inspector. +@param ctx The JSGlobalContext that you want to change the inspectability of. +@result Whether the context is inspectable in Web Inspector. +*/ +JS_EXPORT bool JSGlobalContextIsInspectable(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(13.3), ios(16.4)); + +/*! +@function +@abstract Sets whether the context is inspectable in Web Inspector. Default value is NO. +@param ctx The JSGlobalContext that you want to change the inspectability of. +@param inspectable YES to allow Web Inspector to connect to the context, otherwise NO. +*/ +JS_EXPORT void JSGlobalContextSetInspectable(JSGlobalContextRef ctx, bool inspectable) JSC_API_AVAILABLE(macos(13.3), ios(16.4)); + #ifdef __cplusplus } #endif diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefInspectorSupport.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefInspectorSupport.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefInspectorSupport.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefInspectorSupport.h index a09d828b..06ef1415 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefInspectorSupport.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefInspectorSupport.h @@ -30,7 +30,7 @@ #error Requires C++ Support. #endif -#include +#include "JSContextRefPrivate.h" namespace Inspector { class AugmentableInspectorController; diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefInternal.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefInternal.h index 149f70ba..0dd994c7 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefInternal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefPrivate.h similarity index 74% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefPrivate.h index 6ae649ef..c009bb5a 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSContextRefPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSContextRefPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,9 +26,9 @@ #ifndef JSContextRefPrivate_h #define JSContextRefPrivate_h -#include -#include -#include +#include "JSObjectRef.h" +#include "JSValueRef.h" +#include "WebKitAvailability.h" #ifndef __cplusplus #include @@ -94,6 +94,31 @@ JS_EXPORT void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef group, doub */ JS_EXPORT void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); +/*! +@function +@abstract Enables sampling profiler. +@param group The JavaScript context group to start sampling. +@result The value of the enablement, true if the sampling profiler gets enabled, otherwise false. +@discussion Remote inspection is true by default. +*/ +JS_EXPORT bool JSContextGroupEnableSamplingProfiler(JSContextGroupRef group) JSC_API_AVAILABLE(macos(14.2), ios(17.2)); + +/*! +@function +@abstract Disables sampling profiler. +@param group The JavaScript context group to stop sampling. +*/ +JS_EXPORT void JSContextGroupDisableSamplingProfiler(JSContextGroupRef group) JSC_API_AVAILABLE(macos(14.2), ios(17.2)); + +/*! +@function +@abstract Gets sampling profiler output in JSON form and clears the sampling profiler records. +@param group The JavaScript context group whose sampling profile output is taken. +@result The sampling profiler output in JSON form. NULL if sampling profiler is not enabled ever before. +@discussion Calling this function clears the sampling data accumulated so far. +*/ +JS_EXPORT JSStringRef JSContextGroupTakeSamplesFromSamplingProfiler(JSContextGroupRef group) JSC_API_AVAILABLE(macos(14.2), ios(17.2)); + /*! @function @abstract Gets a whether or not remote inspection is enabled on the context. @@ -101,7 +126,7 @@ JS_EXPORT void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group) JS @result The value of the setting, true if remote inspection is enabled, otherwise false. @discussion Remote inspection is true by default. */ -JS_EXPORT bool JSGlobalContextGetRemoteInspectionEnabled(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); +JS_EXPORT bool JSGlobalContextGetRemoteInspectionEnabled(JSGlobalContextRef ctx) JSC_API_DEPRECATED_WITH_REPLACEMENT("JSGlobalContextIsInspectable", macos(10.10, 13.3), ios(8.0, 16.4)); /*! @function @@ -109,7 +134,7 @@ JS_EXPORT bool JSGlobalContextGetRemoteInspectionEnabled(JSGlobalContextRef ctx) @param ctx The JSGlobalContext that you want to change. @param enabled The new remote inspection enabled setting for the context. */ -JS_EXPORT void JSGlobalContextSetRemoteInspectionEnabled(JSGlobalContextRef ctx, bool enabled) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); +JS_EXPORT void JSGlobalContextSetRemoteInspectionEnabled(JSGlobalContextRef ctx, bool enabled) JSC_API_DEPRECATED_WITH_REPLACEMENT("JSGlobalContextSetInspectable", macos(10.10, 13.3), ios(8.0, 16.4)); /*! @function @@ -136,7 +161,16 @@ JS_EXPORT void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions( @param function The callback function to set, which receives the promise and rejection reason as arguments. @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. */ -JS_EXPORT void JSGlobalContextSetUnhandledRejectionCallback(JSGlobalContextRef ctx, JSObjectRef function, JSValueRef* exception) JSC_API_AVAILABLE(macos(JSC_MAC_TBA), ios(JSC_IOS_TBA)); +JS_EXPORT void JSGlobalContextSetUnhandledRejectionCallback(JSGlobalContextRef ctx, JSObjectRef function, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.15.4), ios(13.4)); + +/*! +@function +@abstract Sets whether a context allows use of eval (or the Function constructor). +@param ctx The JSGlobalContext that you want to change. +@param enabled The new eval enabled setting for the context. +@param message The error message to display when user attempts to call eval (or the Function constructor). Pass NULL when setting enabled to true. +*/ +JS_EXPORT void JSGlobalContextSetEvalEnabled(JSGlobalContextRef ctx, bool enabled, JSStringRef message) JSC_API_AVAILABLE(macos(12.3), ios(15.4)); #ifdef __cplusplus } diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSExport.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSExport.h similarity index 96% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSExport.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSExport.h index 5caace64..0059f59e 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSExport.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSExport.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,9 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import +#ifndef JSExport_h +#define JSExport_h -#if JSC_OBJC_API_ENABLED +#include "JavaScriptCore.h" + +#if defined(__OBJC__) && JSC_OBJC_API_ENABLED /*! @protocol @@ -144,3 +147,5 @@ @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector #endif + +#endif /* JSExport_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSHeapFinalizerPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSHeapFinalizerPrivate.h similarity index 94% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSHeapFinalizerPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSHeapFinalizerPrivate.h index 8c9b1525..7cab44a9 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSHeapFinalizerPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSHeapFinalizerPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Apple Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ #ifndef JSHeapFinalizerPrivate_h #define JSHeapFinalizerPrivate_h -#include +#include "JSBase.h" #include #ifdef __cplusplus diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBasePrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSLockRefPrivate.h similarity index 63% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBasePrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSLockRefPrivate.h index 2fc916b7..bc3d3e65 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSBasePrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSLockRefPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2020 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,35 +20,33 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JSBasePrivate_h -#define JSBasePrivate_h +#pragma once -#include -#include +#include "JSBase.h" #ifdef __cplusplus extern "C" { #endif /*! -@function -@abstract Reports an object's non-GC memory payload to the garbage collector. -@param ctx The execution context to use. -@param size The payload's size, in bytes. -@discussion Use this function to notify the garbage collector that a GC object -owns a large non-GC memory region. Calling this function will encourage the -garbage collector to collect soon, hoping to reclaim that large non-GC memory -region. -*/ -JS_EXPORT void JSReportExtraMemoryCost(JSContextRef ctx, size_t size) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + @function + @abstract Acquire the API lock for the given JSContextRef. + @param ctx The execution context to be locked. + @discussion The lock has to be held to perform any interactions with the JSContextRef. This function allows holding the lock across multiple interactions to amortize the cost. This lock is a recursive lock. + */ +JS_EXPORT void JSLock(JSContextRef ctx); -JS_EXPORT void JSDisableGCTimer(void); +/*! + @function + @abstract Release the API lock for the given JSContextRef. + @param ctx The execution context to be unlocked. + @discussion Releases the lock that was previously acquired using JSLock. + */ +JS_EXPORT void JSUnlock(JSContextRef ctx); #ifdef __cplusplus } #endif - -#endif /* JSBasePrivate_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSManagedValue.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSManagedValue.h similarity index 92% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSManagedValue.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSManagedValue.h index 3ebc7a4b..fd5f8bcb 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSManagedValue.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSManagedValue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,10 +26,10 @@ #ifndef JSManagedValue_h #define JSManagedValue_h -#import -#import +#include "JSBase.h" +#include "WebKitAvailability.h" -#if JSC_OBJC_API_ENABLED +#if defined(__OBJC__) && JSC_OBJC_API_ENABLED @class JSValue; @class JSContext; @@ -76,6 +76,6 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @end -#endif // JSC_OBJC_API_ENABLED +#endif -#endif // JSManagedValue_h +#endif /* JSManagedValue_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSManagedValueInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSManagedValueInternal.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSManagedValueInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSManagedValueInternal.h index 2443fe5a..e2ba5738 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSManagedValueInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSManagedValueInternal.h @@ -27,6 +27,7 @@ #define JSManagedValueInternal_h #import +#import #if JSC_OBJC_API_ENABLED diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSMarkingConstraintPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSMarkingConstraintPrivate.h similarity index 95% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSMarkingConstraintPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSMarkingConstraintPrivate.h index aaf85cad..d3ee3753 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSMarkingConstraintPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSMarkingConstraintPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Apple Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ #ifndef JSMarkingConstraintPrivate_h #define JSMarkingConstraintPrivate_h -#include +#include "JSContextRef.h" #include #ifdef __cplusplus diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSObjectRef.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSObjectRef.h similarity index 98% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSObjectRef.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSObjectRef.h index 330f5e3b..f73332e9 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSObjectRef.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSObjectRef.h @@ -27,9 +27,9 @@ #ifndef JSObjectRef_h #define JSObjectRef_h -#include -#include -#include +#include "JSBase.h" +#include "JSValueRef.h" +#include "WebKitAvailability.h" #ifndef __cplusplus #include @@ -339,6 +339,8 @@ JSStaticValue StaticValueArray[] = { Standard JavaScript practice calls for storing function objects in prototypes, so they can be shared. The default JSClass created by JSClassCreate follows this idiom, instantiating objects with a shared, automatically generating prototype containing the class's function objects. The kJSClassAttributeNoAutomaticPrototype attribute specifies that a JSClass should not automatically generate such a prototype. The resulting JSClass instantiates objects with the default object prototype, and gives each instance object its own copy of the class's function objects. A NULL callback specifies that the default object callback should substitute, except in the case of hasProperty, where it specifies that getProperty should substitute. + +It is not possible to use JS subclassing with objects created from a class definition that sets callAsConstructor by default. Subclassing is supported via the JSObjectMakeConstructor function, however. */ typedef struct { int version; /* current (and only) version is 0 */ @@ -426,7 +428,7 @@ JS_EXPORT JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStrin @param jsClass A JSClass that is the class your constructor will assign to the objects its constructs. jsClass will be used to set the constructor's .prototype property, and to evaluate 'instanceof' expressions. Pass NULL to use the default object class. @param callAsConstructor A JSObjectCallAsConstructorCallback to invoke when your constructor is used in a 'new' expression. Pass NULL to use the default object constructor. @result A JSObject that is a constructor. The object's prototype will be the default object prototype. -@discussion The default object constructor takes no arguments and constructs an object of class jsClass with no private data. +@discussion The default object constructor takes no arguments and constructs an object of class jsClass with no private data. If the constructor is inherited via JS subclassing and the value returned from callAsConstructor was created with jsClass, then the returned object will have it's prototype overridden to the derived class's prototype. */ JS_EXPORT JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor); diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSObjectRefPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSObjectRefPrivate.h similarity index 98% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSObjectRefPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSObjectRefPrivate.h index 6e32612e..d12077c6 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSObjectRefPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSObjectRefPrivate.h @@ -26,7 +26,7 @@ #ifndef JSObjectRefPrivate_h #define JSObjectRefPrivate_h -#include +#include "JSObjectRef.h" #ifdef __cplusplus extern "C" { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSRemoteInspector.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRemoteInspector.h similarity index 81% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSRemoteInspector.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRemoteInspector.h index 85768e4d..9c6fcc62 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSRemoteInspector.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRemoteInspector.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) 2015-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,10 +26,11 @@ #ifndef JSRemoteInspector_h #define JSRemoteInspector_h -#include -#include +#include "JSBase.h" +#include "WebKitAvailability.h" #if defined(WIN32) || defined(_WIN32) +#include typedef int JSProcessID; #else #include @@ -47,7 +48,10 @@ JS_EXPORT void JSRemoteInspectorSetParentProcessInformation(JSProcessID, const u JS_EXPORT void JSRemoteInspectorSetLogToSystemConsole(bool) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); JS_EXPORT bool JSRemoteInspectorGetInspectionEnabledByDefault(void) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); -JS_EXPORT void JSRemoteInspectorSetInspectionEnabledByDefault(bool) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); +JS_EXPORT void JSRemoteInspectorSetInspectionEnabledByDefault(bool) JSC_API_DEPRECATED("Use JSGlobalContextSetInspectable on a single JSGlobalContextRef.", macos(10.11, 13.3), ios(9.0, 16.4)); + +JS_EXPORT bool JSRemoteInspectorGetInspectionFollowsInternalPolicies(void) JSC_API_AVAILABLE(macos(13.3), ios(16.4)); +JS_EXPORT void JSRemoteInspectorSetInspectionFollowsInternalPolicies(bool) JSC_API_AVAILABLE(macos(13.3), ios(16.4)); #ifdef __cplusplus } diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRemoteInspectorServer.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRemoteInspectorServer.h new file mode 100644 index 00000000..2bbcf899 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRemoteInspectorServer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 Sony Interactive Entertainment Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSRemoteInspectorServer_h +#define JSRemoteInspectorServer_h + +#include "JSBase.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +JS_EXPORT uint16_t JSRemoteInspectorServerStart(const char* address, uint16_t port); + +#ifdef __cplusplus +} +#endif + +#endif /* JSRemoteInspectorServer_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSRetainPtr.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRetainPtr.h similarity index 88% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSRetainPtr.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRetainPtr.h index fd8412f3..4a4b1622 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSRetainPtr.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSRetainPtr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2018 Apple Inc. All rights reserved. + * Copyright (C) 2005-2020 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,14 +28,18 @@ #pragma once -#include -#include +#include "JSContextRef.h" +#include "JSObjectRef.h" +#include "JSStringRef.h" #include +#include -inline void JSRetain(JSStringRef string) { JSStringRetain(string); } -inline void JSRelease(JSStringRef string) { JSStringRelease(string); } +inline void JSRetain(JSClassRef context) { JSClassRetain(context); } +inline void JSRelease(JSClassRef context) { JSClassRelease(context); } inline void JSRetain(JSGlobalContextRef context) { JSGlobalContextRetain(context); } inline void JSRelease(JSGlobalContextRef context) { JSGlobalContextRelease(context); } +inline void JSRetain(JSStringRef string) { JSStringRetain(string); } +inline void JSRelease(JSStringRef string) { JSStringRelease(string); } enum AdoptTag { Adopt }; @@ -74,6 +78,7 @@ template class JSRetainPtr { T m_ptr { nullptr }; }; +JSRetainPtr adopt(JSClassRef); JSRetainPtr adopt(JSStringRef); JSRetainPtr adopt(JSGlobalContextRef); @@ -82,6 +87,11 @@ template inline JSRetainPtr::JSRetainPtr(AdoptTag, T ptr) { } +inline JSRetainPtr adopt(JSClassRef o) +{ + return JSRetainPtr(Adopt, o); +} + inline JSRetainPtr adopt(JSStringRef o) { return JSRetainPtr(Adopt, o); @@ -161,23 +171,3 @@ template inline bool operator==(const JSRetainPtr& a, { return a.get() == b; } - -template inline bool operator==(T* a, const JSRetainPtr& b) -{ - return a == b.get(); -} - -template inline bool operator!=(const JSRetainPtr& a, const JSRetainPtr& b) -{ - return a.get() != b.get(); -} - -template inline bool operator!=(const JSRetainPtr& a, U* b) -{ - return a.get() != b; -} - -template inline bool operator!=(T* a, const JSRetainPtr& b) -{ - return a != b.get(); -} diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScript.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScript.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScript.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScript.h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptInternal.h similarity index 99% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptInternal.h index 4a9427d2..951438d4 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptInternal.h @@ -23,14 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#pragma once +#if JSC_OBJC_API_ENABLED #import "JSScript.h" #import "SourceCode.h" #import -#if JSC_OBJC_API_ENABLED - NS_ASSUME_NONNULL_BEGIN namespace JSC { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptRefPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptRefPrivate.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptRefPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptRefPrivate.h index e6224ce3..f895239d 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptRefPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptRefPrivate.h @@ -26,9 +26,9 @@ #ifndef JSScriptRefPrivate_h #define JSScriptRefPrivate_h -#include -#include -#include +#include "JSContextRef.h" +#include "JSStringRef.h" +#include "JSValueRef.h" /*! @typedef JSScriptRef A JavaScript script reference. */ typedef struct OpaqueJSScript* JSScriptRef; diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptSourceProvider.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptSourceProvider.h similarity index 88% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptSourceProvider.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptSourceProvider.h index 09e4018d..ee36ac13 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSScriptSourceProvider.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSScriptSourceProvider.h @@ -29,7 +29,7 @@ @class JSScript; -class JSScriptSourceProvider : public JSC::SourceProvider { +class JSScriptSourceProvider final : public JSC::SourceProvider { public: template static Ref create(JSScript *script, Args&&... args) @@ -37,9 +37,9 @@ class JSScriptSourceProvider : public JSC::SourceProvider { return adoptRef(*new JSScriptSourceProvider(script, std::forward(args)...)); } - unsigned hash() const override; - StringView source() const override; - RefPtr cachedBytecode() const override; + unsigned hash() const final; + StringView source() const final; + RefPtr cachedBytecode() const final; private: template @@ -48,7 +48,7 @@ class JSScriptSourceProvider : public JSC::SourceProvider { , m_script(script) { } - RetainPtr m_script; + const RetainPtr m_script; }; #endif // JSC_OBJC_API_ENABLED diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRef.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRef.h similarity index 93% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRef.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRef.h index bc03ed70..5fdea1d1 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRef.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRef.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ #ifndef JSStringRef_h #define JSStringRef_h -#include +#include "JSValueRef.h" #ifndef __cplusplus #include @@ -37,7 +37,7 @@ extern "C" { #endif -#if !defined(_NATIVE_WCHAR_T_DEFINED) /* MSVC */ \ +#if !defined(_NATIVE_WCHAR_T_DEFINED) /* MSVC */ \ && (!defined(__WCHAR_MAX__) || (__WCHAR_MAX__ > 0xffffU)) /* ISO C/C++ */ \ && (!defined(WCHAR_MAX) || (WCHAR_MAX > 0xffffU)) /* RVCT */ /*! @@ -46,9 +46,9 @@ extern "C" { character. As with all scalar types, endianness depends on the underlying architecture. */ - typedef unsigned short JSChar; +typedef unsigned short JSChar; #else - typedef wchar_t JSChar; +typedef wchar_t JSChar; #endif /*! @@ -90,35 +90,35 @@ JS_EXPORT void JSStringRelease(JSStringRef string); JS_EXPORT size_t JSStringGetLength(JSStringRef string); /*! @function -@abstract Returns a pointer to the Unicode character buffer that +@abstract Returns a pointer to the Unicode character buffer that serves as the backing store for a JavaScript string. @param string The JSString whose backing store you want to access. -@result A pointer to the Unicode character buffer that serves as string's +@result A pointer to the Unicode character buffer that serves as string's backing store, which will be deallocated when string is deallocated. */ JS_EXPORT const JSChar* JSStringGetCharactersPtr(JSStringRef string); /*! @function -@abstract Returns the maximum number of bytes a JavaScript string will +@abstract Returns the maximum number of bytes a JavaScript string will take up if converted into a null-terminated UTF8 string. -@param string The JSString whose maximum converted size (in bytes) you +@param string The JSString whose maximum converted size (in bytes) you want to know. -@result The maximum number of bytes that could be required to convert string into a - null-terminated UTF8 string. The number of bytes that the conversion actually ends +@result The maximum number of bytes that could be required to convert string into a + null-terminated UTF8 string. The number of bytes that the conversion actually ends up requiring could be less than this, but never more. */ JS_EXPORT size_t JSStringGetMaximumUTF8CStringSize(JSStringRef string); /*! @function -@abstract Converts a JavaScript string into a null-terminated UTF8 string, +@abstract Converts a JavaScript string into a null-terminated UTF8 string, and copies the result into an external byte buffer. @param string The source JSString. -@param buffer The destination byte buffer into which to copy a null-terminated - UTF8 representation of string. On return, buffer contains a UTF8 string - representation of string. If bufferSize is too small, buffer will contain only - partial results. If buffer is not at least bufferSize bytes in size, - behavior is undefined. +@param buffer The destination byte buffer into which to copy a null-terminated + UTF8 representation of string. On return, buffer contains a UTF8 string + representation of string. If bufferSize is too small, buffer will contain only + partial results. If buffer is not at least bufferSize bytes in size, + behavior is undefined. @param bufferSize The size of the external buffer in bytes. @result The number of bytes written into buffer (including the null-terminator byte). */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefBSTR.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefBSTR.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefBSTR.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefBSTR.h index 066c68d5..0f0a0ccd 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefBSTR.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefBSTR.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefCF.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefCF.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefCF.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefCF.h index 1e210c7a..7667fb47 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefCF.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefCF.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,8 +26,8 @@ #ifndef JSStringRefCF_h #define JSStringRefCF_h -#include "JSBase.h" #include +#include "JSBase.h" #ifdef __cplusplus extern "C" { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefPrivate.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefPrivate.h index f1db806e..76a8470a 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSStringRefPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSStringRefPrivate.h @@ -26,7 +26,7 @@ #ifndef JSStringRefPrivate_h #define JSStringRefPrivate_h -#include +#include "JSStringRef.h" #ifdef __cplusplus extern "C" { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSTypedArray.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSTypedArray.h similarity index 99% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSTypedArray.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSTypedArray.h index 7eaf76c5..e28c2cff 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSTypedArray.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSTypedArray.h @@ -27,8 +27,8 @@ #ifndef JSTypedArray_h #define JSTypedArray_h -#include -#include +#include "JSBase.h" +#include "JSValueRef.h" #ifdef __cplusplus extern "C" { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValue.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValue.h similarity index 69% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValue.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValue.h index 1b5845e9..6b2438de 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValue.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * Copyright (C) 2013-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ #ifndef JSValue_h #define JSValue_h -#if JSC_OBJC_API_ENABLED +#if defined(__OBJC__) && JSC_OBJC_API_ENABLED #import @@ -51,7 +51,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @property @abstract The JSContext that this value originates from. */ -@property (readonly, strong) JSContext *context; +@property (readonly, strong) JSContext * _Null_unspecified context; /*! @methodgroup Creating JavaScript Values @@ -63,7 +63,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param value The Objective-C object to be converted. @result The new JSValue. */ -+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithObject:(id _Null_unspecified)value inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -71,7 +71,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting JSValue will be created. @result The new JSValue representing the equivalent boolean value. */ -+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithBool:(BOOL)value inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -79,7 +79,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting JSValue will be created. @result The new JSValue representing the equivalent boolean value. */ -+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithDouble:(double)value inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -87,7 +87,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting JSValue will be created. @result The new JSValue representing the equivalent boolean value. */ -+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithInt32:(int32_t)value inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -95,7 +95,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting JSValue will be created. @result The new JSValue representing the equivalent boolean value. */ -+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithUInt32:(uint32_t)value inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -103,7 +103,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting object will be created. @result The new JavaScript object. */ -+ (JSValue *)valueWithNewObjectInContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithNewObjectInContext:(JSContext * _Null_unspecified)context; /*! @method @@ -111,7 +111,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting array will be created. @result The new JavaScript array. */ -+ (JSValue *)valueWithNewArrayInContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithNewArrayInContext:(JSContext * _Null_unspecified)context; /*! @method @@ -121,7 +121,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting regular expression object will be created. @result The new JavaScript regular expression object. */ -+ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithNewRegularExpressionFromPattern:(NSString * _Null_unspecified)pattern flags:(NSString * _Null_unspecified)flags inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -130,7 +130,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext in which the resulting error object will be created. @result The new JavaScript error object. */ -+ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithNewErrorFromMessage:(NSString * _Null_unspecified)message inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -140,7 +140,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @result The JSValue representing a new promise JavaScript object. @discussion This method is equivalent to calling the Promise constructor in JavaScript. the resolve and reject callbacks each normally take a single value, which they forward to all relevent pending reactions. While inside the executor callback context will act as if it were in any other callback, except calleeFunction will be nil. This also means means the new promise object may be accessed via [context thisValue]. */ -+ (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *resolve, JSValue *reject))callback JSC_API_AVAILABLE(macos(10.15), ios(13.0)); ++ (JSValue * _Null_unspecified)valueWithNewPromiseInContext:(JSContext * _Null_unspecified)context fromExecutor:(void (^ _Null_unspecified)(JSValue * _Null_unspecified resolve, JSValue * _Null_unspecified reject))callback JSC_API_AVAILABLE(macos(10.15), ios(13.0)); /*! @method @@ -150,7 +150,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @result The JSValue representing a new promise JavaScript object. @discussion This method is equivalent to calling [JSValue valueWithNewPromiseFromExecutor:^(JSValue *resolve, JSValue *reject) { [resolve callWithArguments:@[result]]; } inContext:context] */ -+ (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); ++ (JSValue * _Null_unspecified)valueWithNewPromiseResolvedWithResult:(id _Null_unspecified)result inContext:(JSContext * _Null_unspecified)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); /*! @method @@ -160,7 +160,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @result The JSValue representing a new promise JavaScript object. @discussion This method is equivalent to calling [JSValue valueWithNewPromiseFromExecutor:^(JSValue *resolve, JSValue *reject) { [reject callWithArguments:@[reason]]; } inContext:context] */ -+ (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); ++ (JSValue * _Null_unspecified)valueWithNewPromiseRejectedWithReason:(id _Null_unspecified)reason inContext:(JSContext * _Null_unspecified)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); /*! @method @@ -169,7 +169,45 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext to which the resulting JSValue belongs. @result The JSValue representing a unique JavaScript value with type symbol. */ -+ (JSValue *)valueWithNewSymbolFromDescription:(NSString *)description inContext:(JSContext *)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); ++ (JSValue * _Null_unspecified)valueWithNewSymbolFromDescription:(NSString * _Null_unspecified)description inContext:(JSContext * _Null_unspecified)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@method +@abstract Create a new BigInt value from a numeric string. +@param string The string representation of the BigInt JavaScript value being created. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a JavaScript value with type BigInt. +@discussion This is equivalent to calling the BigInt constructor from JavaScript with a string argument. +*/ ++ (nullable JSValue *)valueWithNewBigIntFromString:(nonnull NSString *)string inContext:(nonnull JSContext *)context JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Create a new BigInt value from a int64_t. +@param int64 The signed 64-bit integer of the BigInt JavaScript value being created. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a JavaScript value with type BigInt. +*/ ++ (nullable JSValue *)valueWithNewBigIntFromInt64:(int64_t)int64 inContext:(nonnull JSContext *)context JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Create a new BigInt value from a uint64_t. +@param uint64 The unsigned 64-bit integer of the BigInt JavaScript value being created. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a JavaScript value with type BigInt. +*/ ++ (nullable JSValue *)valueWithNewBigIntFromUInt64:(uint64_t)uint64 inContext:(nonnull JSContext *)context JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Create a new BigInt value from a double. +@param value The value of the BigInt JavaScript value being created. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a JavaScript value with type BigInt. +@discussion If the value is not an integer, an exception is thrown. +*/ ++ (nullable JSValue *)valueWithNewBigIntFromDouble:(double)value inContext:(nonnull JSContext *)context JSC_API_AVAILABLE(macos(15.0), ios(18.0)); /*! @method @@ -177,7 +215,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext to which the resulting JSValue belongs. @result The JSValue representing the JavaScript value null. */ -+ (JSValue *)valueWithNullInContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithNullInContext:(JSContext * _Null_unspecified)context; /*! @method @@ -185,7 +223,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param context The JSContext to which the resulting JSValue belongs. @result The JSValue representing the JavaScript value undefined. */ -+ (JSValue *)valueWithUndefinedInContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithUndefinedInContext:(JSContext * _Null_unspecified)context; /*! @methodgroup Converting to Objective-C Types @@ -240,7 +278,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) to the conversion rules specified above. @result The Objective-C representation of this JSValue. */ -- (id)toObject; +- (id _Null_unspecified)toObject; /*! @method @@ -249,7 +287,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) If the result is not of the specified Class then nil will be returned. @result An Objective-C object of the specified Class or nil. */ -- (id)toObjectOfClass:(Class)expectedClass; +- (id _Null_unspecified)toObjectOfClass:(Class _Null_unspecified)expectedClass; /*! @method @@ -263,17 +301,15 @@ NS_CLASS_AVAILABLE(10_9, 7_0) /*! @method @abstract Convert a JSValue to a double. -@discussion The JSValue is converted to a number according to the rules specified - by the JavaScript language. @result The double result of the conversion. +@discussion Convert the JSValue to a number according to the rules specified by the JavaScript language. Unless the JSValue is a BigInt then this is equivalent to Number(value) in JavaScript. */ - (double)toDouble; /*! @method @abstract Convert a JSValue to an int32_t. -@discussion The JSValue is converted to an integer according to the rules specified - by the JavaScript language. +@discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the JSValue is a BigInt, then the value is truncated to an int32_t. @result The int32_t result of the conversion. */ - (int32_t)toInt32; @@ -281,21 +317,33 @@ NS_CLASS_AVAILABLE(10_9, 7_0) /*! @method @abstract Convert a JSValue to a uint32_t. -@discussion The JSValue is converted to an integer according to the rules specified - by the JavaScript language. +@discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the JSValue is a BigInt, then the value is truncated to a uint32_t. @result The uint32_t result of the conversion. */ - (uint32_t)toUInt32; +/*! +@method +@abstract Convert a JSValue to a int64_t. +@discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the value is a BigInt, then the value is truncated to an int64_t. +*/ +- (int64_t)toInt64 JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Convert a JSValue to a uint64_t. +@discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the value is a BigInt, then the value is truncated to a uint64_t. +*/ +- (uint64_t)toUInt64 JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + /*! @method @abstract Convert a JSValue to a NSNumber. -@discussion If the JSValue represents a boolean, a NSNumber value of YES or NO - will be returned. For all other types the value will be converted to a number according - to the rules specified by the JavaScript language. +@discussion If the JSValue represents a boolean, a NSNumber value of YES or NO + will be returned. For all other types, the result is equivalent to Number(value) in JavaScript. @result The NSNumber result of the conversion. */ -- (NSNumber *)toNumber; +- (NSNumber * _Null_unspecified)toNumber; /*! @method @@ -304,7 +352,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) by the JavaScript language. @result The NSString containing the result of the conversion. */ -- (NSString *)toString; +- (NSString * _Null_unspecified)toString; /*! @method @@ -313,7 +361,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) since 1970 which is then used to create a new NSDate instance. @result The NSDate created using the converted time interval. */ -- (NSDate *)toDate; +- (NSDate * _Null_unspecified)toDate; /*! @method @@ -322,12 +370,12 @@ NS_CLASS_AVAILABLE(10_9, 7_0) If the value is not an object then a JavaScript TypeError will be thrown. The property length is read from the object, converted to an unsigned integer, and an NSArray of this size is allocated. Properties corresponding - to indicies within the array bounds will be copied to the array, with + to indices within the array bounds will be copied to the array, with JSValues converted to equivalent Objective-C objects as specified. @result The NSArray containing the recursively converted contents of the converted JavaScript array. */ -- (NSArray *)toArray; +- (NSArray * _Null_unspecified)toArray; /*! @method @@ -339,10 +387,10 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @result The NSDictionary containing the recursively converted contents of the converted JavaScript object. */ -- (NSDictionary *)toDictionary; +- (NSDictionary * _Null_unspecified)toDictionary; /*! -@functiongroup Checking JavaScript Types +@methodgroup Checking JavaScript Types */ /*! @@ -402,26 +450,72 @@ NS_CLASS_AVAILABLE(10_9, 7_0) */ @property (readonly) BOOL isSymbol JSC_API_AVAILABLE(macos(10.15), ios(13.0)); +/*! +@property +@abstract Check if a JSValue is a BigInt. +*/ +@property (readonly) BOOL isBigInt JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Check if a JSValue is an instance of another object. +@discussion This method has the same function as the JavaScript operator instanceof. + If an object other than a JSValue is passed, it will first be converted according to + the aforementioned rules. +*/ +- (BOOL)isInstanceOf:(id _Null_unspecified)value; + +/*! +@methodgroup Compare JavaScript values +*/ + /*! @method @abstract Compare two JSValues using JavaScript's === operator. */ -- (BOOL)isEqualToObject:(id)value; +- (BOOL)isEqualToObject:(id _Null_unspecified)value; /*! @method @abstract Compare two JSValues using JavaScript's == operator. */ -- (BOOL)isEqualWithTypeCoercionToObject:(id)value; +- (BOOL)isEqualWithTypeCoercionToObject:(id _Null_unspecified)value; /*! @method -@abstract Check if a JSValue is an instance of another object. -@discussion This method has the same function as the JavaScript operator instanceof. - If an object other than a JSValue is passed, it will first be converted according to - the aforementioned rules. +@abstract Compare two JSValues. +@other The JSValue to compare with. +@result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. +@discussion The result is computed by comparing the results of JavaScript's ==, <, and > operators. If either self or other is (or would coerce to) NaN in JavaScript, then the result is kJSRelationConditionUndefined. +*/ +- (JSRelationCondition)compareJSValue:(nonnull JSValue *)other JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Compare a JSValue with a int64_t. +@other The int64_t to compare with. +@result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. +@discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language then compared with other. +*/ +- (JSRelationCondition)compareInt64:(int64_t)other JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Compare a JSValue with a uint64_t. +@other The uint64_t to compare with. +@result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. +@discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language then compared with other. +*/ +- (JSRelationCondition)compareUInt64:(uint64_t)other JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! +@method +@abstract Compare a JSValue with a double. +@other The double to compare with. +@result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. +@discussion The JSValue is converted to a double according to the rules specified by the JavaScript language then compared with other. */ -- (BOOL)isInstanceOf:(id)value; +- (JSRelationCondition)compareDouble:(double)other JSC_API_AVAILABLE(macos(15.0), ios(18.0)); /*! @methodgroup Calling Functions and Constructors @@ -434,7 +528,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param arguments The arguments to pass to the function. @result The return value of the function call. */ -- (JSValue *)callWithArguments:(NSArray *)arguments; +- (JSValue * _Null_unspecified)callWithArguments:(NSArray * _Null_unspecified)arguments; /*! @method @@ -443,7 +537,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param arguments The arguments to pass to the constructor. @result The return value of the constructor call. */ -- (JSValue *)constructWithArguments:(NSArray *)arguments; +- (JSValue * _Null_unspecified)constructWithArguments:(NSArray * _Null_unspecified)arguments; /*! @method @@ -455,7 +549,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @param arguments The arguments to pass to the method. @result The return value of the method call. */ -- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments; +- (JSValue * _Null_unspecified)invokeMethod:(NSString * _Null_unspecified)method withArguments:(NSArray * _Null_unspecified)arguments; @end @@ -478,7 +572,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @result A newly allocated JavaScript object containing properties named x and y, with values from the CGPoint. */ -+ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithPoint:(CGPoint)point inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -486,7 +580,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @result A newly allocated JavaScript object containing properties named location and length, with values from the NSRange. */ -+ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithRange:(NSRange)range inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -495,7 +589,7 @@ Create a JSValue from a CGRect. @result A newly allocated JavaScript object containing properties named x, y, width, and height, with values from the CGRect. */ -+ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithRect:(CGRect)rect inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -503,7 +597,7 @@ Create a JSValue from a CGRect. @result A newly allocated JavaScript object containing properties named width and height, with values from the CGSize. */ -+ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithSize:(CGSize)size inContext:(JSContext * _Null_unspecified)context; /*! @method @@ -550,9 +644,9 @@ Create a JSValue from a CGRect. @interface JSValue (PropertyAccess) #if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500) || (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000) -typedef NSString *JSValueProperty; +typedef NSString * _Null_unspecified JSValueProperty; #else -typedef id JSValueProperty; +typedef id _Null_unspecified JSValueProperty; #endif /*! @@ -562,14 +656,14 @@ typedef id JSValueProperty; if the property does not exist. @discussion Corresponds to the JavaScript operation object[property]. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. */ -- (JSValue *)valueForProperty:(JSValueProperty)property; +- (JSValue * _Null_unspecified)valueForProperty:(JSValueProperty)property; /*! @method @abstract Set a property on a JSValue. @discussion Corresponds to the JavaScript operation object[property] = value. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. */ -- (void)setValue:(id)value forProperty:(JSValueProperty)property; +- (void)setValue:(id _Null_unspecified)value forProperty:(JSValueProperty)property; /*! @method @@ -594,7 +688,7 @@ typedef id JSValueProperty; @discussion This method may be used to create a data or accessor property on an object. This method operates in accordance with the Object.defineProperty method in the JavaScript language. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. */ -- (void)defineProperty:(JSValueProperty)property descriptor:(id)descriptor; +- (void)defineProperty:(JSValueProperty)property descriptor:(id _Null_unspecified)descriptor; /*! @method @@ -602,7 +696,7 @@ typedef id JSValueProperty; @result The JSValue for the property at the specified index. Returns the JavaScript value undefined if no property exists at that index. */ -- (JSValue *)valueAtIndex:(NSUInteger)index; +- (JSValue * _Null_unspecified)valueAtIndex:(NSUInteger)index; /*! @method @@ -610,7 +704,7 @@ typedef id JSValueProperty; @discussion For JSValues that are JavaScript arrays, indices greater than UINT_MAX - 1 will not affect the length of the array. */ -- (void)setValue:(id)value atIndex:(NSUInteger)index; +- (void)setValue:(id _Null_unspecified)value atIndex:(NSUInteger)index; @end @@ -635,10 +729,10 @@ typedef id JSValueProperty; */ @interface JSValue (SubscriptSupport) -- (JSValue *)objectForKeyedSubscript:(id)key; -- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index; -- (void)setObject:(id)object forKeyedSubscript:(id)key; -- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index; +- (JSValue * _Null_unspecified)objectForKeyedSubscript:(id _Null_unspecified)key; +- (JSValue * _Null_unspecified)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(id _Null_unspecified)object forKeyedSubscript:(id _Null_unspecified)key; +- (void)setObject:(id _Null_unspecified)object atIndexedSubscript:(NSUInteger)index; @end @@ -653,14 +747,14 @@ typedef id JSValueProperty; @abstract Creates a JSValue, wrapping its C API counterpart. @result The Objective-C API equivalent of the specified JSValueRef. */ -+ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context; ++ (JSValue * _Null_unspecified)valueWithJSValueRef:(JSValueRef _Null_unspecified)value inContext:(JSContext * _Null_unspecified)context; /*! @property @abstract Returns the C API counterpart wrapped by a JSContext. @result The C API equivalent of this JSValue. */ -@property (readonly) JSValueRef JSValueRef; +@property (readonly) JSValueRef _Null_unspecified JSValueRef; @end #ifdef __cplusplus @@ -699,27 +793,27 @@ extern "C" { /*! @const */ -JS_EXPORT extern NSString * const JSPropertyDescriptorWritableKey; +JS_EXPORT extern NSString * _Null_unspecified const JSPropertyDescriptorWritableKey; /*! @const */ -JS_EXPORT extern NSString * const JSPropertyDescriptorEnumerableKey; +JS_EXPORT extern NSString * _Null_unspecified const JSPropertyDescriptorEnumerableKey; /*! @const */ -JS_EXPORT extern NSString * const JSPropertyDescriptorConfigurableKey; +JS_EXPORT extern NSString * _Null_unspecified const JSPropertyDescriptorConfigurableKey; /*! @const */ -JS_EXPORT extern NSString * const JSPropertyDescriptorValueKey; +JS_EXPORT extern NSString * _Null_unspecified const JSPropertyDescriptorValueKey; /*! @const */ -JS_EXPORT extern NSString * const JSPropertyDescriptorGetKey; +JS_EXPORT extern NSString * _Null_unspecified const JSPropertyDescriptorGetKey; /*! @const */ -JS_EXPORT extern NSString * const JSPropertyDescriptorSetKey; +JS_EXPORT extern NSString * _Null_unspecified const JSPropertyDescriptorSetKey; #ifdef __cplusplus } // extern "C" @@ -727,4 +821,4 @@ JS_EXPORT extern NSString * const JSPropertyDescriptorSetKey; #endif -#endif // JSValue_h +#endif /* JSValue_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValueInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValueInternal.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValueInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValueInternal.h index 54b755ef..a9369553 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValueInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValueInternal.h @@ -28,6 +28,10 @@ #import +#ifdef __cplusplus +extern "C" { +#endif + #if JSC_OBJC_API_ENABLED @interface JSValue(Internal) @@ -54,4 +58,8 @@ NSInvocation *valueToTypeInvocationFor(const char* encodedType); #endif +#ifdef __cplusplus +} +#endif + #endif // JSValueInternal_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValuePrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValuePrivate.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValuePrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValuePrivate.h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValueRef.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValueRef.h new file mode 100644 index 00000000..c1feecc3 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSValueRef.h @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2006-2024 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSValueRef_h +#define JSValueRef_h +#ifndef __cplusplus +#include +#endif +#include "JSBase.h" +#include "WebKitAvailability.h" + +#ifndef __cplusplus +#include +#endif +#include /* for size_t */ +#include /* for int64_t and uint64_t */ + +/*! +@enum JSType +@abstract A constant identifying the type of a JSValue. +@constant kJSTypeUndefined The unique undefined value. +@constant kJSTypeNull The unique null value. +@constant kJSTypeBoolean A primitive boolean value, one of true or false. +@constant kJSTypeNumber A primitive number value. +@constant kJSTypeString A primitive string value. +@constant kJSTypeObject An object value (meaning that this JSValueRef is a JSObjectRef). +@constant kJSTypeSymbol A primitive symbol value. +@constant kJSTypeBigInt A primitive BigInt value. +*/ +typedef enum { + kJSTypeUndefined, + kJSTypeNull, + kJSTypeBoolean, + kJSTypeNumber, + kJSTypeString, + kJSTypeObject, + kJSTypeSymbol JSC_API_AVAILABLE(macos(10.15), ios(13.0)), + kJSTypeBigInt JSC_API_AVAILABLE(macos(15.0), ios(18.0)) +} JSType; + +/*! + @enum JSTypedArrayType + @abstract A constant identifying the Typed Array type of a JSObjectRef. + @constant kJSTypedArrayTypeInt8Array Int8Array + @constant kJSTypedArrayTypeInt16Array Int16Array + @constant kJSTypedArrayTypeInt32Array Int32Array + @constant kJSTypedArrayTypeUint8Array Uint8Array + @constant kJSTypedArrayTypeUint8ClampedArray Uint8ClampedArray + @constant kJSTypedArrayTypeUint16Array Uint16Array + @constant kJSTypedArrayTypeUint32Array Uint32Array + @constant kJSTypedArrayTypeFloat32Array Float32Array + @constant kJSTypedArrayTypeFloat64Array Float64Array + @constant kJSTypedArrayTypeBigInt64Array BigInt64Array + @constant kJSTypedArrayTypeBigUint64Array BigUint64Array + @constant kJSTypedArrayTypeArrayBuffer ArrayBuffer + @constant kJSTypedArrayTypeNone Not a Typed Array + + */ +typedef enum { + kJSTypedArrayTypeInt8Array, + kJSTypedArrayTypeInt16Array, + kJSTypedArrayTypeInt32Array, + kJSTypedArrayTypeUint8Array, + kJSTypedArrayTypeUint8ClampedArray, + kJSTypedArrayTypeUint16Array, + kJSTypedArrayTypeUint32Array, + kJSTypedArrayTypeFloat32Array, + kJSTypedArrayTypeFloat64Array, + kJSTypedArrayTypeArrayBuffer, + kJSTypedArrayTypeNone, + kJSTypedArrayTypeBigInt64Array, + kJSTypedArrayTypeBigUint64Array, +} JSTypedArrayType JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! +@enum JSRelationCondition +@abstract A constant identifying the type of JavaScript relation condition. +@constant kJSRelationConditionUndefined Fail to compare two operands. +@constant kJSRelationConditionEqual Two operands have equivalent values. +@constant kJSRelationConditionGreaterThan The left operand is greater than the right operand. +@constant kJSRelationConditionLessThan The left operand is less than the right operand. +*/ +JSC_CF_ENUM(JSRelationCondition, + kJSRelationConditionUndefined, + kJSRelationConditionEqual, + kJSRelationConditionGreaterThan, + kJSRelationConditionLessThan +) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Returns a JavaScript value's type. +@param ctx The execution context to use. +@param value The JSValue whose type you want to obtain. +@result A value of type JSType that identifies value's type. +*/ +JS_EXPORT JSType JSValueGetType(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the undefined type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the undefined type, otherwise false. +*/ +JS_EXPORT bool JSValueIsUndefined(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the null type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the null type, otherwise false. +*/ +JS_EXPORT bool JSValueIsNull(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the boolean type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the boolean type, otherwise false. +*/ +JS_EXPORT bool JSValueIsBoolean(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the number type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the number type, otherwise false. +*/ +JS_EXPORT bool JSValueIsNumber(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the string type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the string type, otherwise false. +*/ +JS_EXPORT bool JSValueIsString(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the symbol type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the symbol type, otherwise false. +*/ +JS_EXPORT bool JSValueIsSymbol(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +JSC_ASSUME_NONNULL_BEGIN +/*! +@function +@abstract Tests whether a JavaScript value's type is the BigInt type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the BigInt type, otherwise false. +*/ +JS_EXPORT bool JSValueIsBigInt(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); +JSC_ASSUME_NONNULL_END + +/*! +@function +@abstract Tests whether a JavaScript value's type is the object type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the object type, otherwise false. +*/ +JS_EXPORT bool JSValueIsObject(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + + +/*! +@function +@abstract Tests whether a JavaScript value is an object with a given class in its class chain. +@param ctx The execution context to use. +@param value The JSValue to test. +@param jsClass The JSClass to test against. +@result true if value is an object and has jsClass in its class chain, otherwise false. +*/ +JS_EXPORT bool JSValueIsObjectOfClass(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, JSC_NULL_UNSPECIFIED JSClassRef jsClass); + +/*! +@function +@abstract Tests whether a JavaScript value is an array. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value is an array, otherwise false. +*/ +JS_EXPORT bool JSValueIsArray(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +/*! +@function +@abstract Tests whether a JavaScript value is a date. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value is a date, otherwise false. +*/ +JS_EXPORT bool JSValueIsDate(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +/*! +@function +@abstract Returns a JavaScript value's Typed Array type. +@param ctx The execution context to use. +@param value The JSValue whose Typed Array type to return. +@param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. +@result A value of type JSTypedArrayType that identifies value's Typed Array type, or kJSTypedArrayTypeNone if the value is not a Typed Array object. + */ +JS_EXPORT JSTypedArrayType JSValueGetTypedArrayType(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/* Comparing values */ + +/*! +@function +@abstract Tests whether two JavaScript values are equal, as compared by the JS == operator. +@param ctx The execution context to use. +@param a The first value to test. +@param b The second value to test. +@param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. +@result true if the two values are equal, false if they are not equal or an exception is thrown. +*/ +JS_EXPORT bool JSValueIsEqual(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef a, JSC_NULL_UNSPECIFIED JSValueRef b, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception); + +/*! +@function +@abstract Tests whether two JavaScript values are strict equal, as compared by the JS === operator. +@param ctx The execution context to use. +@param a The first value to test. +@param b The second value to test. +@result true if the two values are strict equal, otherwise false. +*/ +JS_EXPORT bool JSValueIsStrictEqual(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef a, JSC_NULL_UNSPECIFIED JSValueRef b); + +/*! +@function +@abstract Tests whether a JavaScript value is an object constructed by a given constructor, as compared by the JS instanceof operator. +@param ctx The execution context to use. +@param value The JSValue to test. +@param constructor The constructor to test against. +@param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. +@result true if value is an object constructed by constructor, as compared by the JS instanceof operator, otherwise false. +*/ +JS_EXPORT bool JSValueIsInstanceOfConstructor(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, JSC_NULL_UNSPECIFIED JSObjectRef constructor, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception); + +JSC_ASSUME_NONNULL_BEGIN +/*! + @function + @abstract Compares two JSValues. + @param ctx The execution context to use. + @param left The JSValue as the left operand. + @param right The JSValue as the right operand. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. + @discussion The result is computed by comparing the results of JavaScript's `==`, `<`, and `>` operators. If either `left` or `right` is (or would coerce to) `NaN` in JavaScript, then the result is kJSRelationConditionUndefined. +*/ +JS_EXPORT JSRelationCondition JSValueCompare(JSContextRef ctx, JSValueRef left, JSValueRef right, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Compares a JSValue with a signed 64-bit integer. + @param ctx The execution context to use. + @param left The JSValue as the left operand. + @param right The int64_t as the right operand. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. + @discussion `left` is converted to an integer according to the rules specified by the JavaScript language then compared with `right`. +*/ +JS_EXPORT JSRelationCondition JSValueCompareInt64(JSContextRef ctx, JSValueRef left, int64_t right, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Compares a JSValue with an unsigned 64-bit integer. + @param ctx The execution context to use. + @param left The JSValue as the left operand. + @param right The uint64_t as the right operand. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. + @discussion `left` is converted to an integer according to the rules specified by the JavaScript language then compared with `right`. +*/ +JS_EXPORT JSRelationCondition JSValueCompareUInt64(JSContextRef ctx, JSValueRef left, uint64_t right, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Compares a JSValue with a double. + @param ctx The execution context to use. + @param left The JSValue as the left operand. + @param right The double as the right operand. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A value of JSRelationCondition, a kJSRelationConditionUndefined is returned if an exception is thrown. + @discussion `left` is converted to a double according to the rules specified by the JavaScript language then compared with `right`. +*/ +JS_EXPORT JSRelationCondition JSValueCompareDouble(JSContextRef ctx, JSValueRef left, double right, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); +JSC_ASSUME_NONNULL_END + +/* Creating values */ + +/*! +@function +@abstract Creates a JavaScript value of the undefined type. +@param ctx The execution context to use. +@result The unique undefined value. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeUndefined(JSC_NULL_UNSPECIFIED JSContextRef ctx); + +/*! +@function +@abstract Creates a JavaScript value of the null type. +@param ctx The execution context to use. +@result The unique null value. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeNull(JSC_NULL_UNSPECIFIED JSContextRef ctx); + +/*! +@function +@abstract Creates a JavaScript value of the boolean type. +@param ctx The execution context to use. +@param boolean The bool to assign to the newly created JSValue. +@result A JSValue of the boolean type, representing the value of boolean. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeBoolean(JSC_NULL_UNSPECIFIED JSContextRef ctx, bool boolean); + +/*! +@function +@abstract Creates a JavaScript value of the number type. +@param ctx The execution context to use. +@param number The double to assign to the newly created JSValue. +@result A JSValue of the number type, representing the value of number. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeNumber(JSC_NULL_UNSPECIFIED JSContextRef ctx, double number); + +/*! +@function +@abstract Creates a JavaScript value of the string type. +@param ctx The execution context to use. +@param string The JSString to assign to the newly created JSValue. The + newly created JSValue retains string, and releases it upon garbage collection. +@result A JSValue of the string type, representing the value of string. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeString(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSStringRef string); + +/*! + @function + @abstract Creates a JavaScript value of the symbol type. + @param ctx The execution context to use. + @param description A description of the newly created symbol value. + @result A unique JSValue of the symbol type, whose description matches the one provided. + */ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeSymbol(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSStringRef description) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +JSC_ASSUME_NONNULL_BEGIN +/*! + @function + @abstract Creates a JavaScript BigInt with a double. + @param ctx The execution context to use. + @param value The value to copy into the new BigInt JSValue. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A BigInt JSValue of the value, or NULL if an exception is thrown. + @discussion If the value is not an integer, an exception is thrown. +*/ +JS_EXPORT JSValueRef JSBigIntCreateWithDouble(JSContextRef ctx, double value, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Creates a JavaScript BigInt with a 64-bit signed integer. + @param ctx The execution context to use. + @param integer The 64-bit signed integer to copy into the new BigInt JSValue. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A BigInt JSValue of the integer, or NULL if an exception is thrown. +*/ +JS_EXPORT JSValueRef JSBigIntCreateWithInt64(JSContextRef ctx, int64_t integer, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Creates a JavaScript BigInt with a 64-bit unsigned integer. + @param ctx The execution context to use. + @param integer The 64-bit unsigned integer to copy into the new BigInt JSValue. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A BigInt JSValue of the integer, or NULL if an exception is thrown. +*/ +JS_EXPORT JSValueRef JSBigIntCreateWithUInt64(JSContextRef ctx, uint64_t integer, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Creates a JavaScript BigInt with an integer represented in string. + @param ctx The execution context to use. + @param string The JSStringRef representation of an integer. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A BigInt JSValue of the string, or NULL if an exception is thrown. + @discussion This is equivalent to calling the `BigInt` constructor from JavaScript with a string argument. +*/ +JS_EXPORT JSValueRef JSBigIntCreateWithString(JSContextRef ctx, JSStringRef string, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); +JSC_ASSUME_NONNULL_END + +/* Converting to and from JSON formatted strings */ + +/*! + @function + @abstract Creates a JavaScript value from a JSON formatted string. + @param ctx The execution context to use. + @param string The JSString containing the JSON string to be parsed. + @result A JSValue containing the parsed value, or NULL if the input is invalid. + */ +JS_EXPORT JSC_NULL_UNSPECIFIED JSValueRef JSValueMakeFromJSONString(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSStringRef string) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); + +/*! + @function + @abstract Creates a JavaScript string containing the JSON serialized representation of a JS value. + @param ctx The execution context to use. + @param value The value to serialize. + @param indent The number of spaces to indent when nesting. If 0, the resulting JSON will not contains newlines. The size of the indent is clamped to 10 spaces. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A JSString with the result of serialization, or NULL if an exception is thrown. + */ +JS_EXPORT JSC_NULL_UNSPECIFIED JSStringRef JSValueCreateJSONString(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, unsigned indent, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); + +/* Converting to primitive values */ + +/*! +@function +@abstract Converts a JavaScript value to boolean and returns the resulting boolean. +@param ctx The execution context to use. +@param value The JSValue to convert. +@result The boolean result of conversion. +*/ +JS_EXPORT bool JSValueToBoolean(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Converts a JavaScript value to number and returns the resulting number. +@param ctx The execution context to use. +@param value The JSValue to convert. +@param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. +@result The numeric result of conversion, or NaN if an exception is thrown. +@discussion The result is equivalent to `Number(value)` in JavaScript. +*/ +JS_EXPORT double JSValueToNumber(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception); + +JSC_ASSUME_NONNULL_BEGIN +/*! + @function + @abstract Converts a JSValue to a singed 32-bit integer and returns the resulting integer. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result An int32_t with the result of conversion, or 0 if an exception is thrown. Since 0 is valid value, `exception` must be checked after the call. + @discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the value is a BigInt, then the JSValue is truncated to an int32_t. +*/ +JS_EXPORT int32_t JSValueToInt32(JSContextRef ctx, JSValueRef value, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Converts a JSValue to an unsigned 32-bit integer and returns the resulting integer. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A uint32_t with the result of conversion, or 0 if an exception is thrown. Since 0 is valid value, `exception` must be checked after the call. + @discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the value is a BigInt, then the JSValue is truncated to a uint32_t. +*/ +JS_EXPORT uint32_t JSValueToUInt32(JSContextRef ctx, JSValueRef value, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Converts a JSValue to a singed 64-bit integer and returns the resulting integer. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result An int64_t with the result of conversion, or 0 if an exception is thrown. Since 0 is valid value, `exception` must be checked after the call. + @discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the value is a BigInt, then the JSValue is truncated to an int64_t. +*/ +JS_EXPORT int64_t JSValueToInt64(JSContextRef ctx, JSValueRef value, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); + +/*! + @function + @abstract Converts a JSValue to an unsigned 64-bit integer and returns the resulting integer. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. + @result A uint64_t with the result of conversion, or 0 if an exception is thrown. Since 0 is valid value, `exception` must be checked after the call. + @discussion The JSValue is converted to an integer according to the rules specified by the JavaScript language. If the value is a BigInt, then the JSValue is truncated to a uint64_t. +*/ +JS_EXPORT uint64_t JSValueToUInt64(JSContextRef ctx, JSValueRef value, JSC_NULLABLE JSValueRef* JSC_NULLABLE exception) JSC_API_AVAILABLE(macos(15.0), ios(18.0)); +JSC_ASSUME_NONNULL_END + +/*! +@function +@abstract Converts a JavaScript value to string and copies the result into a JavaScript string. +@param ctx The execution context to use. +@param value The JSValue to convert. +@param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. +@result A JSString with the result of conversion, or NULL if an exception is thrown. Ownership follows the Create Rule. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSStringRef JSValueToStringCopy(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception); + +/*! +@function +@abstract Converts a JavaScript value to object and returns the resulting object. +@param ctx The execution context to use. +@param value The JSValue to convert. +@param exception A pointer to a JSValueRef in which to store an exception, if any. To reliable detect exception, initialize this to null before the call. Pass NULL if you do not care to store an exception. +@result The JSObject result of conversion, or NULL if an exception is thrown. +*/ +JS_EXPORT JSC_NULL_UNSPECIFIED JSObjectRef JSValueToObject(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value, JSC_NULL_UNSPECIFIED JSValueRef* JSC_NULL_UNSPECIFIED exception); + +/* Garbage collection */ +/*! +@function +@abstract Protects a JavaScript value from garbage collection. +@param ctx The execution context to use. +@param value The JSValue to protect. +@discussion Use this method when you want to store a JSValue in a global or on the heap, where the garbage collector will not be able to discover your reference to it. + +A value may be protected multiple times and must be unprotected an equal number of times before becoming eligible for garbage collection. +*/ +JS_EXPORT void JSValueProtect(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + +/*! +@function +@abstract Unprotects a JavaScript value from garbage collection. +@param ctx The execution context to use. +@param value The JSValue to unprotect. +@discussion A value may be protected multiple times and must be unprotected an + equal number of times before becoming eligible for garbage collection. +*/ +JS_EXPORT void JSValueUnprotect(JSC_NULL_UNSPECIFIED JSContextRef ctx, JSC_NULL_UNSPECIFIED JSValueRef value); + + +#ifdef __cplusplus +} +#endif + +#endif /* JSValueRef_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachine.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachine.h similarity index 93% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachine.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachine.h index e9c75da0..3060b883 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachine.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachine.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,9 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import +#ifndef JSVirtualMachine_h +#define JSVirtualMachine_h -#if JSC_OBJC_API_ENABLED +#include "JavaScriptCore.h" + +#if defined(__OBJC__) && JSC_OBJC_API_ENABLED /*! @interface @@ -85,3 +88,5 @@ NS_CLASS_AVAILABLE(10_9, 7_0) @end #endif + +#endif /* JSVirtualMachine_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachineInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachineInternal.h similarity index 87% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachineInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachineInternal.h index b533482a..08a9c4e8 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachineInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachineInternal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2017 Apple Inc. All rights reserved. + * Copyright (C) 2013-2021 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,7 +32,7 @@ namespace JSC { class VM; -class SlotVisitor; +class AbstractSlotVisitor; } #if defined(__OBJC__) @@ -46,16 +46,16 @@ JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *); - (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext; - (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext; -- (JSC::VM&)vm; - - (BOOL)isWebThreadAware; +@property (readonly) JSContextGroupRef JSContextGroupRef; + @end #endif // defined(__OBJC__) -void scanExternalObjectGraph(JSC::VM&, JSC::SlotVisitor&, void* root); -void scanExternalRememberedSet(JSC::VM&, JSC::SlotVisitor&); +void scanExternalObjectGraph(JSC::VM&, JSC::AbstractSlotVisitor&, void* root); +void scanExternalRememberedSet(JSC::VM&, JSC::AbstractSlotVisitor&); #endif // JSC_OBJC_API_ENABLED diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachinePrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachinePrivate.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachinePrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachinePrivate.h index 950afc73..ff8906a2 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSVirtualMachinePrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSVirtualMachinePrivate.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "JSExportMacros.h" -#include +#import +#import #if JSC_OBJC_API_ENABLED @@ -45,8 +45,6 @@ - (void)shrinkFootprintWhenIdle JSC_API_AVAILABLE(macos(10.14), ios(12.0)); -#if ENABLE(DFG_JIT) - /*! @method @abstract Set the number of threads to be used by the DFG JIT compiler. @@ -80,8 +78,6 @@ */ + (void)setCrashOnVMCreation:(BOOL)shouldCrash; -#endif // ENABLE(DFG_JIT) - @end #endif // JSC_OBJC_API_ENABLED diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakObjectMapRefInternal.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakObjectMapRefInternal.h similarity index 88% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakObjectMapRefInternal.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakObjectMapRefInternal.h index 9037947d..957dcb00 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakObjectMapRefInternal.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakObjectMapRefInternal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,6 +39,9 @@ typedef void (*JSWeakMapDestroyedCallback)(struct OpaqueJSWeakObjectMap*, void*) typedef JSC::WeakGCMap WeakMapType; +#define OPAQUE_JSWEAK_OBJECT_MAP_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("OpaqueJSWeakObjectMap." #method) method + struct OpaqueJSWeakObjectMap : public RefCounted { public: static Ref create(JSC::VM& vm, void* data, JSWeakMapDestroyedCallback callback) @@ -62,8 +65,9 @@ struct OpaqueJSWeakObjectMap : public RefCounted { } WeakMapType m_map; void* m_data; - JSWeakMapDestroyedCallback m_callback; + JSWeakMapDestroyedCallback OPAQUE_JSWEAK_OBJECT_MAP_METHOD(m_callback); }; +#undef OPAQUE_JSWEAK_OBJECT_MAP_METHOD #endif // JSWeakObjectMapInternal_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakObjectMapRefPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakObjectMapRefPrivate.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakObjectMapRefPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakObjectMapRefPrivate.h index a335e23c..1df7f4a6 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakObjectMapRefPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakObjectMapRefPrivate.h @@ -26,8 +26,8 @@ #ifndef JSWeakObjectMapRefPrivate_h #define JSWeakObjectMapRefPrivate_h -#include -#include +#include "JSContextRef.h" +#include "JSValueRef.h" #ifdef __cplusplus extern "C" { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakPrivate.h similarity index 94% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakPrivate.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakPrivate.h index 9ca2ffa6..b77c8b9c 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakPrivate.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Apple Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ #ifndef JSWeakPrivate_h #define JSWeakPrivate_h -#include +#include "JSObjectRef.h" #ifdef __cplusplus extern "C" { diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakValue.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakValue.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakValue.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakValue.h index 177ef41c..e5ab2b14 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWeakValue.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWeakValue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. * Copyright (C) 2018 Igalia S.L. * * Redistribution and use in source and binary forms, with or without diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWrapperMap.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWrapperMap.h similarity index 95% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWrapperMap.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWrapperMap.h index 6c18c64b..a5946a8f 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSWrapperMap.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JSWrapperMap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import "JSValueInternal.h" #import -#import #import #if JSC_OBJC_API_ENABLED diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JavaScript.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JavaScript.h similarity index 85% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JavaScript.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JavaScript.h index 251e3937..093e9f35 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JavaScript.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JavaScript.h @@ -21,17 +21,17 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef JavaScript_h #define JavaScript_h -#include -#include -#include -#include -#include -#include +#include "JSBase.h" +#include "JSContextRef.h" +#include "JSObjectRef.h" +#include "JSStringRef.h" +#include "JSTypedArray.h" +#include "JSValueRef.h" #endif /* JavaScript_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JavaScriptCore.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JavaScriptCore.h similarity index 84% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JavaScriptCore.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JavaScriptCore.h index b2fde1db..b940825a 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JavaScriptCore.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/JavaScriptCore.h @@ -26,17 +26,13 @@ #ifndef JavaScriptCore_h #define JavaScriptCore_h -#include -#include +#include "JavaScript.h" +#include "JSStringRefCF.h" -#if defined(__OBJC__) && JSC_OBJC_API_ENABLED - -#import "JSContext.h" -#import "JSValue.h" -#import "JSManagedValue.h" -#import "JSVirtualMachine.h" -#import "JSExport.h" - -#endif +#include "JSContext.h" +#include "JSValue.h" +#include "JSManagedValue.h" +#include "JSVirtualMachine.h" +#include "JSExport.h" #endif /* JavaScriptCore_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/MarkedJSValueRefArray.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/MarkedJSValueRefArray.h new file mode 100644 index 00000000..519951bf --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/MarkedJSValueRefArray.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "APICast.h" +#include "ArgList.h" +#include +#include +#include +#include + +WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN + +namespace JSC { + +class MarkedJSValueRefArray final : public BasicRawSentinelNode { + WTF_MAKE_NONCOPYABLE(MarkedJSValueRefArray); + WTF_MAKE_NONMOVABLE(MarkedJSValueRefArray); + WTF_FORBID_HEAP_ALLOCATION; +public: + static constexpr size_t inlineCapacity = MarkedArgumentBuffer::inlineCapacity; + + JS_EXPORT_PRIVATE MarkedJSValueRefArray(JSGlobalContextRef, unsigned); + JS_EXPORT_PRIVATE ~MarkedJSValueRefArray(); + + size_t size() const { return m_size; } + bool isEmpty() const { return !m_size; } + + JSValueRef& operator[](unsigned index) LIFETIME_BOUND { return data()[index]; } + + const JSValueRef* data() const LIFETIME_BOUND + { + return const_cast(this)->data(); + } + + JSValueRef* data() LIFETIME_BOUND + { + if (m_buffer) + return m_buffer.get(); + return m_inlineBuffer; + } + + template void visitAggregate(Visitor&); + +private: + unsigned m_size; + JSValueRef m_inlineBuffer[inlineCapacity] { }; + UniqueArray m_buffer; +}; + +} // namespace JSC + +WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/ObjCCallbackFunction.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ObjCCallbackFunction.h similarity index 82% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/ObjCCallbackFunction.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ObjCCallbackFunction.h index c30c1562..c6016fa7 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/ObjCCallbackFunction.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ObjCCallbackFunction.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * Copyright (C) 2013-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,36 +25,42 @@ #ifndef ObjCCallbackFunction_h #define ObjCCallbackFunction_h -#include +#include "JSBase.h" #if JSC_OBJC_API_ENABLED -#import +#import "JSCallbackFunction.h" #if defined(__OBJC__) +@class JSContext; + JSObjectRef objCCallbackFunctionForMethod(JSContext *, Class, Protocol *, BOOL isInstanceMethod, SEL, const char* types); JSObjectRef objCCallbackFunctionForBlock(JSContext *, id); JSObjectRef objCCallbackFunctionForInit(JSContext *, Class, Protocol *, SEL, const char* types); -id tryUnwrapConstructor(JSC::VM*, JSObjectRef); +id tryUnwrapConstructor(JSObjectRef); #endif namespace JSC { class ObjCCallbackFunctionImpl; +#define OBJC_CALLBACK_FUNCTION_METHOD(method) \ + WTF_VTBL_FUNCPTR_PTRAUTH_STR("ObjCCallbackFunction." #method) method + class ObjCCallbackFunction : public InternalFunction { friend struct APICallbackFunction; public: typedef InternalFunction Base; template - static IsoSubspace* subspaceFor(VM& vm) + static GCClient::IsoSubspace* subspaceFor(VM& vm) { return vm.objCCallbackFunctionSpace(); } static ObjCCallbackFunction* create(VM&, JSGlobalObject*, const String& name, std::unique_ptr); + static constexpr DestructionMode needsDestruction = NeedsDestruction; static void destroy(JSCell*); static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) @@ -74,11 +80,13 @@ class ObjCCallbackFunction : public InternalFunction { JSObjectCallAsFunctionCallback functionCallback() { return m_functionCallback; } JSObjectCallAsConstructorCallback constructCallback() { return m_constructCallback; } - JSObjectCallAsFunctionCallback m_functionCallback; - JSObjectCallAsConstructorCallback m_constructCallback; + JSObjectCallAsFunctionCallback OBJC_CALLBACK_FUNCTION_METHOD(m_functionCallback); + JSObjectCallAsConstructorCallback OBJC_CALLBACK_FUNCTION_METHOD(m_constructCallback); std::unique_ptr m_impl; }; +#undef OBJC_CALLBACK_FUNCTION_METHOD + } // namespace JSC #endif diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/ObjcRuntimeExtras.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ObjcRuntimeExtras.h similarity index 81% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/ObjcRuntimeExtras.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ObjcRuntimeExtras.h index 20d8b858..48dd1a93 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/ObjcRuntimeExtras.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/ObjcRuntimeExtras.h @@ -27,10 +27,13 @@ #import #import #import +#import #import #import #import +WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN + template inline std::unique_ptr> adoptSystem(U value) { @@ -39,10 +42,9 @@ inline std::unique_ptr> adoptSystem(U value) inline bool protocolImplementsProtocol(Protocol *candidate, Protocol *target) { - unsigned protocolProtocolsCount; - auto protocolProtocols = adoptSystem<__unsafe_unretained Protocol*[]>(protocol_copyProtocolList(candidate, &protocolProtocolsCount)); - for (unsigned i = 0; i < protocolProtocolsCount; ++i) { - if (protocol_isEqual(protocolProtocols[i], target)) + auto protocolProtocols = protocol_copyProtocolListSpan(candidate); + for (auto* protocolProtocol : protocolProtocols.span()) { + if (protocol_isEqual(protocolProtocol, target)) return true; } return false; @@ -54,13 +56,12 @@ inline void forEachProtocolImplementingProtocol(Class cls, Protocol *target, voi ASSERT(target); Vector worklist; - HashSet visited; + UncheckedKeyHashSet visited; // Initially fill the worklist with the Class's protocols. { - unsigned protocolsCount; - auto protocols = adoptSystem<__unsafe_unretained Protocol*[]>(class_copyProtocolList(cls, &protocolsCount)); - worklist.append(protocols.get(), protocolsCount); + auto protocols = class_copyProtocolListSpan(cls); + worklist.append(protocols.span()); } bool stop = false; @@ -80,36 +81,29 @@ inline void forEachProtocolImplementingProtocol(Class cls, Protocol *target, voi } // Add incorporated protocols to the worklist. - { - unsigned protocolsCount; - auto protocols = adoptSystem<__unsafe_unretained Protocol*[]>(protocol_copyProtocolList(protocol, &protocolsCount)); - worklist.append(protocols.get(), protocolsCount); - } + worklist.append(protocol_copyProtocolListSpan(protocol).span()); } } inline void forEachMethodInClass(Class cls, void (^callback)(Method)) { - unsigned count; - auto methods = adoptSystem(class_copyMethodList(cls, &count)); - for (unsigned i = 0; i < count; ++i) - callback(methods[i]); + auto methods = class_copyMethodListSpan(cls); + for (auto& method : methods.span()) + callback(method); } inline void forEachMethodInProtocol(Protocol *protocol, BOOL isRequiredMethod, BOOL isInstanceMethod, void (^callback)(SEL, const char*)) { - unsigned count; - auto methods = adoptSystem(protocol_copyMethodDescriptionList(protocol, isRequiredMethod, isInstanceMethod, &count)); - for (unsigned i = 0; i < count; ++i) - callback(methods[i].name, methods[i].types); + auto methods = protocol_copyMethodDescriptionListSpan(protocol, isRequiredMethod, isInstanceMethod); + for (auto& method : methods.span()) + callback(method.name, method.types); } inline void forEachPropertyInProtocol(Protocol *protocol, void (^callback)(objc_property_t)) { - unsigned count; - auto properties = adoptSystem(protocol_copyPropertyList(protocol, &count)); - for (unsigned i = 0; i < count; ++i) - callback(properties[i]); + auto properties = protocol_copyPropertyListSpan(protocol); + for (auto& property : properties.span()) + callback(property); } template @@ -131,10 +125,10 @@ class StringRange { WTF_MAKE_NONCOPYABLE(StringRange); public: StringRange(const char* begin, const char* end) - : m_string(begin, end - begin) + : m_string({ begin, end }) { } - operator const char*() const { return m_string.data(); } - const char* get() const { return m_string.data(); } + operator const char*() const LIFETIME_BOUND { return m_string.data(); } + const char* get() const LIFETIME_BOUND { return m_string.data(); } private: CString m_string; @@ -243,8 +237,8 @@ typename DelegateType::ResultType parseObjCType(const char*& position) extern "C" { // Forward declare some Objective-C runtime internal methods that are not API. const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod); - id objc_initWeak(id *, id); - void objc_destroyWeak(id *); bool _Block_has_signature(void *); const char * _Block_signature(void *); } + +WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/OpaqueJSString.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/OpaqueJSString.h similarity index 75% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/OpaqueJSString.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/OpaqueJSString.h index 4a4b5edf..b6c43f35 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/OpaqueJSString.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/OpaqueJSString.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OpaqueJSString_h -#define OpaqueJSString_h +#pragma once #include #include @@ -41,14 +40,14 @@ struct OpaqueJSString : public ThreadSafeRefCounted { return adoptRef(*new OpaqueJSString); } - static Ref create(const LChar* characters, unsigned length) + static Ref create(std::span characters) { - return adoptRef(*new OpaqueJSString(characters, length)); + return adoptRef(*new OpaqueJSString(characters)); } - static Ref create(const UChar* characters, unsigned length) + static Ref create(std::span characters) { - return adoptRef(*new OpaqueJSString(characters, length)); + return adoptRef(*new OpaqueJSString(characters)); } JS_EXPORT_PRIVATE static RefPtr tryCreate(const String&); @@ -57,11 +56,11 @@ struct OpaqueJSString : public ThreadSafeRefCounted { JS_EXPORT_PRIVATE ~OpaqueJSString(); bool is8Bit() { return m_string.is8Bit(); } - const LChar* characters8() { return m_string.characters8(); } - const UChar* characters16() { return m_string.characters16(); } + std::span span8() LIFETIME_BOUND { return m_string.span8(); } + std::span span16() LIFETIME_BOUND { return m_string.span16(); } unsigned length() { return m_string.length(); } - const UChar* characters(); + const char16_t* characters() LIFETIME_BOUND; JS_EXPORT_PRIVATE String string() const; JSC::Identifier identifier(JSC::VM*) const; @@ -78,32 +77,30 @@ struct OpaqueJSString : public ThreadSafeRefCounted { OpaqueJSString(const String& string) : m_string(string.isolatedCopy()) - , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.characters16())) + , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.span16().data())) { } explicit OpaqueJSString(String&& string) : m_string(WTFMove(string)) - , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.characters16())) + , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.span16().data())) { } - OpaqueJSString(const LChar* characters, unsigned length) - : m_string(characters, length) + OpaqueJSString(std::span characters) + : m_string(characters) , m_characters(nullptr) { } - OpaqueJSString(const UChar* characters, unsigned length) - : m_string(characters, length) - , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.characters16())) + OpaqueJSString(std::span characters) + : m_string(characters) + , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.span16().data())) { } String m_string; // This will be initialized on demand when characters() is called if the string needs up-conversion. - std::atomic m_characters; + std::atomic m_characters; }; - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/PASReportCrashPrivate.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/PASReportCrashPrivate.h new file mode 100644 index 00000000..ce7bbc8a --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/PASReportCrashPrivate.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "JSBase.h" + +#ifdef __APPLE__ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +JS_EXPORT kern_return_t PASReportCrashExtractResults(vm_address_t fault_address, mach_vm_address_t pas_dead_root, unsigned version, task_t task, pas_report_crash_pgm_report *report, crash_reporter_memory_reader_t crm_reader); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPLE__ */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/WebKitAvailability.h b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/WebKitAvailability.h similarity index 86% rename from test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/WebKitAvailability.h rename to test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/WebKitAvailability.h index 2a1ce7ce..ab63ae80 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/WebKitAvailability.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/JavaScriptCore/WebKitAvailability.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010, 2014 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009, 2010, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,13 +31,13 @@ #include #include -#if defined(BUILDING_GTK__) -#undef JSC_API_AVAILABLE -#define JSC_API_AVAILABLE(...) #endif -#else +#ifndef JSC_FRAMEWORK_HEADER_POSTPROCESSING_ENABLED #define JSC_API_AVAILABLE(...) +#define JSC_API_DEPRECATED(...) +#define JSC_API_DEPRECATED_WITH_REPLACEMENT(...) +#define JSC_CLASS_AVAILABLE(...) JS_EXPORT #endif #endif /* __WebKitAvailability__ */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APICallbackFunction.h b/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APICallbackFunction.h deleted file mode 100644 index e5283b5b..00000000 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/APICallbackFunction.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef APICallbackFunction_h -#define APICallbackFunction_h - -#include "APICast.h" -#include "Error.h" -#include "JSCallbackConstructor.h" -#include "JSLock.h" -#include - -namespace JSC { - -struct APICallbackFunction { - -template static EncodedJSValue JSC_HOST_CALL call(ExecState*); -template static EncodedJSValue JSC_HOST_CALL construct(ExecState*); - -}; - -template -EncodedJSValue JSC_HOST_CALL APICallbackFunction::call(ExecState* exec) -{ - VM& vm = exec->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSContextRef execRef = toRef(exec); - JSObjectRef functionRef = toRef(exec->jsCallee()); - JSObjectRef thisObjRef = toRef(jsCast(exec->thisValue().toThis(exec, NotStrictMode))); - - int argumentCount = static_cast(exec->argumentCount()); - Vector arguments; - arguments.reserveInitialCapacity(argumentCount); - for (int i = 0; i < argumentCount; i++) - arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); - - JSValueRef exception = 0; - JSValueRef result; - { - JSLock::DropAllLocks dropAllLocks(exec); - result = jsCast(toJS(functionRef))->functionCallback()(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception); - } - if (exception) - throwException(exec, scope, toJS(exec, exception)); - - // result must be a valid JSValue. - if (!result) - return JSValue::encode(jsUndefined()); - - return JSValue::encode(toJS(exec, result)); -} - -template -EncodedJSValue JSC_HOST_CALL APICallbackFunction::construct(ExecState* exec) -{ - VM& vm = exec->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSObject* constructor = exec->jsCallee(); - JSContextRef ctx = toRef(exec); - JSObjectRef constructorRef = toRef(constructor); - - JSObjectCallAsConstructorCallback callback = jsCast(constructor)->constructCallback(); - if (callback) { - size_t argumentCount = exec->argumentCount(); - Vector arguments; - arguments.reserveInitialCapacity(argumentCount); - for (size_t i = 0; i < argumentCount; ++i) - arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); - - JSValueRef exception = 0; - JSObjectRef result; - { - JSLock::DropAllLocks dropAllLocks(exec); - result = callback(ctx, constructorRef, argumentCount, arguments.data(), &exception); - } - if (exception) { - throwException(exec, scope, toJS(exec, exception)); - return JSValue::encode(toJS(exec, exception)); - } - // result must be a valid JSValue. - if (!result) - return throwVMTypeError(exec, scope); - return JSValue::encode(toJS(result)); - } - - return JSValue::encode(toJS(JSObjectMake(ctx, jsCast(constructor)->classRef(), 0))); -} - -} // namespace JSC - -#endif // APICallbackFunction_h diff --git a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValueRef.h b/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValueRef.h deleted file mode 100644 index 911b4bfc..00000000 --- a/test-app/runtime/src/main/cpp/napi/jsc/include/JavaScriptCore/JSValueRef.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (C) 2006-2019 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef JSValueRef_h -#define JSValueRef_h - -#include -#include - -#ifndef __cplusplus -#include -#endif - -/*! -@enum JSType -@abstract A constant identifying the type of a JSValue. -@constant kJSTypeUndefined The unique undefined value. -@constant kJSTypeNull The unique null value. -@constant kJSTypeBoolean A primitive boolean value, one of true or false. -@constant kJSTypeNumber A primitive number value. -@constant kJSTypeString A primitive string value. -@constant kJSTypeObject An object value (meaning that this JSValueRef is a JSObjectRef). -@constant kJSTypeSymbol A primitive symbol value. -*/ -typedef enum { - kJSTypeUndefined, - kJSTypeNull, - kJSTypeBoolean, - kJSTypeNumber, - kJSTypeString, - kJSTypeObject, - kJSTypeSymbol JSC_API_AVAILABLE(macos(10.15), ios(13.0)) -} JSType; - -/*! - @enum JSTypedArrayType - @abstract A constant identifying the Typed Array type of a JSObjectRef. - @constant kJSTypedArrayTypeInt8Array Int8Array - @constant kJSTypedArrayTypeInt16Array Int16Array - @constant kJSTypedArrayTypeInt32Array Int32Array - @constant kJSTypedArrayTypeUint8Array Uint8Array - @constant kJSTypedArrayTypeUint8ClampedArray Uint8ClampedArray - @constant kJSTypedArrayTypeUint16Array Uint16Array - @constant kJSTypedArrayTypeUint32Array Uint32Array - @constant kJSTypedArrayTypeFloat32Array Float32Array - @constant kJSTypedArrayTypeFloat64Array Float64Array - @constant kJSTypedArrayTypeArrayBuffer ArrayBuffer - @constant kJSTypedArrayTypeNone Not a Typed Array - - */ -typedef enum { - kJSTypedArrayTypeInt8Array, - kJSTypedArrayTypeInt16Array, - kJSTypedArrayTypeInt32Array, - kJSTypedArrayTypeUint8Array, - kJSTypedArrayTypeUint8ClampedArray, - kJSTypedArrayTypeUint16Array, - kJSTypedArrayTypeUint32Array, - kJSTypedArrayTypeFloat32Array, - kJSTypedArrayTypeFloat64Array, - kJSTypedArrayTypeArrayBuffer, - kJSTypedArrayTypeNone, -} JSTypedArrayType JSC_API_AVAILABLE(macos(10.12), ios(10.0)); - -#ifdef __cplusplus -extern "C" { -#endif - -/*! -@function -@abstract Returns a JavaScript value's type. -@param ctx The execution context to use. -@param value The JSValue whose type you want to obtain. -@result A value of type JSType that identifies value's type. -*/ -JS_EXPORT JSType JSValueGetType(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the undefined type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the undefined type, otherwise false. -*/ -JS_EXPORT bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the null type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the null type, otherwise false. -*/ -JS_EXPORT bool JSValueIsNull(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the boolean type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the boolean type, otherwise false. -*/ -JS_EXPORT bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the number type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the number type, otherwise false. -*/ -JS_EXPORT bool JSValueIsNumber(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the string type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the string type, otherwise false. -*/ -JS_EXPORT bool JSValueIsString(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the symbol type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the symbol type, otherwise false. -*/ -JS_EXPORT bool JSValueIsSymbol(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); - -/*! -@function -@abstract Tests whether a JavaScript value's type is the object type. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value's type is the object type, otherwise false. -*/ -JS_EXPORT bool JSValueIsObject(JSContextRef ctx, JSValueRef value); - - -/*! -@function -@abstract Tests whether a JavaScript value is an object with a given class in its class chain. -@param ctx The execution context to use. -@param value The JSValue to test. -@param jsClass The JSClass to test against. -@result true if value is an object and has jsClass in its class chain, otherwise false. -*/ -JS_EXPORT bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass); - -/*! -@function -@abstract Tests whether a JavaScript value is an array. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value is an array, otherwise false. -*/ -JS_EXPORT bool JSValueIsArray(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); - -/*! -@function -@abstract Tests whether a JavaScript value is a date. -@param ctx The execution context to use. -@param value The JSValue to test. -@result true if value is a date, otherwise false. -*/ -JS_EXPORT bool JSValueIsDate(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); - -/*! -@function -@abstract Returns a JavaScript value's Typed Array type. -@param ctx The execution context to use. -@param value The JSValue whose Typed Array type to return. -@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. -@result A value of type JSTypedArrayType that identifies value's Typed Array type, or kJSTypedArrayTypeNone if the value is not a Typed Array object. - */ -JS_EXPORT JSTypedArrayType JSValueGetTypedArrayType(JSContextRef ctx, JSValueRef value, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); - -/* Comparing values */ - -/*! -@function -@abstract Tests whether two JavaScript values are equal, as compared by the JS == operator. -@param ctx The execution context to use. -@param a The first value to test. -@param b The second value to test. -@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. -@result true if the two values are equal, false if they are not equal or an exception is thrown. -*/ -JS_EXPORT bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception); - -/*! -@function -@abstract Tests whether two JavaScript values are strict equal, as compared by the JS === operator. -@param ctx The execution context to use. -@param a The first value to test. -@param b The second value to test. -@result true if the two values are strict equal, otherwise false. -*/ -JS_EXPORT bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b); - -/*! -@function -@abstract Tests whether a JavaScript value is an object constructed by a given constructor, as compared by the JS instanceof operator. -@param ctx The execution context to use. -@param value The JSValue to test. -@param constructor The constructor to test against. -@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. -@result true if value is an object constructed by constructor, as compared by the JS instanceof operator, otherwise false. -*/ -JS_EXPORT bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception); - -/* Creating values */ - -/*! -@function -@abstract Creates a JavaScript value of the undefined type. -@param ctx The execution context to use. -@result The unique undefined value. -*/ -JS_EXPORT JSValueRef JSValueMakeUndefined(JSContextRef ctx); - -/*! -@function -@abstract Creates a JavaScript value of the null type. -@param ctx The execution context to use. -@result The unique null value. -*/ -JS_EXPORT JSValueRef JSValueMakeNull(JSContextRef ctx); - -/*! -@function -@abstract Creates a JavaScript value of the boolean type. -@param ctx The execution context to use. -@param boolean The bool to assign to the newly created JSValue. -@result A JSValue of the boolean type, representing the value of boolean. -*/ -JS_EXPORT JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool boolean); - -/*! -@function -@abstract Creates a JavaScript value of the number type. -@param ctx The execution context to use. -@param number The double to assign to the newly created JSValue. -@result A JSValue of the number type, representing the value of number. -*/ -JS_EXPORT JSValueRef JSValueMakeNumber(JSContextRef ctx, double number); - -/*! -@function -@abstract Creates a JavaScript value of the string type. -@param ctx The execution context to use. -@param string The JSString to assign to the newly created JSValue. The - newly created JSValue retains string, and releases it upon garbage collection. -@result A JSValue of the string type, representing the value of string. -*/ -JS_EXPORT JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string); - -/*! - @function - @abstract Creates a JavaScript value of the symbol type. - @param ctx The execution context to use. - @param description A description of the newly created symbol value. - @result A unique JSValue of the symbol type, whose description matches the one provided. - */ -JS_EXPORT JSValueRef JSValueMakeSymbol(JSContextRef ctx, JSStringRef description) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); - -/* Converting to and from JSON formatted strings */ - -/*! - @function - @abstract Creates a JavaScript value from a JSON formatted string. - @param ctx The execution context to use. - @param string The JSString containing the JSON string to be parsed. - @result A JSValue containing the parsed value, or NULL if the input is invalid. - */ -JS_EXPORT JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); - -/*! - @function - @abstract Creates a JavaScript string containing the JSON serialized representation of a JS value. - @param ctx The execution context to use. - @param value The value to serialize. - @param indent The number of spaces to indent when nesting. If 0, the resulting JSON will not contains newlines. The size of the indent is clamped to 10 spaces. - @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. - @result A JSString with the result of serialization, or NULL if an exception is thrown. - */ -JS_EXPORT JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef value, unsigned indent, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); - -/* Converting to primitive values */ - -/*! -@function -@abstract Converts a JavaScript value to boolean and returns the resulting boolean. -@param ctx The execution context to use. -@param value The JSValue to convert. -@result The boolean result of conversion. -*/ -JS_EXPORT bool JSValueToBoolean(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Converts a JavaScript value to number and returns the resulting number. -@param ctx The execution context to use. -@param value The JSValue to convert. -@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. -@result The numeric result of conversion, or NaN if an exception is thrown. -*/ -JS_EXPORT double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception); - -/*! -@function -@abstract Converts a JavaScript value to string and copies the result into a JavaScript string. -@param ctx The execution context to use. -@param value The JSValue to convert. -@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. -@result A JSString with the result of conversion, or NULL if an exception is thrown. Ownership follows the Create Rule. -*/ -JS_EXPORT JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception); - -/*! -@function -@abstract Converts a JavaScript value to object and returns the resulting object. -@param ctx The execution context to use. -@param value The JSValue to convert. -@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. -@result The JSObject result of conversion, or NULL if an exception is thrown. -*/ -JS_EXPORT JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception); - -/* Garbage collection */ -/*! -@function -@abstract Protects a JavaScript value from garbage collection. -@param ctx The execution context to use. -@param value The JSValue to protect. -@discussion Use this method when you want to store a JSValue in a global or on the heap, where the garbage collector will not be able to discover your reference to it. - -A value may be protected multiple times and must be unprotected an equal number of times before becoming eligible for garbage collection. -*/ -JS_EXPORT void JSValueProtect(JSContextRef ctx, JSValueRef value); - -/*! -@function -@abstract Unprotects a JavaScript value from garbage collection. -@param ctx The execution context to use. -@param value The JSValue to unprotect. -@discussion A value may be protected multiple times and must be unprotected an - equal number of times before becoming eligible for garbage collection. -*/ -JS_EXPORT void JSValueUnprotect(JSContextRef ctx, JSValueRef value); - -#ifdef __cplusplus -} -#endif - -#endif /* JSValueRef_h */ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.cpp b/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.cpp index 0f2a615d..1a243bce 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.cpp +++ b/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.cpp @@ -1,4 +1,7 @@ #include "jsc-api.h" +// Native weak references (JSWeakCreate / JSWeakGetObject) live in this private +// JSC header; used by napi_ref__ to back weak references. +#include "JavaScriptCore/JSWeakPrivate.h" #include #include #include @@ -105,6 +108,21 @@ class JSString { return JSStringCreateWithUTF8CString(string); } + // Fast path: pure-ASCII input maps 1:1 onto UTF-16 code units, so we can + // widen it directly and skip the costly std::wstring_convert/codecvt + // transcode (which dominates JSC string creation for the common case). + bool isAscii = true; + for (size_t i = 0; i < length; ++i) { + if (static_cast(string[i]) >= 0x80) { isAscii = false; break; } + } + if (isAscii) { + std::vector chars(length); + for (size_t i = 0; i < length; ++i) { + chars[i] = static_cast(static_cast(string[i])); + } + return JSStringCreateWithCharacters(chars.data(), length); + } + std::u16string u16str{std::wstring_convert< std::codecvt_utf8_utf16, char16_t>{}.from_bytes(string, string + length)}; return JSStringCreateWithCharacters(reinterpret_cast(u16str.data()), u16str.size()); @@ -215,10 +233,27 @@ class NativeInfo { template static T* GetNativeInfo(JSContextRef ctx, JSObjectRef obj, const char * propertyKey) { + // Guard against a receiver that isn't a wrapped object (e.g. a plain {} passed + // to napi_unwrap via method.call({})). Reading the info property yields undefined; + // JSValueToObject(undefined) then returns NULL and JSObjectGetPrivate(NULL) would + // dereference a null JSObjectRef -> SIGSEGV. V8/QuickJS return safely here, so + // mirror that and just report "no native info". + if (obj == nullptr) { + return nullptr; + } JSValueRef exception {}; JSValueRef native_info = JSObjectGetProperty(ctx, obj, JSString(propertyKey), &exception); + if (exception != nullptr || native_info == nullptr || + !JSValueIsObject(ctx, native_info)) { + return nullptr; + } - NativeInfo* info = Get(JSValueToObject(ctx, native_info, &exception)); + JSObjectRef info_obj = JSValueToObject(ctx, native_info, &exception); + if (exception != nullptr || info_obj == nullptr) { + return nullptr; + } + + NativeInfo* info = Get(info_obj); if (info != nullptr && info->Type() == T::StaticType) { return reinterpret_cast(info); } @@ -259,6 +294,58 @@ class ConstructorInfo : public NativeInfo { } JSObjectRef constructor{JSObjectMakeConstructor(env->context, info->_class, CallAsConstructor)}; + + // Give the constructor a non-writable own `Symbol.hasInstance` equal to + // the built-in default (Function.prototype[@@hasInstance], i.e. the + // OrdinaryHasInstance handler). JSObjectMakeConstructor produces an + // object whose prototype chain does not include Function.prototype, so + // `Class[Symbol.hasInstance]` would otherwise be `undefined` — unlike + // V8/QuickJS where these constructors are ordinary functions that + // inherit the (non-writable) default. That gap lets the TypeScript + // `__extends` helper's defensive `child[Symbol.hasInstance] = fn` + // assignment SUCCEED on JSC (it is a silent no-op elsewhere because the + // inherited default is non-writable), installing a broken own + // @@hasInstance that reduces `x instanceof ExtendedClass` to the + // always-false `x instanceof `. Defining the + // default here as a non-writable own property restores the correct + // `instanceof` behaviour WITHOUT reparenting the constructor to + // Function.prototype (which would also override its class-name + // `toString`). + { + JSObjectRef jsGlobal = JSContextGetGlobalObject(env->context); + JSValueRef symbolVal = JSObjectGetProperty(env->context, jsGlobal, + JSString("Symbol"), nullptr); + JSObjectRef symbolCtor = JSValueToObject(env->context, symbolVal, nullptr); + JSValueRef funcVal = JSObjectGetProperty(env->context, jsGlobal, + JSString("Function"), nullptr); + JSObjectRef funcCtor = JSValueToObject(env->context, funcVal, nullptr); + if (symbolCtor != nullptr && funcCtor != nullptr) { + JSValueRef hasInstanceKey = JSObjectGetProperty(env->context, symbolCtor, + JSString("hasInstance"), nullptr); + JSValueRef funcProtoVal = JSObjectGetProperty(env->context, funcCtor, + JSString("prototype"), nullptr); + JSObjectRef funcProto = JSValueToObject(env->context, funcProtoVal, nullptr); + if (hasInstanceKey != nullptr && JSValueIsSymbol(env->context, hasInstanceKey) && + funcProto != nullptr) { + JSValueRef defaultHasInstance = JSObjectGetPropertyForKey( + env->context, funcProto, hasInstanceKey, nullptr); + if (defaultHasInstance != nullptr && + JSValueIsObject(env->context, defaultHasInstance)) { + // Non-writable (blocks the __extends `[[Set]]` + // assignment, matching the inherited default's + // writability on V8/QuickJS) but CONFIGURABLE, so + // interfaces can still install their own java-backed + // @@hasInstance via Object.defineProperty + // (RegisterSymbolHasInstanceCallback). + JSObjectSetPropertyForKey( + env->context, constructor, hasInstanceKey, defaultHasInstance, + kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum, + nullptr); + } + } + } + } + JSValueRef exception{}; if (length) { napi_value name; @@ -534,37 +621,6 @@ class ExternalInfo: public BaseInfoT { } }; -class ReferenceInfo : public BaseInfoT { - public: - static napi_status Initialize(napi_env env, napi_value object, FinalizerT finalizer) { - - napi_valuetype type; - napi_typeof(env, object, &type); - - if (type == napi_object || type == napi_function) { - ReferenceInfo* info = new ReferenceInfo(env); - if (info == nullptr) { - return napi_set_last_error(env, napi_generic_failure); - } - - // JSObjectRef ref{JSObjectMake(env->context, info->_class, info)}; - // JSObjectSetPrototype(env->context, prototype, JSObjectGetPrototype(env->context, ToJSObject(env, object))); - // JSObjectSetPrototype(env->context, ToJSObject(env, object), prototype); - - NativeInfo::SetNativeInfo(env->context, ToJSObject(env, object), info->_class, "[[jsc_reference_info]]", info); - - info->AddFinalizer(finalizer); - } - - return napi_ok; - } - - private: - ReferenceInfo(napi_env env) - : BaseInfoT{env, "Native (Reference)"} { - } -}; - class WrapperInfo : public BaseInfoT { public: static napi_status Wrap(napi_env env, napi_value object, WrapperInfo** result) { @@ -659,12 +715,13 @@ struct napi_ref__ { } napi_status init(napi_env env) { - // track the ref values to support weak refs - auto pair{env->active_ref_values.insert(_value)}; - if (pair.second) { - CHECK_NAPI(ReferenceInfo::Initialize(env, _value, [value = _value](ReferenceInfo* info) { - info->Env()->active_ref_values.erase(value); - })); + // For objects we hold a native JSC weak reference, which lets value() + // report when the target has been collected without pinning it or + // mutating the object. Non-object values (e.g. symbols) cannot be + // weakly tracked and are returned on a best-effort basis. + JSValueRef jsValue{ToJSValue(_value)}; + if (JSValueIsObject(env->context, jsValue)) { + _weak = JSWeakCreate(JSContextGetGroup(env->context), ToJSObject(env, _value)); } if (_count != 0) { @@ -679,6 +736,11 @@ struct napi_ref__ { unprotect(env); } + if (_weak != nullptr) { + JSWeakRelease(JSContextGetGroup(env->context), _weak); + _weak = nullptr; + } + _value = nullptr; _count = 0; } @@ -700,10 +762,13 @@ struct napi_ref__ { } napi_value value(napi_env env) const { - if (env->active_ref_values.find(_value) == env->active_ref_values.end()) { - return nullptr; + if (_weak != nullptr) { + // Returns NULL once the target object has been garbage-collected. + JSObjectRef object{JSWeakGetObject(_weak)}; + return object != nullptr ? ToNapi(object) : nullptr; } + // Non-object value: not weakly trackable, returned as-is. return _value; } @@ -721,6 +786,7 @@ struct napi_ref__ { napi_value _value{}; uint32_t _count{}; + JSWeakRef _weak{}; std::list::iterator _iter{}; }; @@ -867,7 +933,8 @@ napi_status napi_get_property_names(napi_env env, CHECK_NAPI(napi_get_global(env, &global)); CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); CHECK_NAPI(napi_get_named_property(env, object_ctor, "getOwnPropertyNames", &function)); - CHECK_NAPI(napi_call_function(env, object_ctor, function, 0, nullptr, result)); + // Object.getOwnPropertyNames(object) + CHECK_NAPI(napi_call_function(env, object_ctor, function, 1, &object, result)); return napi_ok; } @@ -880,14 +947,13 @@ napi_status napi_set_property(napi_env env, CHECK_ARG(env, key); CHECK_ARG(env, value); + // Use the *ForKey APIs so the key can be a string or a symbol; converting to + // a JSString would coerce (and break) symbol keys. JSValueRef exception{}; - JSString key_str{ToJSString(env, key, &exception)}; - CHECK_JSC(env, exception); - - JSObjectSetProperty( + JSObjectSetPropertyForKey( env->context, ToJSObject(env, object), - key_str, + ToJSValue(key), ToJSValue(value), kJSPropertyAttributeNone, &exception); @@ -905,13 +971,12 @@ napi_status napi_has_property(napi_env env, CHECK_ARG(env, key); JSValueRef exception{}; - JSString key_str{ToJSString(env, key, &exception)}; - CHECK_JSC(env, exception); - - *result = JSObjectHasProperty( + *result = JSObjectHasPropertyForKey( env->context, ToJSObject(env, object), - key_str); + ToJSValue(key), + &exception); + CHECK_JSC(env, exception); return napi_ok; } @@ -924,13 +989,10 @@ napi_status napi_get_property(napi_env env, CHECK_ARG(env, result); JSValueRef exception{}; - JSString key_str{ToJSString(env, key, &exception)}; - CHECK_JSC(env, exception); - - *result = ToNapi(JSObjectGetProperty( + *result = ToNapi(JSObjectGetPropertyForKey( env->context, ToJSObject(env, object), - key_str, + ToJSValue(key), &exception)); CHECK_JSC(env, exception); @@ -942,16 +1004,14 @@ napi_status napi_delete_property(napi_env env, napi_value key, bool* result) { CHECK_ENV(env); + CHECK_ARG(env, key); CHECK_ARG(env, result); JSValueRef exception{}; - JSString key_str{ToJSString(env, key, &exception)}; - CHECK_JSC(env, exception); - - *result = JSObjectDeleteProperty( + *result = JSObjectDeletePropertyForKey( env->context, ToJSObject(env, object), - key_str, + ToJSValue(key), &exception); CHECK_JSC(env, exception); @@ -959,20 +1019,23 @@ napi_status napi_delete_property(napi_env env, } NAPI_EXTERN napi_status napi_has_own_property(napi_env env, -napi_value object, - napi_value key, -bool* result) { -CHECK_ENV(env); -CHECK_ARG(env, result); + napi_value object, + napi_value key, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, key); + CHECK_ARG(env, result); -napi_value global{}, object_ctor{}, function{}, value{}; -CHECK_NAPI(napi_get_global(env, &global)); -CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); -CHECK_NAPI(napi_get_named_property(env, object_ctor, "hasOwnProperty", &function)); -CHECK_NAPI(napi_call_function(env, object_ctor, function, 0, nullptr, &value)); -*result = JSValueToBoolean(env->context, ToJSValue(value)); + // Object.prototype.hasOwnProperty.call(object, key) + napi_value global{}, object_ctor{}, proto{}, function{}, value{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); + CHECK_NAPI(napi_get_named_property(env, object_ctor, "prototype", &proto)); + CHECK_NAPI(napi_get_named_property(env, proto, "hasOwnProperty", &function)); + CHECK_NAPI(napi_call_function(env, object, function, 1, &key, &value)); + *result = JSValueToBoolean(env->context, ToJSValue(value)); -return napi_ok; + return napi_ok; } napi_status napi_set_named_property(napi_env env, @@ -1128,7 +1191,7 @@ napi_status napi_define_properties(napi_env env, CHECK_NAPI(napi_set_named_property(env, descriptor, "configurable", configurable)); napi_value enumerable{}; - CHECK_NAPI(napi_get_boolean(env, (p->attributes & napi_configurable), &enumerable)); + CHECK_NAPI(napi_get_boolean(env, (p->attributes & napi_enumerable), &enumerable)); CHECK_NAPI(napi_set_named_property(env, descriptor, "enumerable", enumerable)); if (p->getter != nullptr || p->setter != nullptr) { @@ -1146,6 +1209,14 @@ napi_status napi_define_properties(napi_env env, napi_value method{}; CHECK_NAPI(napi_create_function(env, p->utf8name, NAPI_AUTO_LENGTH, p->method, p->data, &method)); CHECK_NAPI(napi_set_named_property(env, descriptor, "value", method)); + // A method is a data property; honor the writable attribute like V8/Node + // do. Omitting it makes Object.defineProperty default writable to false, + // which leaves the method non-writable and prevents callers from shadowing + // it with an own property (e.g. ts_helpers wrapping URLSearchParams.set to + // propagate changes back to the parent URL). + napi_value method_writable{}; + CHECK_NAPI(napi_get_boolean(env, (p->attributes & napi_writable), &method_writable)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "writable", method_writable)); } else { RETURN_STATUS_IF_FALSE(env, p->value != nullptr, napi_invalid_arg); @@ -1367,10 +1438,17 @@ napi_status napi_create_symbol(napi_env env, CHECK_ENV(env); CHECK_ARG(env, result); - napi_value global{}, symbol_func{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "Symbol", &symbol_func)); - CHECK_NAPI(napi_call_function(env, global, symbol_func, 1, &description, result)); + // Create the symbol directly instead of round-tripping through the JS + // `Symbol()` constructor. A null description yields `Symbol()`. + if (description == nullptr || + JSValueIsUndefined(env->context, ToJSValue(description))) { + *result = ToNapi(JSValueMakeSymbol(env->context, nullptr)); + } else { + JSValueRef exception{}; + JSString descriptionString{ToJSString(env, description, &exception)}; + CHECK_JSC(env, exception); + *result = ToNapi(JSValueMakeSymbol(env->context, descriptionString)); + } return napi_ok; } @@ -1429,12 +1507,29 @@ napi_status napi_create_range_error(napi_env env, return napi_ok; } +napi_status napi_create_syntax_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + napi_value global{}, error_ctor{}, error{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "SyntaxError", &error_ctor)); + CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); + CHECK_NAPI(napi_set_error_code(env, error, code, nullptr)); + + *result = error; + return napi_ok; +} + napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); - // JSC does not support BigInt JSType valueType = JSValueGetType(env->context, ToJSValue(value)); switch (valueType) { case kJSTypeUndefined: *result = napi_undefined; break; @@ -1443,6 +1538,7 @@ napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result) case kJSTypeNumber: *result = napi_number; break; case kJSTypeString: *result = napi_string; break; case kJSTypeSymbol: *result = napi_symbol; break; + case kJSTypeBigInt: *result = napi_bigint; break; default: JSObjectRef object{ToJSObject(env, value)}; if (JSObjectIsFunction(env->context, object)) { @@ -1635,14 +1731,10 @@ napi_status napi_get_value_int32(napi_env env, napi_value value, int32_t* result CHECK_ARG(env, result); JSValueRef exception{}; - double number = JSValueToNumber(env->context, ToJSValue(value), &exception); - - if (number > INT_MAX) { - *result = -1; - } else { - *result = static_cast(number); - } - + // JSValueToInt32 applies the ECMAScript ToInt32 conversion (modulo 2^32, + // NaN/Infinity -> 0), matching Node's napi_get_value_int32 semantics, and + // truncates BigInt values. + *result = JSValueToInt32(env->context, ToJSValue(value), &exception); CHECK_JSC(env, exception); return napi_ok; @@ -1654,14 +1746,9 @@ napi_status napi_get_value_uint32(napi_env env, napi_value value, uint32_t* resu CHECK_ARG(env, result); JSValueRef exception{}; - *result = static_cast(JSValueToNumber(env->context, ToJSValue(value), &exception)); - - double number = JSValueToNumber(env->context, ToJSValue(value), &exception); - if (number > UINT32_MAX) { - *result = -1; - } else { - *result = static_cast(number); - } + // JSValueToUInt32 applies the ECMAScript ToUint32 conversion (modulo 2^32, + // NaN/Infinity -> 0), matching Node's napi_get_value_uint32 semantics. + *result = JSValueToUInt32(env->context, ToJSValue(value), &exception); CHECK_JSC(env, exception); return napi_ok; @@ -1693,6 +1780,84 @@ napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { return napi_ok; } +// BigInt support relies on JSC APIs available in newer JavaScriptCore +// (macOS 15 / iOS 18 and the corresponding Android build). +napi_status napi_create_bigint_int64(napi_env env, + int64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSValueRef bigint{JSBigIntCreateWithInt64(env->context, value, &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(bigint); + return napi_ok; +} + +napi_status napi_create_bigint_uint64(napi_env env, + uint64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSValueRef bigint{JSBigIntCreateWithUInt64(env->context, value, &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(bigint); + return napi_ok; +} + +napi_status napi_get_value_bigint_int64(napi_env env, + napi_value value, + int64_t* result, + bool* lossless) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + RETURN_STATUS_IF_FALSE( + env, JSValueIsBigInt(env->context, ToJSValue(value)), napi_bigint_expected); + + JSValueRef exception{}; + *result = JSValueToInt64(env->context, ToJSValue(value), &exception); + CHECK_JSC(env, exception); + + // The conversion is lossless when the (truncated) int64 compares equal to + // the original BigInt. + *lossless = JSValueCompareInt64(env->context, ToJSValue(value), *result, &exception) == + kJSRelationConditionEqual; + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_get_value_bigint_uint64(napi_env env, + napi_value value, + uint64_t* result, + bool* lossless) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + RETURN_STATUS_IF_FALSE( + env, JSValueIsBigInt(env->context, ToJSValue(value)), napi_bigint_expected); + + JSValueRef exception{}; + *result = JSValueToUInt64(env->context, ToJSValue(value), &exception); + CHECK_JSC(env, exception); + + *lossless = JSValueCompareUInt64(env->context, ToJSValue(value), *result, &exception) == + kJSRelationConditionEqual; + CHECK_JSC(env, exception); + + return napi_ok; +} + // Copies a JavaScript string into a LATIN-1 string buffer. The result is the // number of bytes (excluding the null terminator) copied into buf. // A sufficient buffer size should be greater than the length of string, @@ -2405,6 +2570,13 @@ napi_status napi_get_version(napi_env env, uint32_t* result) { return napi_ok; } +// Holds the resolve/reject functions of a deferred promise. Both are protected +// from GC for the lifetime of the deferred and released when it is settled. +struct napi_deferred__ { + JSObjectRef resolve; + JSObjectRef reject; +}; + napi_status napi_create_promise(napi_env env, napi_deferred* deferred, napi_value* promise) { @@ -2412,70 +2584,62 @@ napi_status napi_create_promise(napi_env env, CHECK_ARG(env, deferred); CHECK_ARG(env, promise); - napi_value global{}, promise_ctor{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); + // Create the promise and its resolve/reject functions directly through the + // JSC C API instead of round-tripping through the JS `Promise` constructor + // with a native executor callback. + JSObjectRef resolve{}, reject{}; + JSValueRef exception{}; + JSObjectRef promiseObject{ + JSObjectMakeDeferredPromise(env->context, &resolve, &reject, &exception)}; + CHECK_JSC(env, exception); - struct Wrapper { - napi_value resolve{}; - napi_value reject{}; + napi_deferred__* holder{new napi_deferred__{resolve, reject}}; + if (holder == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + JSValueProtect(env->context, resolve); + JSValueProtect(env->context, reject); - static napi_value Callback(napi_env env, napi_callback_info cbinfo) { - Wrapper* wrapper = reinterpret_cast(cbinfo->data); - wrapper->resolve = cbinfo->argv[0]; - wrapper->reject = cbinfo->argv[1]; - return nullptr; - } - } wrapper; + *deferred = reinterpret_cast(holder); + *promise = ToNapi(promiseObject); - napi_value executor{}; - CHECK_NAPI(napi_create_function(env, "executor", NAPI_AUTO_LENGTH, Wrapper::Callback, &wrapper, &executor)); - CHECK_NAPI(napi_new_instance(env, promise_ctor, 1, &executor, promise)); + return napi_ok; +} - napi_value deferred_value{}; - CHECK_NAPI(napi_create_object(env, &deferred_value)); - CHECK_NAPI(napi_set_named_property(env, deferred_value, "resolve", wrapper.resolve)); - CHECK_NAPI(napi_set_named_property(env, deferred_value, "reject", wrapper.reject)); +// Shared body for resolve/reject: invokes the stored settle function with the +// given value, then releases and frees the deferred. +static napi_status napi_settle_deferred(napi_env env, + napi_deferred deferred, + napi_value value, + bool resolve) { + CHECK_ENV(env); + CHECK_ARG(env, deferred); + + napi_deferred__* holder{reinterpret_cast(deferred)}; + JSObjectRef settle{resolve ? holder->resolve : holder->reject}; + + JSValueRef exception{}; + JSValueRef argument{ToJSValue(value)}; + JSObjectCallAsFunction(env->context, settle, nullptr, 1, &argument, &exception); - napi_ref deferred_ref{}; - CHECK_NAPI(napi_create_reference(env, deferred_value, 1, &deferred_ref)); - *deferred = reinterpret_cast(deferred_ref); + JSValueUnprotect(env->context, holder->resolve); + JSValueUnprotect(env->context, holder->reject); + delete holder; + CHECK_JSC(env, exception); return napi_ok; } napi_status napi_resolve_deferred(napi_env env, napi_deferred deferred, napi_value resolution) { - CHECK_ENV(env); - CHECK_ARG(env, deferred); - - napi_ref deferred_ref{reinterpret_cast(deferred)}; - napi_value undefined{}, deferred_value{}, resolve{}; - CHECK_NAPI(napi_get_undefined(env, &undefined)); - CHECK_NAPI(napi_get_reference_value(env, deferred_ref, &deferred_value)); - CHECK_NAPI(napi_get_named_property(env, deferred_value, "resolve", &resolve)); - CHECK_NAPI(napi_call_function(env, undefined, resolve, 1, &resolution, nullptr)); - CHECK_NAPI(napi_delete_reference(env, deferred_ref)); - - return napi_ok; + return napi_settle_deferred(env, deferred, resolution, /*resolve*/ true); } napi_status napi_reject_deferred(napi_env env, napi_deferred deferred, napi_value rejection) { - CHECK_ENV(env); - CHECK_ARG(env, deferred); - - napi_ref deferred_ref{reinterpret_cast(deferred)}; - napi_value undefined{}, deferred_value{}, reject{}; - CHECK_NAPI(napi_get_undefined(env, &undefined)); - CHECK_NAPI(napi_get_reference_value(env, deferred_ref, &deferred_value)); - CHECK_NAPI(napi_get_named_property(env, deferred_value, "reject", &reject)); - CHECK_NAPI(napi_call_function(env, undefined, reject, 1, &rejection, nullptr)); - CHECK_NAPI(napi_delete_reference(env, deferred_ref)); - - return napi_ok; + return napi_settle_deferred(env, deferred, rejection, /*resolve*/ false); } napi_status napi_is_promise(napi_env env, @@ -2525,6 +2689,8 @@ napi_status napi_run_script_source(napi_env env, JSString script_str{ToJSString(env, script, &exception)}; CHECK_JSC(env, exception); + + JSValueRef return_value{JSEvaluateScript( env->context, script_str, nullptr, JSString(source_url), 0, &exception)}; CHECK_JSC(env, exception); @@ -2699,4 +2865,278 @@ napi_status napi_object_seal(napi_env env, CHECK_NAPI(napi_get_named_property(env, object_ctor, "seal", &seal)); CHECK_NAPI(napi_call_function(env, object_ctor, seal, 1, &object, nullptr)); return napi_ok; -} \ No newline at end of file +} + +#ifdef USE_HOST_OBJECT + +namespace { +static std::once_flag hostObjectClassOnceFlag; +static JSClassRef hostObjectClass{}; + +// JSC has no dedicated indexed-property callbacks: every property operation +// arrives as a JSStringRef. To honour the `napi_host_object_methods` contract +// (a number for indexed access, a string otherwise) we detect canonical array +// indices ourselves and route them to the `indexed_*` fast paths when present, +// matching the V8 implementation's behaviour. +bool HostObjectToIndex(JSStringRef str, uint32_t* out) { + size_t length{JSStringGetLength(str)}; + if (length == 0) { + return false; + } + const JSChar* chars{JSStringGetCharactersPtr(str)}; + if (length > 1 && chars[0] == '0') { + return false; // reject leading zeros ("01" is not a canonical index) + } + uint64_t value{0}; + for (size_t i = 0; i < length; ++i) { + JSChar ch{chars[i]}; + if (ch < '0' || ch > '9') { + return false; + } + value = value * 10 + (ch - '0'); + if (value > 0xFFFFFFFEull) { // max valid array index is 2^32 - 2 + return false; + } + } + *out = static_cast(value); + return true; +} +} + +// A "host object" is a transparent proxy: every property operation is +// dispatched to the native callbacks in `_methods`, which receive the host +// object itself and the `_data` pointer. The callbacks are wired through a +// single shared JSClass so a host object can be recognized by its class. +struct HostObjectInfo { + napi_env _env; + napi_finalize _finalize; + void* _data; + napi_host_object_methods _methods; + + static JSClassRef Class() { + std::call_once(hostObjectClassOnceFlag, []() { + JSClassDefinition definition{kJSClassDefinitionEmpty}; + definition.className = "NapiHostObject"; + definition.getProperty = GetProperty; + definition.setProperty = SetProperty; + definition.hasProperty = HasProperty; + definition.deleteProperty = DeleteProperty; + definition.getPropertyNames = GetPropertyNames; + definition.finalize = Finalize; + hostObjectClass = JSClassCreate(&definition); + }); + return hostObjectClass; + } + + static HostObjectInfo* From(JSObjectRef object) { + return reinterpret_cast(JSObjectGetPrivate(object)); + } + + // Propagate any exception raised by a napi callback back to JSC. + static bool ForwardException(napi_env env, JSValueRef* exception) { + if (env->last_exception != nullptr) { + if (exception != nullptr) { + *exception = env->last_exception; + } + env->last_exception = nullptr; + return true; + } + return false; + } + + // JSObjectGetPropertyCallback + static JSValueRef GetProperty(JSContextRef ctx, + JSObjectRef object, + JSStringRef propertyName, + JSValueRef* exception) { + HostObjectInfo* info{From(object)}; + if (info == nullptr) { + return nullptr; + } + napi_env env{info->_env}; + napi_clear_last_error(env); + + napi_value host{ToNapi(object)}; + napi_value result{nullptr}; + uint32_t index{}; + if (HostObjectToIndex(propertyName, &index) && + info->_methods.indexed_get != nullptr) { + result = info->_methods.indexed_get(env, host, index, info->_data); + } else { + napi_value prop{ToNapi(JSValueMakeString(ctx, propertyName))}; + result = info->_methods.get(env, host, prop, info->_data); + } + + if (ForwardException(env, exception)) { + return nullptr; + } + return result != nullptr ? ToJSValue(result) : JSValueMakeUndefined(ctx); + } + + // JSObjectSetPropertyCallback + static bool SetProperty(JSContextRef ctx, + JSObjectRef object, + JSStringRef propertyName, + JSValueRef value, + JSValueRef* exception) { + HostObjectInfo* info{From(object)}; + if (info == nullptr) { + return false; + } + napi_env env{info->_env}; + napi_clear_last_error(env); + + napi_value host{ToNapi(object)}; + napi_value val{ToNapi(value)}; + uint32_t index{}; + if (HostObjectToIndex(propertyName, &index) && + info->_methods.indexed_set != nullptr) { + info->_methods.indexed_set(env, host, index, val, info->_data); + } else { + napi_value prop{ToNapi(JSValueMakeString(ctx, propertyName))}; + info->_methods.set(env, host, prop, val, info->_data); + } + + ForwardException(env, exception); + return true; // fully handled + } + + // JSObjectHasPropertyCallback (no exception out-param available) + static bool HasProperty(JSContextRef ctx, + JSObjectRef object, + JSStringRef propertyName) { + HostObjectInfo* info{From(object)}; + if (info == nullptr || info->_methods.has == nullptr) { + return false; + } + napi_env env{info->_env}; + napi_clear_last_error(env); + + napi_value host{ToNapi(object)}; + napi_value prop{ToNapi(JSValueMakeString(ctx, propertyName))}; + bool present{info->_methods.has(env, host, prop, info->_data) != 0}; + env->last_exception = nullptr; + return present; + } + + // JSObjectDeletePropertyCallback + static bool DeleteProperty(JSContextRef ctx, + JSObjectRef object, + JSStringRef propertyName, + JSValueRef* exception) { + HostObjectInfo* info{From(object)}; + if (info == nullptr || info->_methods.delete_property == nullptr) { + return false; + } + napi_env env{info->_env}; + napi_clear_last_error(env); + + napi_value host{ToNapi(object)}; + napi_value prop{ToNapi(JSValueMakeString(ctx, propertyName))}; + bool deleted{info->_methods.delete_property(env, host, prop, info->_data) != 0}; + + ForwardException(env, exception); + return deleted; + } + + // JSObjectGetPropertyNamesCallback + static void GetPropertyNames(JSContextRef ctx, + JSObjectRef object, + JSPropertyNameAccumulatorRef propertyNames) { + HostObjectInfo* info{From(object)}; + if (info == nullptr || info->_methods.own_keys == nullptr) { + return; + } + napi_env env{info->_env}; + napi_clear_last_error(env); + + napi_value host{ToNapi(object)}; + napi_value keys{info->_methods.own_keys(env, host, info->_data)}; + env->last_exception = nullptr; + if (keys == nullptr) { + return; + } + + uint32_t length{}; + if (napi_get_array_length(env, keys, &length) != napi_ok) { + return; + } + for (uint32_t i = 0; i < length; ++i) { + napi_value element{}; + if (napi_get_element(env, keys, i, &element) != napi_ok) { + continue; + } + JSValueRef exception{}; + JSStringRef name{JSValueToStringCopy(ctx, ToJSValue(element), &exception)}; + if (name != nullptr) { + JSPropertyNameAccumulatorAddName(propertyNames, name); + JSStringRelease(name); + } + } + } + + // JSObjectFinalizeCallback + static void Finalize(JSObjectRef object) { + HostObjectInfo* info{From(object)}; + if (info == nullptr) { + return; + } + if (info->_finalize != nullptr) { + info->_finalize(info->_env, info->_data, nullptr); + } + delete info; + } +}; + +napi_status napi_create_host_object(napi_env env, + napi_finalize finalize, + void* data, + const napi_host_object_methods* methods, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, methods); + CHECK_ARG(env, result); + RETURN_STATUS_IF_FALSE( + env, methods->get != nullptr && methods->set != nullptr, napi_invalid_arg); + + HostObjectInfo* info{new HostObjectInfo{env, finalize, data, *methods}}; + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + JSObjectRef object{JSObjectMake(env->context, HostObjectInfo::Class(), info)}; + *result = ToNapi(object); + return napi_ok; +} + +napi_status napi_get_host_object_data(napi_env env, + napi_value object, + void** data) { + CHECK_ENV(env); + CHECK_ARG(env, object); + CHECK_ARG(env, data); + + *data = nullptr; + JSValueRef value{ToJSValue(object)}; + if (JSValueIsObjectOfClass(env->context, value, HostObjectInfo::Class())) { + HostObjectInfo* info{HostObjectInfo::From(ToJSObject(env, object))}; + if (info != nullptr) { + *data = info->_data; + } + } + return napi_ok; +} + +napi_status napi_is_host_object(napi_env env, + napi_value object, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, object); + CHECK_ARG(env, result); + + *result = JSValueIsObjectOfClass( + env->context, ToJSValue(object), HostObjectInfo::Class()); + return napi_ok; +} + +#endif // USE_HOST_OBJECT \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.h b/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.h index d22aa153..326e82d6 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/jsc-api.h @@ -7,17 +7,21 @@ #include "js_native_api.h" #include "js_native_api_types.h" -#include +#include "JavaScriptCore/JavaScript.h" #include #include #include +#include #include + + struct napi_env__ { JSGlobalContextRef context{}; JSValueRef last_exception{}; napi_extended_error_info last_error{nullptr, nullptr, 0, napi_ok}; - std::unordered_set active_ref_values{}; + // Strong (protected) napi references, released on env teardown. Weak + // references are backed by native JSC weak handles inside napi_ref__. std::list strong_refs{}; JSValueRef constructor_info_symbol{}; @@ -27,6 +31,16 @@ struct napi_env__ { const std::thread::id thread_id{std::this_thread::get_id()}; + // Serializes all host->JS entries into this context. JSC's C API takes its + // per-VM lock around each individual call, but a logical host->JS callback + // spans many napi_* calls that must run atomically w.r.t. other threads + // (background-thread JNI callbacks, timers, worker marshaling). Without this, + // two threads interleave inside a single JS callback and corrupt the runtime's + // shared C++ state (ObjectManager/ArgConverter) and JS heap -> SIGSEGV. This + // mirrors QuickJS's per-env recursive mutex taken by NapiScope. Recursive so a + // nested NapiScope on the same thread re-enters instead of self-deadlocking. + std::recursive_mutex js_mutex{}; + napi_env__(JSGlobalContextRef context) : context{context} { napi_envs[context] = this; JSGlobalContextRetain(context); @@ -37,13 +51,18 @@ struct napi_env__ { } ~napi_env__() { + // All of these (deinit_refs and deinit_symbol) call JSValueUnprotect + // on `context`, so the context must stay alive until after they run. + // Releasing it earlier frees the underlying JSC context when this env + // holds the last reference (e.g. a terminating worker), turning the + // subsequent unprotect calls into a use-after-free crash. deinit_refs(); - JSGlobalContextRelease(context); deinit_symbol(wrapper_info_symbol); deinit_symbol(reference_info_symbol); deinit_symbol(function_info_symbol); deinit_symbol(constructor_info_symbol); napi_envs.erase(context); + JSGlobalContextRelease(context); } static napi_env get(JSGlobalContextRef context) { @@ -62,6 +81,11 @@ struct napi_env__ { void deinit_symbol(JSValueRef symbol); }; +napi_status napi_set_exception__(napi_env env, JSValueRef exception) { + env->last_exception = exception; + return napi_ok; +} + #define RETURN_STATUS_IF_FALSE(env, condition, status) \ do { \ if (!(condition)) { \ diff --git a/test-app/runtime/src/main/cpp/napi/jsc/jsr.cpp b/test-app/runtime/src/main/cpp/napi/jsc/jsr.cpp index d5b66d58..feca80bd 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/jsr.cpp +++ b/test-app/runtime/src/main/cpp/napi/jsc/jsr.cpp @@ -1,12 +1,50 @@ #include "jsr.h" +// JSGlobalContextSetUnhandledRejectionCallback lives in this private JSC header. +#include +// Public C API for on-disk program bytecode caching (added by the jsc-android +// build via jsc_android_bytecode_cache.patch). +#include +#include "NativeScriptAssert.h" +#include +#include +#include +#include +#include -napi_status js_create_runtime(napi_runtime *runtime) { +// Defined in jsc-api.cpp (same runtime library): records `exception` as the +// env's pending exception and returns napi_pending_exception. This is the same +// helper napi_run_script_source uses via CHECK_JSC; it has no public prototype, +// so we forward-declare it here. +napi_status napi_set_exception(napi_env env, JSValueRef exception); + +// Native trampoline for JSC's unhandled-promise-rejection callback. JSC invokes +// it with (promise, reason); we forward to the JS-side +// globalThis.onUnhandledPromiseRejectionTracker (installed by ts_helpers.js), +// mirroring how the V8 path routes rejections. +static napi_value JscUnhandledRejectionCallback(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_value global, tracker; + napi_get_global(env, &global); + napi_get_named_property(env, global, "onUnhandledPromiseRejectionTracker", &tracker); + + napi_valuetype type; + napi_typeof(env, tracker, &type); + if (type == napi_function) { + napi_call_function(env, global, tracker, argc, args, nullptr); + } + return nullptr; +} + +napi_status js_create_runtime(jsr_ns_runtime *runtime) { if (!runtime) return napi_invalid_arg; - *runtime = (napi_runtime) JSGlobalContextCreateInGroup(nullptr, nullptr); + *runtime = (jsr_ns_runtime) JSGlobalContextCreateInGroup(nullptr, nullptr); return napi_ok; } -napi_status js_create_napi_env(napi_env* env, napi_runtime runtime) { +napi_status js_create_napi_env(napi_env* env, jsr_ns_runtime runtime) { if (env == nullptr) return napi_invalid_arg; *env = new napi_env__((JSGlobalContextRef) runtime); @@ -23,6 +61,19 @@ napi_status js_create_napi_env(napi_env* env, napi_runtime runtime) { napi_get_global(*env, &global); napi_set_named_property(*env, global, "gc", gc); + // Report unhandled promise rejections. JSC keeps the callback alive (it is + // stored on and marked by the global object), so no extra protection is + // needed. JSC only surfaces the "unhandled" event, not a later "handled" + // retraction, so the JS tracker treats every call as unhandled. + napi_value rejectionCallback; + napi_create_function(*env, "onUnhandledRejection", NAPI_AUTO_LENGTH, + JscUnhandledRejectionCallback, nullptr, &rejectionCallback); + JSValueRef rejectionException = nullptr; + JSGlobalContextSetUnhandledRejectionCallback( + (*env)->context, + reinterpret_cast(rejectionCallback), + &rejectionException); + return napi_ok; @@ -33,10 +84,14 @@ napi_status js_set_runtime_flags(const char* flags) { } napi_status js_lock_env(napi_env env) { + if (env == nullptr) return napi_invalid_arg; + env->js_mutex.lock(); return napi_ok; } napi_status js_unlock_env(napi_env env) { + if (env == nullptr) return napi_invalid_arg; + env->js_mutex.unlock(); return napi_ok; } @@ -46,18 +101,134 @@ napi_status js_free_napi_env(napi_env env) { return napi_ok; } -napi_status js_free_runtime(napi_runtime runtime) { +napi_status js_free_runtime(jsr_ns_runtime runtime) { // JSContextGroupRelease((JSContextGroupRef) runtime); return napi_ok; } +// --------------------------------------------------------------------------- +// Bytecode code cache +// +// JSC has no ahead-of-time bytecode format, so — exactly like the V8 path — we +// cache the engine's own serialized bytecode next to each source file and reuse +// it on later launches. The heavy lifting (serialize / validate / run-from-cache) +// lives in JSC behind the JSBytecodeCache.h C API; here we only manage the cache +// file: where it lives, when it is stale, and publishing it atomically. +// +// The .cache file is stamped with its source file's mtime; a mismatch means the +// source changed and the cache is ignored (and JSC re-validates internally too). +// --------------------------------------------------------------------------- + +// Turn a script's source URL into the on-disk JS path we cache next to. Returns +// false for sources that aren't real files (e.g. the synthetic +// ""), which must never be cached. +static bool NormalizeScriptPath(const char *file, std::string &out) { + if (file == nullptr) return false; + std::string f(file); + static const std::string scheme = "file://"; + if (f.rfind(scheme, 0) == 0) { + out = f.substr(scheme.size()); + } else if (!f.empty() && f[0] == '/') { + out = f; // already a plain absolute path + } else { + return false; + } + return !out.empty(); +} + +// Publish a freshly written temp cache file: stamp it with the source's mtime so +// js_run_cached_script accepts it, then atomically rename it into place. Any +// failure leaves no cache behind. Best-effort. +static void PublishCacheFile(const std::string &tmpPath, const std::string &cachePath, + const std::string &fsPath) { + struct stat srcStat; + struct utimbuf new_times; + new_times.actime = time(nullptr); + new_times.modtime = (stat(fsPath.c_str(), &srcStat) == 0) ? srcStat.st_mtime : time(nullptr); + utime(tmpPath.c_str(), &new_times); + + if (rename(tmpPath.c_str(), cachePath.c_str()) != 0) { + DEBUG_WRITE("[code-cache] failed to publish cache file: %s", cachePath.c_str()); + remove(tmpPath.c_str()); + return; + } + DEBUG_WRITE("[code-cache] wrote JSC bytecode cache: %s", cachePath.c_str()); +} + +// Serialize `sourceStr` to a bytecode cache published next to `fsPath`, stamped +// with the source's mtime. Writes to a temp file first then renames, so a crash +// or a concurrent reader never sees a half-written cache. Best-effort: any +// failure just leaves no cache behind. Returns true if a cache was published. +static bool WriteCacheForSource(napi_env env, JSStringRef sourceStr, JSStringRef sourceUrl, + const std::string &fsPath) { + auto cachePath = fsPath + ".cache"; + auto tmpPath = cachePath + ".tmp"; + + JSStringRef errorMessage = nullptr; + bool ok = JSWriteBytecodeCacheForProgram(env->context, sourceStr, sourceUrl, + tmpPath.c_str(), &errorMessage); + if (!ok) { + if (errorMessage) { + size_t maxSize = JSStringGetMaximumUTF8CStringSize(errorMessage); + std::string msg(maxSize, '\0'); + JSStringGetUTF8CString(errorMessage, &msg[0], maxSize); + DEBUG_WRITE("[code-cache] failed to generate bytecode for %s: %s", + fsPath.c_str(), msg.c_str()); + JSStringRelease(errorMessage); + } else { + DEBUG_WRITE("[code-cache] failed to generate bytecode for %s (no error message)", + fsPath.c_str()); + } + remove(tmpPath.c_str()); + return false; + } + + PublishCacheFile(tmpPath, cachePath, fsPath); + return true; +} + napi_status js_execute_script(napi_env env, napi_value script, const char *file, napi_value *result) { + // Fast path: run JSC's on-disk bytecode cache when one is present and + // current. js_run_cached_script returns napi_cannot_run_js on a cache miss + // (so we fall back to source), or any other status when it actually ran the + // script (success or a thrown exception) — in which case we must NOT run it + // again. + napi_status status = js_run_cached_script(env, file, script, nullptr, result); + if (status != napi_cannot_run_js) { + return status; + } - return napi_run_script_source(env, script, file, result); + // Cold path: publish a bytecode cache for the next launch, then run. When the + // cache is written we run straight from it (JSC decodes the bytecode instead + // of parsing again), so the source is compiled only once even on this first + // launch; otherwise we fall back to running the source directly. + std::string fsPath; + if (NormalizeScriptPath(file, fsPath)) { + JSValueRef exception = nullptr; + JSStringRef sourceStr = JSValueToStringCopy(env->context, + reinterpret_cast(script), + &exception); + if (sourceStr != nullptr && exception == nullptr) { + JSStringRef sourceUrl = JSStringCreateWithUTF8CString(file); + DEBUG_WRITE("[code-cache] compiling from source (cold): %s", file); + bool wrote = WriteCacheForSource(env, sourceStr, sourceUrl, fsPath); + JSStringRelease(sourceUrl); + JSStringRelease(sourceStr); + if (wrote) { + napi_status cached = js_run_cached_script(env, file, script, nullptr, result); + if (cached != napi_cannot_run_js) { + return cached; + } + } + } else if (sourceStr != nullptr) { + JSStringRelease(sourceStr); + } + } + return napi_run_script_source(env, script, file, result); } napi_status js_execute_pending_jobs(napi_env env) { @@ -73,16 +244,91 @@ napi_status js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64 return napi_ok; } +// Compile `source` and publish a bytecode cache next to `file`. Retained for the +// jsr interface; the hot path (js_execute_script) caches inline. No-op for +// synthetic / non-file sources. napi_status js_cache_script(napi_env env, const char *source, const char *file) { + std::string fsPath; + if (!NormalizeScriptPath(file, fsPath)) { + return napi_ok; + } + + JSStringRef sourceStr = JSStringCreateWithUTF8CString(source); + JSStringRef sourceUrl = JSStringCreateWithUTF8CString(file); + WriteCacheForSource(env, sourceStr, sourceUrl, fsPath); + JSStringRelease(sourceUrl); + JSStringRelease(sourceStr); return napi_ok; } napi_status js_run_cached_script(napi_env env, const char *file, napi_value script, void *cache, napi_value *result) { + if (env == nullptr || script == nullptr) return napi_invalid_arg; + + std::string fsPath; + if (!NormalizeScriptPath(file, fsPath)) { + return napi_cannot_run_js; // synthetic / non-file source: no cache + } + + auto cachePath = fsPath + ".cache"; + struct stat cacheStat; + if (stat(cachePath.c_str(), &cacheStat) != 0) { + DEBUG_WRITE("[code-cache] miss (no cache on disk): %s", fsPath.c_str()); + return napi_cannot_run_js; // no cache written yet + } + struct stat srcStat; + if (stat(fsPath.c_str(), &srcStat) == 0 && srcStat.st_mtime != cacheStat.st_mtime) { + // Source changed since the cache was written — ignore the stale cache. + DEBUG_WRITE("[code-cache] miss (source newer than cache): %s", fsPath.c_str()); + return napi_cannot_run_js; + } + + JSValueRef exception = nullptr; + JSStringRef sourceStr = JSValueToStringCopy(env->context, + reinterpret_cast(script), + &exception); + if (sourceStr == nullptr || exception != nullptr) { + if (sourceStr) JSStringRelease(sourceStr); + return napi_cannot_run_js; + } + JSStringRef sourceUrl = JSStringCreateWithUTF8CString(file); + + bool cacheRejected = false; + JSValueRef exc = nullptr; + JSValueRef ret = JSEvaluateProgramWithBytecodeCacheFile(env->context, sourceStr, sourceUrl, + cachePath.c_str(), nullptr, + &cacheRejected, &exc); + JSStringRelease(sourceUrl); + JSStringRelease(sourceStr); + + if (cacheRejected) { + // The cache was stale/incompatible; JSC did NOT run anything. Report a + // miss so the caller runs the source and refreshes the cache. + DEBUG_WRITE("[code-cache] miss (JSC rejected cache as incompatible): %s", fsPath.c_str()); + return napi_cannot_run_js; + } + + if (exc != nullptr) { + // The script threw while executing. This is a real run — surface the + // exception; the caller must NOT execute the source a second time. + return napi_set_exception__(env, exc); + } + + DEBUG_WRITE("[code-cache] loaded JSC bytecode cache: %s", fsPath.c_str()); + if (result != nullptr) { + *result = reinterpret_cast(const_cast(ret)); + } return napi_ok; } +napi_status js_run_bytecode_file(napi_env env, const char *file, napi_value *result) { + // JSC has no compile-time bytecode format (it caches serialized bytecode at + // runtime instead, see js_cache_script/js_run_cached_script); always fall + // back to source. + return napi_cannot_run_js; +} + napi_status js_get_runtime_version(napi_env env, napi_value* version) { napi_create_string_utf8(env, "JSC", NAPI_AUTO_LENGTH, version); diff --git a/test-app/runtime/src/main/cpp/napi/jsc/jsr.h b/test-app/runtime/src/main/cpp/napi/jsc/jsr.h index 3bbc5313..2ecd9d47 100644 --- a/test-app/runtime/src/main/cpp/napi/jsc/jsr.h +++ b/test-app/runtime/src/main/cpp/napi/jsc/jsr.h @@ -8,18 +8,23 @@ #include "jsr_common.h" #include "jsc-api.h" -typedef struct napi_runtime__ *napi_runtime; - class NapiScope { public: explicit NapiScope(napi_env env, bool openHandle = true) : env_(env) { + // Serialize this host->JS entry against all other threads (background-thread + // JNI callbacks, timers, workers). The lock is a per-env recursive mutex, so + // a nested NapiScope on the same thread re-enters rather than deadlocking. + // JSC drains its own microtask queue when the outermost API call returns, so + // unlike QuickJS we don't drain jobs here. + js_lock_env(env_); // napi_open_handle_scope(env_, &napiHandleScope_); } ~NapiScope() { // napi_close_handle_scope(env_, napiHandleScope_); + js_unlock_env(env_); } private: diff --git a/test-app/runtime/src/main/cpp/napi/primjs/code_cache.cc b/test-app/runtime/src/main/cpp/napi/primjs/code_cache.cc deleted file mode 100644 index 1194bda0..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/code_cache.cc +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. - * - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file in the root of the source tree. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#include "code_cache.h" - -#include - -#include -#include - -#if OS_ANDROID -#include "basic/log/logging.h" -#else -#define VLOGD(...) void((__VA_ARGS__)) -#endif // OS_ANDROID - -#ifdef PROFILE_CODECACHE -#define INCREASE(target) ++(target) -#else -#define INCREASE(target) -#endif // PROFILE_CODECACHE - -constexpr double CacheBlob::MAGIC; - -bool CacheBlob::insert(const std::string& filename, const uint8_t* data, - int length) { - // too large (more than half of MAX) cached data must be discarded - if (length == 0 || data == nullptr) { - INCREASE(expired_query_); - return false; - } - CachedData* target = nullptr; - const uint8_t* old_data = nullptr; - { - std::unique_lock lock(write_mutex_); - auto it = cache_map_.find(filename); - if (it != cache_map_.end()) { - target = it->second; - current_size_ -= target->length_; - remove_from_ranking_list(target); - - if (get_enough_space(length)) { - if (mode_ == kAppending) mode_ = kWriting; - target->length_ = length; - } else { - cache_map_.erase(it); - return false; - } - - } else if (get_enough_space(length)) { - target = new CachedData(length, nullptr, filename); - cache_map_[filename] = target; - if (mode_ == kAppending) { - if (append_vec_ == nullptr) { - append_vec_ = new CacheVector(); - } - append_vec_->push_back(target); - } - } else { - return false; - } - - old_data = target->data_; - target->data_ = data; - } - - heat_ranking_.push_back(target); - current_size_ += length; - if (old_data) { - delete[] old_data; - } - INCREASE(target->used_times_); - - return true; -} - -// That len will be not nullptr is ensured by the context. -const CachedData* CacheBlob::find(const std::string& filename, int* len) const { - if (!write_mutex_.try_lock()) { - INCREASE(missed_query_); - INCREASE(total_query_); - return empty_cache_.get(); - } - auto it = cache_map_.find(filename); - write_mutex_.unlock(); - CachedData* result = nullptr; - if (it != cache_map_.end()) { - // every time a cache is searched, its heat-ranking - // rises and thus the ranking list changes - result = it->second; - *len = result->length_; - INCREASE(result->used_times_); - } else { - *len = 0; - INCREASE(missed_query_); - } - INCREASE(total_query_); - - // NOTE: - // The result cached data is safe beyond this scope, - // because JS engines got this data and then use it - // consecutively in one thread. Tasks that insert new - // data for the same filename will only be posted after - // that cached data has been already used up. - return result; -} - -void CacheBlob::remove(const std::string& filename) { - auto it = cache_map_.find(filename); - if (it != cache_map_.end()) { - current_size_ -= it->second->length_; - delete it->second; - cache_map_.erase(it); - if (mode_ == kAppending) mode_ = kWriting; - } -} - -// cache file structure: -// Header: -// | magic number | --> 8 bytes -// Body : -// | file name size | --> 2 bytes -// | file name | --> x bytes -// | cache data length | --> 4 bytes -// | cache data | --> y bytes -// ... -void CacheBlob::output() { - if (mode_ == kAppending && append_vec_ == nullptr) return; - bool appending = mode_ == kAppending && append_vec_ != nullptr; - - FILE* file_out = appending ? fopen(target_path_.c_str(), "ab") - : fopen(target_path_.c_str(), "wb"); - if (file_out) { - if (appending) { - for (auto it : *append_vec_) write_cache_unit(file_out, it); - } else { - fwrite(&MAGIC, DOUBLE_SIZE, 1, file_out); - for (auto& it : cache_map_) write_cache_unit(file_out, it.second); - } - fclose(file_out); - VLOGD("codecache: output cache file %s succeed.\n", target_path_.c_str()); - } -} - -// steps to rebuild blob: -// 1. read and check magic number; -// 2. read 2 bytes to get the size (x) of file name; -// 3. read x bytes to get the file name; -// 4. read 4 bytes to get the size (y) of cache data; -// 5. read y bytes to get the actual content of cache data; -// 6. go back to step 2 until EOF -bool CacheBlob::input() { - FILE* file_in = fopen(target_path_.c_str(), "rb"); - - bool succeeded = false; - if (file_in) { - double maybe_magic; - fread(reinterpret_cast(&maybe_magic), DOUBLE_SIZE, 1, file_in); - // check whether file is valid. - if (maybe_magic == MAGIC) { - int c; - while ((c = fgetc(file_in)) != EOF) { - ungetc(c, file_in); - read_cache_unit(file_in); - } - mode_ = kAppending; - succeeded = true; - } - fclose(file_in); - } - return succeeded; -} - -#ifdef PROFILE_CODECACHE -void CacheBlob::dump_status(void* p) { - std::vector >* status_vec = - reinterpret_cast >*>(p); - std::sort(heat_ranking_.begin(), heat_ranking_.end(), CachedData::compare); - status_vec->push_back(std::pair("Total", total_query_)); - status_vec->push_back(std::pair("Missed", missed_query_)); - status_vec->push_back(std::pair("Expired", expired_query_)); - status_vec->push_back(std::pair( - "Updated", mode_ == kAppending && append_vec_ == nullptr ? 0 : 1)); - status_vec->push_back(std::pair("Size", current_size_)); - status_vec->push_back(std::pair("Heat Ranking, total ", - heat_ranking_.size())); - for (size_t i = 0; i < heat_ranking_.size(); ++i) { - CachedData* dt = heat_ranking_[i]; - status_vec->push_back( - std::pair(dt->file_name_, dt->used_times_)); - } -} -#endif // PROFILE_CODECACHE - -void CacheBlob::write_cache_unit(FILE* file_out, const CachedData* unit) { - // write file name - uint16_t size = static_cast(unit->file_name_.size()); - fwrite(static_cast(&size), SHORT_SIZE, 1, file_out); - fwrite(unit->file_name_.c_str(), 1, unit->file_name_.size(), file_out); - - uint32_t length = unit->length_; - fwrite(static_cast(&length), INT_SIZE, 1, file_out); - fwrite(unit->data_, 1, unit->length_, file_out); -} - -void CacheBlob::read_cache_unit(FILE* file_in) { - uint16_t filename_length; - fread(&filename_length, SHORT_SIZE, 1, file_in); - std::string name(filename_length, '\0'); - fread(&name[0], 1, filename_length, file_in); - - uint32_t data_length; - fread(&data_length, INT_SIZE, 1, file_in); - uint8_t* data = new uint8_t[data_length]; - fread(data, 1, data_length, file_in); - - CachedData* cd = new CachedData(static_cast(data_length), data, name); - - current_size_ += data_length; - heat_ranking_.push_back(cd); - cache_map_[name] = cd; -} - -void CacheBlob::remove_from_ranking_list(CachedData* target) { - for (size_t i = 0; i < heat_ranking_.size(); ++i) { - if (target == heat_ranking_[i]) { - heat_ranking_.erase(heat_ranking_.begin() + i); - break; - } - } -} - -// This is a vast-time-costing function -bool CacheBlob::get_enough_space(int data_size) { - // 0. check whether available space is enough - if (current_size_ + data_size <= max_capacity_) return true; - int size_needed = data_size + current_size_ - max_capacity_; - // 1. sort the heat_ranking_ - std::sort(heat_ranking_.begin(), heat_ranking_.end(), CachedData::compare); - // 2. pick CachedDatas - for (int i = heat_ranking_.size() - 1; i >= 0; --i) { - size_needed -= heat_ranking_[i]->length_; - if (size_needed <= 0) { - for (size_t j = i; j < heat_ranking_.size(); ++j) { - CachedData* it = heat_ranking_[j]; - remove(it->file_name_); - } - heat_ranking_.erase(heat_ranking_.begin() + i, heat_ranking_.end()); - return true; - } - } - return false; -} diff --git a/test-app/runtime/src/main/cpp/napi/primjs/code_cache.h b/test-app/runtime/src/main/cpp/napi/primjs/code_cache.h deleted file mode 100644 index f10a44ee..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/code_cache.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. - * - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file in the root of the source tree. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_NAPI_COMMON_CODE_CACHE_H_ -#define SRC_NAPI_COMMON_CODE_CACHE_H_ - -#include -#include -#include -#include -#include - -struct CachedData { - CachedData() : length_(0), data_(nullptr), file_name_("") {} - - CachedData(int length, const uint8_t* data, const std::string& name) - : length_(length), data_(data), file_name_(name) {} - - // carefully copy and move objects of this class - ~CachedData() { - if (data_) delete[] data_; - } - - static bool compare(CachedData* left, CachedData* right) { - if (left->used_times_ > right->used_times_) { - return true; - } else if (left->used_times_ < right->used_times_) { - return false; - } else { - return left->length_ > right->length_; - } - } - - int used_times_ = 0; - int length_; - const uint8_t* data_; - const std::string file_name_; -}; - -typedef std::unordered_map CacheMap; -typedef std::vector CacheVector; -#define SHORT_SIZE 2 -#define INT_SIZE 4 -#define DOUBLE_SIZE 8 - -class CacheBlob { - private: - enum CacheMode { kWriting, kAppending }; - static constexpr double MAGIC = 3.14159265; - - public: - explicit CacheBlob(const std::string& path, int max_cap = 1 << 20) - : current_size_(0), - target_path_(path), - max_capacity_(max_cap), - empty_cache_(new CachedData) {} - - virtual ~CacheBlob() { - for (auto it : cache_map_) delete it.second; - if (append_vec_) delete append_vec_; - } - - // NOTE: modifications on CacheBlob can only happen in worker Thread. - bool insert(const std::string& filename, const uint8_t* data, int length); - const CachedData* find(const std::string& filename, int* len) const; - void remove(const std::string& filename); - void output(); - bool input(); - int size() const { return current_size_; } - -#ifdef PROFILE_CODECACHE - void dump_status(void* p); -#endif // PROFILE_CODECACHE - - private: - void write_cache_unit(FILE* file_out, const CachedData* unit); - void read_cache_unit(FILE* file_in); - void remove_from_ranking_list(CachedData* target); - // This is a vast-time-costing function - bool get_enough_space(int data_size); - - CacheMap cache_map_; - // ranking list for caches' frequency of being used. - CacheVector heat_ranking_; - int current_size_; - const std::string target_path_; - int max_capacity_; - mutable std::mutex write_mutex_; - std::unique_ptr empty_cache_; - -#ifdef PROFILE_CODECACHE - mutable int total_query_ = 0; - mutable int missed_query_ = 0; - mutable int expired_query_ = 0; -#endif // PROFILE_CODECACHE - CacheMode mode_ = kWriting; - CacheVector* append_vec_ = nullptr; -}; - -#endif // SRC_NAPI_COMMON_CODE_CACHE_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/allocator.h b/test-app/runtime/src/main/cpp/napi/primjs/include/allocator.h similarity index 98% rename from test-app/runtime/src/main/cpp/napi/primjs/include/gc/allocator.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/allocator.h index bcf6f0d9..8ccbe123 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/allocator.h +++ b/test-app/runtime/src/main/cpp/napi/primjs/include/allocator.h @@ -93,6 +93,9 @@ struct malloc_state { size_t footprint; size_t max_footprint; size_t footprint_limit; + size_t outer_heap_size; + size_t gc_info_threshold; + size_t gc_info_interval_size; flag_t mflags; #if USE_LOCKS MLOCK_T mutex; diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/base_export.h b/test-app/runtime/src/main/cpp/napi/primjs/include/base_export.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/base_export.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/base_export.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/basic/log/logging.h b/test-app/runtime/src/main/cpp/napi/primjs/include/basic/log/logging.h deleted file mode 100644 index 2cb633ee..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/basic/log/logging.h +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_BASIC_LOG_LOGGING_H_ -#define SRC_BASIC_LOG_LOGGING_H_ - -#include -#include -#include - -#include "quickjs/include/base_export.h" - -#if defined(OS_ANDROID) -#include - -#define VLOGW(...) __android_log_print(ANDROID_LOG_WARN, "PRIMJS", __VA_ARGS__) -#define VLOGE(...) __android_log_print(ANDROID_LOG_ERROR, "PRIMJS", __VA_ARGS__) -#define VLOGI(...) __android_log_print(ANDROID_LOG_INFO, "PRIMJS", __VA_ARGS__) -#define VLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PRIMJS", __VA_ARGS__) -#else -#include - -#define VLOGW(format, ...) \ - fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) -#define VLOGE(format, ...) \ - fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) -#define VLOGI(format, ...) \ - fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) -#define VLOGD(format, ...) \ - fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) -#endif - -namespace primjs { -namespace general { -namespace logging { - -class LogMessage; -void Log(LogMessage *msg); - -QJS_HIDE void SetMinLogLevel(int level); - -QJS_EXPORT int GetMinAllLogLevel(); - -#define PRIMJS_LOG_LEVEL_VERBOSE -1 -#define PRIMJS_LOG_LEVEL_INFO 0 -#define PRIMJS_LOG_LEVEL_WARNING 1 -#define PRIMJS_LOG_LEVEL_ERROR 2 -#define PRIMJS_LOG_LEVEL_FATAL 3 -#define PRIMJS_LOG_LEVEL_NUM 4 - -typedef int LogSeverity; -const LogSeverity LOG_VERBOSE = PRIMJS_LOG_LEVEL_VERBOSE; -const LogSeverity LOG_INFO = PRIMJS_LOG_LEVEL_INFO; -const LogSeverity LOG_WARNING = PRIMJS_LOG_LEVEL_WARNING; -const LogSeverity LOG_ERROR = PRIMJS_LOG_LEVEL_ERROR; -const LogSeverity LOG_FATAL = PRIMJS_LOG_LEVEL_FATAL; -const LogSeverity LOG_NUM_SEVERITIES = PRIMJS_LOG_LEVEL_NUM; - -// This class is used to explicitly ignore values in the conditional -// logging macros. This avoids compiler warnings like "value computed -// is not used" and "statement has no effect". -class LogMessageVoidify { - public: - LogMessageVoidify() = default; - // This has to be an operator with a precedence lower than << but - // higher than ?: - void operator&(std::ostream &) {} -}; - -#define LOG_IS_ON(severity) \ - ((primjs::general::logging::LOG_##severity) >= \ - primjs::general::logging::GetMinAllLogLevel()) - -#define LOG_STREAM(severity) \ - primjs::general::logging::LogMessage( \ - __FILE__, __LINE__, primjs::general::logging::LOG_##severity) \ - .stream() - -#define LAZY_STREAM(stream, condition) \ - !(condition) ? (void)0 \ - : primjs::general::logging::LogMessageVoidify() & (stream) - -// Use this macro to suppress warning if the variable in log is not used. -#define UNUSED_LOG_VARIABLE __attribute__((unused)) - -#ifndef PRIMJS_MIN_LOG_LEVEL -#define PRIMJS_MIN_LOG_LEVEL PRIMJS_LOG_LEVEL_VERBOSE -#endif - -// TODO(zhixuan): Currently, the usage of log macros is like "LOGI("abc" << -// variable)", which is mixed of stream pattern and format string pattern. -// Change the loggin fashion entirely to format string pattern in future. -#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_VERBOSE -#define LOGV(msg) LAZY_STREAM(LOG_STREAM(VERBOSE), LOG_IS_ON(VERBOSE)) << msg -#define DLOGV(msg) LAZY_STREAM(LOG_STREAM(VERBOSE), LOG_IS_ON(VERBOSE)) << msg -#else -#define LOGV(msg) -#define DLOGV(msg) -#endif - -#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_INFO -#define LOGI(msg) LAZY_STREAM(LOG_STREAM(INFO), LOG_IS_ON(INFO)) << msg -#define DLOGI(msg) LAZY_STREAM(LOG_STREAM(INFO), LOG_IS_ON(INFO)) << msg -#else -#define LOGI(msg) -#define DLOGI(msg) -#endif - -#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_WARNING -#define LOGW(msg) LAZY_STREAM(LOG_STREAM(WARNING), LOG_IS_ON(WARNING)) << msg -#define DLOGW(msg) LAZY_STREAM(LOG_STREAM(WARNING), LOG_IS_ON(WARNING)) << msg -#else -#define LOGW(msg) -#define DLOGW(msg) -#endif - -#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_ERROR -#define LOGE(msg) LAZY_STREAM(LOG_STREAM(ERROR), LOG_IS_ON(ERROR)) << msg -#define DLOGE(msg) LAZY_STREAM(LOG_STREAM(ERROR), LOG_IS_ON(ERROR)) << msg -#else -#define LOGE(msg) -#define DLOGE(msg) -#endif - -#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_FATAL -#define LOGF(msg) LAZY_STREAM(LOG_STREAM(FATAL), LOG_IS_ON(FATAL)) << msg -#define DLOGF(msg) LAZY_STREAM(LOG_STREAM(FATAL), LOG_IS_ON(FATAL)) << msg -#else -#define LOGF(msg) -#define DLOGF(msg) -#endif - -#ifndef DCHECK -// for debug, if check failed, log fatal and abort -#if !defined(NDEBUG) -#define DCHECK(condition) \ - LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ - << "Check failed: " #condition ". " -#else -// for release, do nothing -#define DCHECK(condition) !(condition) ? (void)0 : (void)0 -#endif -#endif - -#define NOTREACHED() LOGF("") - -class QJS_EXPORT LogMessage { - public: - LogMessage(const char *file, int line, LogSeverity severity); - ~LogMessage(); - std::ostringstream &stream(); - LogSeverity severity() { return severity_; } - - private: - void Init(const char *file, int line); - - LogSeverity severity_; - std::ostringstream stream_; - - const char *file_; - const int line_; - LogMessage(const LogMessage &) = delete; - LogMessage &operator=(const LogMessage &) = delete; -}; - -} // namespace logging -} // namespace general -} // namespace primjs - -#endif // SRC_BASIC_LOG_LOGGING_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/cutils.h b/test-app/runtime/src/main/cpp/napi/primjs/include/cutils.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/cutils.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/cutils.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/base-global-handles.h b/test-app/runtime/src/main/cpp/napi/primjs/include/gc/base-global-handles.h deleted file mode 100644 index 348e8724..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/base-global-handles.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2009 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_GC_BASE_GLOBAL_HANDLES_H_ -#define SRC_GC_BASE_GLOBAL_HANDLES_H_ - -#define ITERATOR(NODETYPE) \ - NodeIterator final { \ - public: \ - explicit NodeIterator(NodeBlock* block) : block_(block) {} \ - NodeIterator(NodeIterator&& other) \ - : block_(other.block_), index_(other.index_) {} \ - NodeIterator(const NodeIterator&) = delete; \ - NodeIterator& operator=(const NodeIterator&) = delete; \ - bool operator==(const NodeIterator& other) const { \ - return block_ == other.block_; \ - } \ - bool operator!=(const NodeIterator& other) const { \ - return block_ != other.block_; \ - } \ - NodeIterator& operator++() { \ - if (++index_ < kBlockSize) return *this; \ - index_ = 0; \ - block_ = block_->next_used(); \ - return *this; \ - } \ - NODETYPE* operator*() { return block_->at(index_); } \ - NODETYPE* operator->() { return block_->at(index_); } \ - \ - private: \ - NodeBlock* block_ = nullptr; \ - size_t index_ = 0; \ - }; - -#define NODEBLOCK(HANDLETYPE, NODETYPE) \ - NodeBlock final { \ - public: \ - inline static const NodeBlock* From(const NODETYPE* node); \ - inline static NodeBlock* From(NODETYPE* node); \ - NodeBlock(HANDLETYPE* global_handles, HANDLETYPE::NodeSpace* space, \ - NodeBlock* next) \ - : next_(next), global_handles_(global_handles), space_(space) {} \ - ~NodeBlock() = default; \ - NodeBlock(const NodeBlock&) = delete; \ - NodeBlock& operator=(const NodeBlock&) = delete; \ - NODETYPE* at(size_t index) { return &nodes_[index]; } \ - const NODETYPE* at(size_t index) const { return &nodes_[index]; } \ - HANDLETYPE::NodeSpace* space() const { return space_; } \ - HANDLETYPE* global_handles() const { return global_handles_; } \ - inline bool IncreaseUsage(); \ - inline bool DecreaseUsage(); \ - inline void ListAdd(NodeBlock** top); \ - inline void ListRemove(NodeBlock** top); \ - NodeBlock* next() const { return next_; } \ - NodeBlock* next_used() const { return next_used_; } \ - const void* begin_address() const { return nodes_; } \ - const void* end_address() const { return &nodes_[kBlockSize]; } \ - \ - private: \ - NODETYPE nodes_[kBlockSize]; \ - NodeBlock* const next_; \ - HANDLETYPE* const global_handles_; \ - HANDLETYPE::NodeSpace* const space_; \ - NodeBlock* next_used_ = nullptr; \ - NodeBlock* prev_used_ = nullptr; \ - uint32_t used_nodes_ = 0; \ - }; -#endif // SRC_GC_BASE_GLOBAL_HANDLES_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/collector.h b/test-app/runtime/src/main/cpp/napi/primjs/include/gc/collector.h deleted file mode 100644 index ada95c1a..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/collector.h +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_GC_COLLECTOR_H_ -#define SRC_GC_COLLECTOR_H_ - -#include - -#include "gc/trace-gc.h" -#include "quickjs/include/quickjs-inner.h" - -enum HandleType; -class Visitor; -class Finalizer; -class Sweeper; -typedef struct JSString JSAtomStruct; -struct LEPUSRuntime; -typedef struct malloc_state *mstate; -struct JSAsyncFunctionState; -struct JSProperty; -struct JSRegExp; -struct BCReaderState; -struct JSToken; -struct ValueBuffer; -class GarbageCollector { - public: - GarbageCollector(LEPUSRuntime *rt, mstate) noexcept; - ~GarbageCollector(); - // gc - void CollectGarbage(size_t size = 0) noexcept; - void DoOnlyFinalizer() noexcept; - - void Init(LEPUSRuntime *rt); - Visitor *GetVisitor() { return visitor; } - Finalizer *GetFinalizer() { return finalizer; } - // gc pause suppression mode - void SetGCPauseSuppressionMode(bool mode) { - gc_pause_suppression_mode_ = mode; - } - bool GetGCPauseSuppressionMode() { return gc_pause_suppression_mode_; } - // forbid_gc - void SetForbidGC() { forbid_gc_++; } - void ResetForbidGC() { forbid_gc_--; } - - // for debug -#ifdef ENABLE_GC_DEBUG_TOOLS - size_t mem_order_cnt; - std::unordered_map cur_mems; - std::unordered_set delete_mems[THREAD_NUM]; - size_t delete_order_cnt; - std::unordered_map del_mems; - - size_t handle_order_cnt; - std::unordered_map cur_handles; - size_t qjsvalue_order_cnt; - std::unordered_map cur_qjsvalues; -#endif - - size_t GetHandleSize() { -#ifdef ENABLE_GC_DEBUG_TOOLS - return cur_handles.size(); -#else - return 0; -#endif - } - - size_t GetQjsValueSize() { -#ifdef ENABLE_GC_DEBUG_TOOLS - return cur_qjsvalues.size(); -#else - return 0; -#endif - } - - void SetMaxLimit(size_t limit); - size_t GetMaxLimit(); - void AddGCDuration(int64_t gc_time) { total_duration += gc_time; } - int64_t GetGCDuration() { return total_duration; } - int js_ref_count; - - private: - // gc - void MarkLiveObjects() noexcept; - void SweepDeadObjects() noexcept; -#ifdef ENABLE_TRACING_GC_LOG - void PrintGCLog(int64_t mark_begin, int64_t mark_end) noexcept; -#endif - void UpdateFootprintLimit(size_t size) noexcept; - void UpdateNGFootprintLimit(size_t size) noexcept; - void UpdateGCInfo(size_t heapsize_before, int64_t duration); - - // field - LEPUSRuntime *rt_; - int forbid_gc_; - bool gc_pause_suppression_mode_ = false; - Visitor *visitor; - Finalizer *finalizer; - Sweeper *sweeper; - size_t max_limit; -#ifdef ENABLE_TRACING_GC_LOG - int64_t gc_begin_time; - int64_t last_gc_time; -#endif - int64_t total_duration; - std::stringstream gc_info; - int info_size; -}; - -class Visitor { - public: - Visitor(LEPUSRuntime *rt) noexcept - : rt_(rt), objs(nullptr), objs_len(0), idx(0) { - for (int i = 0; i < THREAD_NUM; i++) { - queue[i] = new Queue(rt_); - } - } - ~Visitor() { - for (int i = 0; i < THREAD_NUM; i++) { - delete queue[i]; - } - if (objs != nullptr) { - system_free(objs); - } - } - void ScanRoots(); - void VisitRootLEPUSValue(LEPUSValue *val, int local_idx) noexcept; - void VisitRootLEPUSValue(LEPUSValue &val, int local_idx) noexcept; - void AddObjectDuringGC(void *ptr) { - if (objs == nullptr) { - objs = static_cast(system_malloc(16 * sizeof(void *))); - objs_len = 16; - } - if (idx >= objs_len) { - int new_len = objs_len * 2; - void **new_objs = - static_cast(system_realloc(objs, new_len * sizeof(void *))); - if (!new_objs) abort(); - objs = new_objs; - objs_len = new_len; - } - objs[idx] = ptr; - idx++; - } - void VisitObjectDuringGC() { - for (int i = 0; i < idx; i++) { - VisitRootHeapObj(objs[i], 0); - } - idx = 0; - } - - private: - // private scanner - void ScanStack() noexcept; - void ScanRuntime() noexcept; - void ScanHandles() noexcept; - void ScanContext(LEPUSContext *ctx) noexcept; - - // visit root - void VisitRoot(void *ptr, HandleType type, int local_idx) noexcept; - void VisitRootHeapObj(void *ptr, int local_idx) noexcept; - void VisitRootHeapObjForTask(Queue *q, void *ptr) noexcept; - void VisitRootCString(char *cstr, int local_idx) noexcept; - void VisitRootJSToken(JSToken *token, int local_idx) noexcept; - void VisitRootBCReaderState(BCReaderState *s, int local_idx) noexcept; - void VisitRootValueBuffer(ValueBuffer *b, int local_idx) noexcept; - void VisitJSAtom(JSAtom atom, int local_idx) noexcept; - - // visitor - /* LEPUSValue with tag -> begin */ - void VisitEntry(void *ptr, int local_idx) noexcept; - void VisitLEPUSLepusRef(void *ptr, int local_idx) noexcept; - void VisitJShape(void *ptr, int local_idx) noexcept; - void VisitJSVarRef(void *ptr, int local_idx) noexcept; - void VisitJSFunctionBytecode(void *ptr, int local_idx) noexcept; - void VisitJSObject(void *ptr, int local_idx) noexcept; - /* LEPUSValue with tag -> end */ - // LEPUS_TAG_BIG_INT - // LEPUS_TAG_BIG_FLOAT - /* LEPUSObject with class_id -> begin */ - void VisitJSBoundFunction(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSCFunctionDataRecord(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSForInIterator(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSArrayBuffer(void *ptr, int local_idx) noexcept; - void VisitJSTypedArray(void *ptr, int local_idx) noexcept; - void VisitJSMapState(void *ptr, int local_idx) noexcept; - void VisitJSMapIteratorData(void *ptr, int local_idx) noexcept; - void VisitJSArrayIteratorData(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSRegExpStringIteratorData(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSGeneratorData(void *ptr, int local_idx) noexcept; - void VisitJSProxyData(void *ptr, int local_idx) noexcept; // normal_free - void VisitJSPromiseData(void *ptr, int local_idx) noexcept; // normal_free - void VisitJSPromiseReactionData(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSPromiseFunctionData(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSAsyncFunctionData(void *ptr, int local_idx) noexcept; - void VisitJSAsyncFromSyncIteratorData(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSAsyncGeneratorData(void *ptr, int local_idx) noexcept; - /* LEPUSObject with class_id -> end */ - // scan context -#ifdef ENABLE_QUICKJS_DEBUGGER - void VisitJSScriptSource(void *ptr, int local_idx) noexcept; -#endif - // other - void VisitJSPropertyEnum(void *ptr, - int local_idx) noexcept; // normal_free - void VisitJSModuleDef(void *ptr, int local_idx) noexcept; - void VisitJSFunctionDef(void *ptr, int local_idx) noexcept; - void VisitJSValueArray(void *ptr, int local_idx) noexcept; - void VisitValueSlot(void *ptr, int local_idx) noexcept; - void VisitJsonStrArray(void *ptr, int local_idx) noexcept; - - void VisitSeparableString(void *ptr, int local_idx) noexcept; - void VisitDebuggerInfo(void *, int32_t) noexcept; - void VisitFinalizationRegistryData(void *ptr, int local_idx) noexcept; - - // push ptr - void PushObjLEPUSValue(LEPUSValue &val, int local_idx) noexcept; - void PushObjLEPUSValue(LEPUSValue *val, int local_idx) noexcept; - void PushObjAtom(JSAtom atom, int local_idx) noexcept; - void PushObjJSAsyncFunctionState(JSAsyncFunctionState *s, - int local_idx) noexcept; - void PushObjJStackFrame(LEPUSStackFrame *sf, int local_idx) noexcept; - void PushBytecodeAtoms(const uint8_t *bc_buf, int bc_len, - int use_short_opcodes, int local_idx) noexcept; - void PushObjJSRegExp(JSRegExp *re, int local_idx) noexcept; - void PushObjFunc(LEPUSObject *obj, int local_idx) noexcept; - void PushObjArray(LEPUSObject *obj, int local_idx) noexcept; - void PushObjRegExp(LEPUSObject *obj, int local_idx) noexcept; - void PushObjProperty(JSProperty *pr, int prop_flags, int local_idx) noexcept; - - // tools - bool IsConstString(void *ptr) { - return get_alloc_tag(ptr) == ALLOC_TAG_JSConstString; - } - // field - LEPUSRuntime *rt_; - Queue *queue[THREAD_NUM]; // for visit - void **objs; - int objs_len; - int idx; -}; - -class Finalizer { - public: - Finalizer(LEPUSRuntime *rt) noexcept : rt_(rt) {} - void close_var_refs(LEPUSStackFrame *sf) noexcept; - void free_atom(LEPUSRuntime *rt, JSAtomStruct *p) noexcept; - // do finalizer - void DoFinalizer(void *ptr) noexcept; - void DoFinalizer2(void *ptr) noexcept; -#ifdef ENABLE_LEPUSNG - void JSLepusRefFinalizer(void *ptr) noexcept; -#endif -#ifdef CONFIG_BIGNUM - void JSBigFloatFinalizer(void *ptr) noexcept; -#endif - void JSObjectFinalizer(void *ptr) noexcept; - void JSObjectOnlyFinalizer(void *ptr) noexcept; - void JSStringFinalizer(void *ptr) noexcept; -#ifdef ENABLE_LEPUSNG - void JSStringOnlyFinalizer(void *ptr) noexcept; -#endif - void JSSymbolFinalizer(void *ptr) noexcept; - void JSShapeFinalizer(void *ptr) noexcept; - void JSVarRefFinalizer(void *ptr) noexcept; - void JSFunctionBytecodeFinalizer(void *ptr) noexcept; - void JSArrayBufferFinalizer(void *ptr) noexcept; - void JSTypedArrayFinalizer(void *ptr) noexcept; - void JSMapStateFinalizer(void *ptr) noexcept; - void JSMapIteratorDataFinalizer(void *ptr) noexcept; - void JSGeneratorDataFinalizer(void *ptr) noexcept; - void JSAsyncFunctionDataFinalizer(void *ptr) noexcept; - void JSAsyncGeneratorDataFinalizer(void *ptr) noexcept; - void JSModuleDefFinalizer(void *ptr) noexcept; - void JSFunctionDefFinalizer(void *ptr) noexcept; - void JSSeparableStringFinalizer(void *ptr) noexcept {} - void FinalizationRegistryDataFinalizer(void *ptr) noexcept; - void WeakRefDataFinalizer(void *ptr) noexcept; - - private: - LEPUSRuntime *rt_; -}; - -class MlockScope { - public: - MlockScope(Queue **q) : queue(q) { -#ifndef _WIN32 - for (int i = 0; i < THREAD_NUM; i++) { - SYSCALL_CHECK( - mlock(queue[i]->GetQueue(), queue[i]->GetSize() * sizeof(uintptr_t))) - } -#endif - } - ~MlockScope() { -#ifndef _WIN32 - for (int i = 0; i < THREAD_NUM; i++) { - SYSCALL_CHECK(munlock(queue[i]->GetQueue(), - queue[i]->GetSize() * sizeof(uintptr_t))); - } -#endif - } - - private: - Queue **queue; -}; -#endif // SRC_GC_COLLECTOR_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/qjsvaluevalue-space.h b/test-app/runtime/src/main/cpp/napi/primjs/include/gc/qjsvaluevalue-space.h deleted file mode 100644 index 09668f55..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/qjsvaluevalue-space.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2009 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_GC_QJSVALUEVALUE_SPACE_H_ -#define SRC_GC_QJSVALUEVALUE_SPACE_H_ - -#include "gc/persistent-handle.h" -class QJSValueValueSpace final { - public: - QJSValueValueSpace(const QJSValueValueSpace&) = delete; - QJSValueValueSpace& operator=(const QJSValueValueSpace&) = delete; - - class NodeBlock; - class NodeIterator; - class NodeSpace; - - static void Destroy(void* location); - explicit QJSValueValueSpace(LEPUSRuntime* runtime); - ~QJSValueValueSpace(); - void* Create(); - void IterateAllRoots(int local_idx); - LEPUSRuntime* runtime() const { return runtime_; } - - private: - LEPUSRuntime* runtime_; - NodeSpace* regular_nodes_; -}; - -#endif // SRC_GC_QJSVALUEVALUE_SPACE_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/sweeper.h b/test-app/runtime/src/main/cpp/napi/primjs/include/gc/sweeper.h deleted file mode 100644 index df7dacab..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/sweeper.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_GC_SWEEPER_H_ -#define SRC_GC_SWEEPER_H_ -#include - -#include "quickjs/include/quickjs-inner.h" - -class Sweeper { - public: - Sweeper(mstate state) : m(state) {} - void sweep_finalizer(); - void sweep_free(); - void traverse_finalizer(bool is_only, int64_t begin_time); - void traverse_chunk_for_finalizer(bool is_only = false); - void free_mmap_objects(); - void traverse_chunk_for_free(); - void reinit_freelist(); - int calculate_task_granularity(); - static void generate_freelist(mstate m, msegmentptr sp_begin, - msegmentptr sp_end); - - private: - mstate m; -}; - -void do_finalizer(void* runtime, void* ptr, bool is_only); - -void do_global_finalizer(void* rt); - -void merge_dead_objs(mstate m, msegmentptr sp_begin, msegmentptr sp_end); - -class AcquireIdxScope { - private: - int local_idx; - mstate m_; - - public: - AcquireIdxScope(mstate m) { - local_idx = atomic_acqurie_local_idx(m); - m_ = m; - while (local_idx == -1) { -#ifndef _WIN32 - sched_yield(); -#endif - local_idx = atomic_acqurie_local_idx(m); - } - } - ~AcquireIdxScope() { atomic_release_local_idx(m_, local_idx); } - operator int() { return local_idx; } -}; -void parallel_traverse_heap_segment( - mstate m, size_t segs_in_thread, ByteThreadPool* workerThreadPool, - std::function func); - -#endif // SRC_GC_SWEEPER_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/thread_pool.h b/test-app/runtime/src/main/cpp/napi/primjs/include/gc/thread_pool.h deleted file mode 100644 index b6b12c3b..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/thread_pool.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) [2020] Huawei Technologies Co.,Ltd.All rights reserved. - * - * OpenArkCompiler is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan - * PSL v2. You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY - * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the - * Mulan PSL v2 for more details. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_GC_THREAD_POOL_H_ -#define SRC_GC_THREAD_POOL_H_ - -#ifndef _WIN32 -#include -#endif - -#include -#include -#include -#include -#include - -class ByteTask { - public: - ByteTask() = default; - virtual ~ByteTask() = default; - virtual void Execute(size_t threadId) = 0; -}; - -class ByteLambdaTask : public ByteTask { - public: - explicit ByteLambdaTask(const std::function &function) - : func(function) {} - ~ByteLambdaTask() = default; - void Execute(size_t threadId) override { func(threadId); } - - private: - std::function func; -}; - -class ByteThreadPool; - -class BytePoolThread { - public: - // use for profiling - std::vector *schedCores; - - BytePoolThread(ByteThreadPool *threadPool, const char *threadName, - size_t threadId, size_t stackSize); - ~BytePoolThread(); - - void SetPriority(int32_t prior); - -#ifndef _WIN32 - // get pthread of thread - pthread_t GetThread() const { return pthread; } - - // get thread id of thread - pid_t GetTid() const { return tid; } -#endif - - static void *WorkerFunc(void *param); - - private: - size_t id; -#ifndef _WIN32 - pthread_t pthread; - pid_t tid; -#endif - std::string name; - ByteThreadPool *pool; -}; - -// manual -// new (SetMaxActiveThreadNum(optional) addTask startPool waitFinish)^. Exit -// delete if need to change MaxActiveThreadNum, should waitFinish or stop pool -// at first -class ByteThreadPool { - public: - // Constructor for thread pool, 1) Create threads, 2) wait all thread created - // & sleep name is the thread pool name. thread name = - // Pool_$(poolname)_ThreadId_$(threadId) maxThreadNum is the max thread number - // in pool. prior is the priority of threads in pool. - ByteThreadPool(const char *name, int32_t maxThreadNum, int32_t prior); - - // Destructor for thread pool, 1) close pool 2) wait thread in pool to exit, - // 3) release resources of class - ~ByteThreadPool(); - - // Set priority of each thread in pool. - void SetPriority(int32_t prior); - - // Set max active thread number of pool, redundant thread hangup in sleep - // condition var. notify more waitting thread get to work when pool is - // running. Range [1 - maxThreadNum]. - void SetMaxActiveThreadNum(int32_t num); - - // Get max active thread number of pool. - int32_t GetMaxActiveThreadNum() const { return maxActiveThreadNum; } - - // Get max thread number of pool, defalut = maxThreadNum. - int32_t GetMaxThreadNum() const { return maxThreadNum; } - - // Add new task to task queue , task should inherit from ByteTask. - void AddTask(ByteTask *task); - - // Add task to thread , func indicate Lambda statement. - void AddTask(std::function func); - - // Start thread pool, notify all sleep threads to get to work - void Start(); - - // Wait all task in task queue finished, if pool stopped, only wait until - // current excuting task finish after all task finished, stop pool - // addToExecute indicate whether the caller thread excute task - void WaitFinish(bool addToExecute, - std::vector *schedCores = nullptr); - - // used in none-parallel concurrent mark - void DrainTaskQueue(); - - // Notify & Wait all thread waitting for task to sleep - void Stop(); - - // Notify all thread in pool to exit , notify all waitFinish thread to - // return,nonblock ^. - void Exit(); - - // Remove all task in task queue - void ClearAllTask(); - - // Get task count in queue - size_t GetTaskNumber() { - std::unique_lock taskLock(taskMutex); - return taskQueue.size(); - } - - // Get all BytePoolThread in pool - const std::vector &GetThreads() const { return threads; } - - private: - // thread default stack size 512 KB. - static const size_t kDefaultStackSize = (512 * 1024); - // int32_t priority; - - std::string name; - // pool stop or running state - std::atomic running; - // is pool exit - std::atomic exit; - // all task put in task queue - std::queue taskQueue; - - // active thread 0 ..... maxActiveThreadNum .....maxThreadNum - // max thread number in pool - int32_t maxThreadNum; - - // max active thread number, redundant thread hang up in threadSleepingCondVar - int32_t maxActiveThreadNum; - - // current active thread, when equals to zero, no thread running, all thread - // slept - std::atomic currActiveThreadNum; - - // current waitting thread, when equals to currActiveThreadNum - // no thread excuting, all task finished - std::atomic currWaittingThreadNum; - - // single lock - std::mutex taskMutex; - - // hangup when no task available - std::condition_variable taskEmptyCondVar; - - // hangup when to much active thread or pool stopped - std::condition_variable threadSleepingCondVar; - - // hangup when there is thread excuting - std::condition_variable allWorkDoneCondVar; - - // hangup when there is thread active - std::condition_variable allThreadStopped; - - // use for profiling - std::vector threads; - - // is pool running or stopped - bool IsRunning() const { return running.load(std::memory_order_relaxed); } - - bool IsExited() const { return exit.load(std::memory_order_relaxed); } - - friend class BytePoolThread; -}; - -#endif // SRC_GC_THREAD_POOL_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/global-handles.h b/test-app/runtime/src/main/cpp/napi/primjs/include/global-handles.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/gc/global-handles.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/global-handles.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libregexp.h b/test-app/runtime/src/main/cpp/napi/primjs/include/libregexp.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libregexp.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/libregexp.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libunicode.h b/test-app/runtime/src/main/cpp/napi/primjs/include/libunicode.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libunicode.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/libunicode.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/list.h b/test-app/runtime/src/main/cpp/napi/primjs/include/list.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/list.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/list.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/persistent-handle.h b/test-app/runtime/src/main/cpp/napi/primjs/include/persistent-handle.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/gc/persistent-handle.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/persistent-handle.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-libc.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs-libc.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-libc.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/quickjs-libc.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-tag.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs-tag.h similarity index 100% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-tag.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/quickjs-tag.h diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs.h similarity index 97% rename from test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/quickjs.h index 01f961f7..a728b35f 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs.h +++ b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs.h @@ -36,6 +36,14 @@ #include "base_export.h" #include "list.h" + +// libquick.so exports these symbols with C linkage. Without this guard, C++ +// translation units (jsr.cpp, js_native_api_adapter.cc, ...) would mangle the +// LEPUS_* references and fail to link. Upstream QuickJS ships this guard; the +// vendored Lynx copy dropped it. +#ifdef __cplusplus +extern "C" { +#endif // #define DUMP_LEAKS 0 // for Debug // @@ -664,6 +672,9 @@ typedef struct LEPUSDebuggerCallbacks { uint8_t (*is_devtool_on)(LEPUSContext *ctx); } LEPUSDebuggerCallbacks; +typedef void LEPUS_MarkFunc(LEPUSRuntime *rt, LEPUSValueConst val, + uint64_t trace_tool); + typedef struct LEPUSLepusRefCallbacks { LEPUSValue (*free_value)(LEPUSRuntime *rt, LEPUSValue val); LEPUSValue (*get_property)(LEPUSContext *ctx, LEPUSValue thisObj, JSAtom prop, @@ -675,6 +686,9 @@ typedef struct LEPUSLepusRefCallbacks { void (*free_str_cache)(void *old_ptr, void *new_ptr); size_t (*lepus_ref_equal)(LEPUSValue val1, LEPUSValue val2); LEPUSValue (*lepus_ref_tostring)(LEPUSContext *ctx, LEPUSValue val); + void (*ref_counted_obj_visitor)(LEPUSRuntime *rt, void *ref_counted_obj, + uint64_t trace_tool, int tag, + LEPUS_MarkFunc *f); // void (*free_string_cache)(); } LEPUSLepusRefCallbacks; @@ -688,7 +702,8 @@ typedef struct LEPUSLepusRef { void RegisterLepusType(LEPUSRuntime *rt, int32_t array_typeid, int32_t table_typeid); -void RegisterGCInfoCallback(LEPUSRuntime *rt, void (*func)(const char *, int)); +void RegisterGCInfoCallback(LEPUSRuntime *rt, + void (*func)(LEPUSContext *, const char *, int)); void RegisterLepusRefCallbacks(LEPUSRuntime *rt, LEPUSLepusRefCallbacks *funcs); @@ -714,10 +729,8 @@ void LEPUS_SetGCThreshold(LEPUSRuntime *rt, size_t gc_threshold); LEPUSRuntime *LEPUS_NewRuntime2(const struct LEPUSMallocFunctions *mf, void *opaque, uint32_t mode); void LEPUS_FreeRuntime(LEPUSRuntime *rt); -typedef void LEPUS_MarkFunc(LEPUSRuntime *rt, LEPUSValueConst val, - int local_idx); void LEPUS_MarkValue(LEPUSRuntime *rt, LEPUSValueConst val, - LEPUS_MarkFunc *mark_func, int local_idx); + LEPUS_MarkFunc *mark_func, uint64_t trace_tool); void LEPUS_RunGC(LEPUSRuntime *rt); LEPUS_BOOL LEPUS_IsInGCSweep(LEPUSRuntime *rt); @@ -854,7 +867,7 @@ typedef struct LEPUSClassExoticMethods { typedef void LEPUSClassFinalizer(LEPUSRuntime *rt, LEPUSValue val); typedef void LEPUSClassGCMark(LEPUSRuntime *rt, LEPUSValueConst val, - LEPUS_MarkFunc *mark_func, int local_idx); + LEPUS_MarkFunc *mark_func, uint64_t trace_tool); #define LEPUS_CALL_FLAG_CONSTRUCTOR (1 << 0) typedef LEPUSValue LEPUSClassCall(LEPUSContext *ctx, LEPUSValueConst func_obj, LEPUSValueConst this_val, int argc, @@ -1004,10 +1017,14 @@ char *LEPUS_GetGCTimingInfo(LEPUSContext *ctx, bool is_start); void LEPUS_PushHandle(LEPUSContext *ctx, void *ptr, int type); void LEPUS_ResetHandle(LEPUSContext *ctx, void *ptr, int type); +void CheckObjectCtx(LEPUSContext *ctx, LEPUSValue obj); +void CheckObjectRt(LEPUSRuntime *rt, LEPUSValue obj); + static inline LEPUSValue LEPUS_DupValue(LEPUSContext *ctx, LEPUSValueConst v) { if (LEPUS_VALUE_HAS_REF_COUNT(v)) { LEPUSRefCountHeader *p = (LEPUSRefCountHeader *)LEPUS_VALUE_GET_PTR(v); p->ref_count++; + CheckObjectCtx(ctx, v); } return (LEPUSValue)v; } @@ -1028,6 +1045,7 @@ static inline LEPUSValue LEPUS_DupValueRT(LEPUSRuntime *rt, LEPUSValueConst v) { if (LEPUS_VALUE_HAS_REF_COUNT(v)) { LEPUSRefCountHeader *p = (LEPUSRefCountHeader *)LEPUS_VALUE_GET_PTR(v); p->ref_count++; + CheckObjectRt(rt, v); } return (LEPUSValue)v; } @@ -1137,6 +1155,9 @@ LEPUSValue LEPUS_CallConstructor2(LEPUSContext *ctx, LEPUSValueConst func_obj, LEPUSValueConst *argv); LEPUSValue LEPUS_Eval(LEPUSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags); +LEPUSValue LEPUS_Eval2(LEPUSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags, + int start_line_number); #define LEPUS_EVAL_BINARY_LOAD_ONLY (1 << 0) /* only load the module */ LEPUSValue LEPUS_EvalBinary(LEPUSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); @@ -1398,8 +1419,8 @@ static inline LEPUSValue LEPUS_NewCFunctionMagic(LEPUSContext *ctx, const char *name, int length, LEPUSCFunctionEnum cproto, int magic) { - return LEPUS_NewCFunction2(ctx, (LEPUSCFunction *)func, name, length, cproto, - magic); + LEPUSCFunctionType ft = {.generic_magic = func}; + return LEPUS_NewCFunction2(ctx, ft.generic, name, length, cproto, magic); } /* C property definition */ @@ -1603,9 +1624,21 @@ const char *LEPUS_GetStringUtf8(LEPUSContext *, const struct JSString *); void LEPUS_SetFuncFileName(LEPUSContext *, LEPUSValue, const char *); void InitLynxTraceEnv(void *(*)(const char *), void (*)(void *)); + +void SetObjectCtxCheckStatus(LEPUSContext *ctx, bool enable); +bool LEPUS_PushObjectCheckTid(LEPUSContext *ctx); + +void UpdateOuterObjSize(LEPUSRuntime *rt, int size); + +void LEPUS_SetGCObserver(LEPUSRuntime *rt, void *opaque); +void *LEPUS_GetGCObserver(LEPUSRuntime *rt); // #undef lepus_unlikely #undef lepus_force_inline +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/BUILD.gn b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/BUILD.gn deleted file mode 100644 index 0c3cb42c..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/BUILD.gn +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2024 The Lynx Authors. All rights reserved. -# Licensed under the Apache License Version 2.0 that can be found in the -# LICENSE file in the root directory of this source tree. -import("//Primjs.gni") -primjs_source_set("quickjs") { - sources = [ - "source/cutils.cc", - "source/libbf.cc", - "source/libregexp.cc", - "source/libunicode.cc", - "source/primjs_monitor.cc", - "source/quickjs-libc.cc", - "source/quickjs.cc", - "source/quickjs_gc.cc", - "source/quickjs_queue.cc", - "source/quickjs_version.cc", - ] - if (use_bignum) { - sources += [ "source/libbf.cc" ] - } -} diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libbf.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libbf.h deleted file mode 100644 index d1e4256e..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libbf.h +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Tiny arbitrary precision floating point library - * - * 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. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIBBF_H_ -#define SRC_INTERPRETER_QUICKJS_INCLUDE_LIBBF_H_ - -#include -#include - -#if defined(__x86_64__) -#define LIMB_LOG2_BITS 6 -#else -#define LIMB_LOG2_BITS 5 -#endif - -#define LIMB_BITS (1 << LIMB_LOG2_BITS) - -#if LIMB_BITS == 64 -typedef __int128 int128_t; -typedef unsigned __int128 uint128_t; -typedef int64_t slimb_t; -typedef uint64_t limb_t; -typedef uint128_t dlimb_t; -#define EXP_MIN INT64_MIN -#define EXP_MAX INT64_MAX - -#else - -typedef int32_t slimb_t; -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -#define EXP_MIN INT32_MIN -#define EXP_MAX INT32_MAX - -#endif - -/* in bits */ -#define BF_EXP_BITS_MIN 3 -#define BF_EXP_BITS_MAX (LIMB_BITS - 2) -#define BF_PREC_MIN 2 -#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 2) -#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ - -#if LIMB_BITS == 64 -#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) -#else -#define BF_CHKSUM_MOD 975620677U -#endif - -#define BF_EXP_ZERO EXP_MIN -#define BF_EXP_INF (EXP_MAX - 1) -#define BF_EXP_NAN EXP_MAX - -/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, - +/-infinity is represented with expn = BF_EXP_INF and len = 0, - NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) - */ -typedef struct { - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bf_t; - -typedef enum { - BF_RNDN, /* round to nearest, ties to even */ - BF_RNDZ, /* round to zero */ - BF_RNDD, /* round to -inf */ - BF_RNDU, /* round to +inf */ - BF_RNDNA, /* round to nearest, ties away from zero */ - BF_RNDNU, /* round to nearest, ties to +inf */ - BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, - inexact flag is always set) */ -} bf_rnd_t; - -/* allow subnormal numbers (only available if the number of exponent - bits is < BF_EXP_BITS_MAX and prec != BF_PREC_INF) */ -#define BF_FLAG_SUBNORMAL (1 << 3) - -#define BF_RND_MASK 0x7 -#define BF_EXP_BITS_SHIFT 4 -#define BF_EXP_BITS_MASK 0x3f - -/* contains the rounding mode and number of exponents bits */ -typedef uint32_t bf_flags_t; - -typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); - -typedef struct { - bf_t val; - limb_t prec; -} BFConstCache; - -typedef struct bf_context_t { - void *realloc_opaque; - bf_realloc_func_t *realloc_func; - BFConstCache log2_cache; - BFConstCache pi_cache; - struct BFNTTState *ntt_state; -} bf_context_t; - -static inline int bf_get_exp_bits(bf_flags_t flags) { - return BF_EXP_BITS_MAX - ((flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK); -} - -static inline bf_flags_t bf_set_exp_bits(int n) { - return (BF_EXP_BITS_MAX - n) << BF_EXP_BITS_SHIFT; -} - -/* returned status */ -#define BF_ST_INVALID_OP (1 << 0) -#define BF_ST_DIVIDE_ZERO (1 << 1) -#define BF_ST_OVERFLOW (1 << 2) -#define BF_ST_UNDERFLOW (1 << 3) -#define BF_ST_INEXACT (1 << 4) -/* not used yet, indicate that a memory allocation error occured. NaN - is returned */ -#define BF_ST_MEM_ERROR (1 << 5) - -#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ - -static inline slimb_t bf_max(slimb_t a, slimb_t b) { - if (a > b) - return a; - else - return b; -} - -static inline slimb_t bf_min(slimb_t a, slimb_t b) { - if (a < b) - return a; - else - return b; -} - -void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, - void *realloc_opaque); -void bf_context_end(bf_context_t *s); -/* free memory allocated for the bf cache data */ -void bf_clear_cache(bf_context_t *s); - -static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) { - return realloc(ptr, size); -} - -void bf_init(bf_context_t *s, bf_t *r); - -static inline void bf_delete(bf_t *r) { - bf_context_t *s = r->ctx; - /* we accept to delete a zeroed bf_t structure */ - if (s) { - bf_realloc(s, r->tab, 0); - } -} - -static inline void bf_neg(bf_t *r) { r->sign ^= 1; } - -static inline int bf_is_finite(const bf_t *a) { return (a->expn < BF_EXP_INF); } - -static inline int bf_is_nan(const bf_t *a) { return (a->expn == BF_EXP_NAN); } - -static inline int bf_is_zero(const bf_t *a) { return (a->expn == BF_EXP_ZERO); } - -void bf_set_ui(bf_t *r, uint64_t a); -void bf_set_si(bf_t *r, int64_t a); -void bf_set_nan(bf_t *r); -void bf_set_zero(bf_t *r, int is_neg); -void bf_set_inf(bf_t *r, int is_neg); -void bf_set(bf_t *r, const bf_t *a); -void bf_move(bf_t *r, bf_t *a); -int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); -void bf_set_float64(bf_t *a, double d); - -int bf_cmpu(const bf_t *a, const bf_t *b); -int bf_cmp_full(const bf_t *a, const bf_t *b); -int bf_cmp_eq(const bf_t *a, const bf_t *b); -int bf_cmp_le(const bf_t *a, const bf_t *b); -int bf_cmp_lt(const bf_t *a, const bf_t *b); -int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, - bf_flags_t flags); -int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); -int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -#define BF_DIVREM_EUCLIDIAN BF_RNDF -int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bf_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, bf_flags_t flags); -int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, limb_t prec, bf_flags_t flags); -int bf_rint(bf_t *r, limb_t prec, bf_flags_t flags); -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); -int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); -int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -slimb_t bf_get_exp_min(const bf_t *a); -void bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); -void bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); -void bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); - -/* additional flags for bf_atof */ -/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ -#define BF_ATOF_NO_HEX (1 << 16) -/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ -#define BF_ATOF_BIN_OCT (1 << 17) -/* Only accept integers (no decimal point, no exponent, no infinity nor NaN */ -#define BF_ATOF_INT_ONLY (1 << 18) -/* Do not accept radix prefix after sign */ -#define BF_ATOF_NO_PREFIX_AFTER_SIGN (1 << 19) -/* Do not parse NaN and parse case sensitive 'Infinity' */ -#define BF_ATOF_JS_QUIRKS (1 << 20) -/* Do not round integers to the indicated precision */ -#define BF_ATOF_INT_PREC_INF (1 << 21) -/* Support legacy octal syntax for well formed numbers */ -#define BF_ATOF_LEGACY_OCTAL (1 << 22) -/* accept _ between digits as a digit separator */ -#define BF_ATOF_UNDERSCORE_SEP (1 << 23) -/* if a 'n' suffix is present, force integer parsing (XXX: remove) */ -#define BF_ATOF_INT_N_SUFFIX (1 << 24) -/* if set return NaN if empty number string (instead of 0) */ -#define BF_ATOF_NAN_IF_EMPTY (1 << 25) -/* only accept decimal floating point if radix = 0 */ -#define BF_ATOF_ONLY_DEC_FLOAT (1 << 26) - -/* additional return flags */ -/* indicate that the parsed number is an integer (only set when the - flags BF_ATOF_INT_PREC_INF or BF_ATOF_INT_N_SUFFIX are used) */ -#define BF_ATOF_ST_INTEGER (1 << 5) -/* integer parsed as legacy octal */ -#define BF_ATOF_ST_LEGACY_OCTAL (1 << 6) - -int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -/* this version accepts prec = BF_PREC_INF and returns the radix - exponent */ -int bf_atof2(bf_t *r, slimb_t *pexponent, const char *str, const char **pnext, - int radix, limb_t prec, bf_flags_t flags); -int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, slimb_t expn, - limb_t prec, bf_flags_t flags); - -#define BF_FTOA_FORMAT_MASK (3 << 16) -/* fixed format: prec significant digits rounded with (flags & - BF_RND_MASK). Exponential notation is used if too many zeros are - needed. */ -#define BF_FTOA_FORMAT_FIXED (0 << 16) -/* fractional format: prec digits after the decimal point rounded with - (flags & BF_RND_MASK) */ -#define BF_FTOA_FORMAT_FRAC (1 << 16) -/* free format: use as many digits as necessary so that bf_atof() - return the same number when using precision 'prec', rounding to - nearest and the subnormal+exponent configuration of 'flags'. The - result is meaningful only if 'a' is already rounded to the wanted - precision. - - Infinite precision (BF_PREC_INF) is supported when the radix is a - power of two. */ -#define BF_FTOA_FORMAT_FREE (2 << 16) -/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits - (takes more computation time). */ -#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) - -/* force exponential notation for fixed or free format */ -#define BF_FTOA_FORCE_EXP (1 << 20) -/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for - base 2 if non zero value */ -#define BF_FTOA_ADD_PREFIX (1 << 21) -#define BF_FTOA_JS_QUIRKS (1 << 22) - -size_t bf_ftoa(char **pbuf, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags); - -/* modulo 2^n instead of saturation. NaN and infinity return 0 */ -#define BF_GET_INT_MOD (1 << 0) -int bf_get_int32(int *pres, const bf_t *a, int flags); -int bf_get_int64(int64_t *pres, const bf_t *a, int flags); - -/* the following functions are exported for testing only. */ -void bf_print_str(const char *str, const bf_t *a); -void bf_resize(bf_t *r, limb_t len); -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); -void bf_recip(bf_t *r, const bf_t *a, limb_t prec); -void bf_rsqrt(bf_t *a, const bf_t *x, limb_t prec); -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); -slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, - int is_ceil1); - -/* transcendental functions */ -int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -#define BF_POW_JS_QUICKS (1 << 16) -int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, - bf_flags_t flags); -int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, limb_t prec, - bf_flags_t flags); -int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); - -#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIBBF_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libregexp-opcode.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libregexp-opcode.h deleted file mode 100644 index 480c23df..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libregexp-opcode.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -#ifdef DEF - -DEF(invalid, 1) /* never used */ -DEF(char, 3) -DEF(char32, 5) -DEF(dot, 1) -DEF(any, 1) /* same as dot but match any character including line terminator */ -DEF(line_start, 1) -DEF(line_end, 1) -DEF(goto, 5) -DEF(split_goto_first, 5) -DEF(split_next_first, 5) -DEF(match, 1) -DEF(save_start, 2) /* save start position */ -DEF(save_end, 2) /* save end position, must come after saved_start */ -DEF(save_reset, 3) /* reset save positions */ -DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ -DEF(push_i32, 5) /* push integer on the stack */ -DEF(drop, 1) -DEF(word_boundary, 1) -DEF(not_word_boundary, 1) -DEF(back_reference, 2) -DEF(backward_back_reference, 2) /* must come after back_reference */ -DEF(range, 3) /* variable length */ -DEF(range32, 3) /* variable length */ -DEF(lookahead, 5) -DEF(negative_lookahead, 5) -DEF(push_char_pos, 1) /* push the character position on the stack */ -DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character - position */ -DEF(prev, 1) /* go to the previous char */ -DEF(simple_greedy_quant, 17) - -#endif /* DEF */ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libunicode-table.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libunicode-table.h deleted file mode 100644 index 9b4a5b4b..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/libunicode-table.h +++ /dev/null @@ -1,3495 +0,0 @@ -/* - * 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. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -/* Compressed unicode tables */ -/* Automatically generated file - do not edit */ - -#include - -static const uint32_t case_conv_table1[359] = { - 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, 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, - 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, - 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, 0x0fbb0200, - 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, 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, - 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201, 0x0ff101d3, 0x0ff181d3, - 0x0ff201ba, 0x0ff28101, 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230, - 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, 0x0ffb01b2, 0x0ffb81d9, - 0x0ffc0230, 0x0ffd0230, 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0, - 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001, 0x10c18240, 0x125b1a31, - 0x12681a01, 0x16002f31, 0x16182f01, 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, 0x53da0c40, 0x53e10240, 0x53e20131, 0x53e28130, - 0x53e30130, 0x55a98101, 0x55b85020, 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, - 0x7d8181da, 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb, 0x7d8a01bb, - 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb, 0x7f909a31, 0x7fa09a01, 0x82002831, - 0x82142801, 0x82582431, 0x826c2401, 0x86403331, 0x86603301, 0x8c502031, - 0x8c602001, 0xb7202031, 0xb7302001, 0xf4802231, 0xf4912201, -}; - -static const uint8_t case_conv_table2[359] = { - 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, 0x10, 0x00, 0x8f, 0x0b, - 0x00, 0x00, 0x11, 0x00, 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00, - 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59, 0x3f, 0x5d, 0x5c, 0x00, - 0x46, 0x61, 0x63, 0x42, 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, - 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, - 0x93, 0x00, 0x00, 0x20, 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22, - 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24, 0x27, 0x14, 0x16, 0x18, - 0x1b, 0x1c, 0x3e, 0x1e, 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e, - 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x49, 0x2c, 0x43, 0x2e, 0x4b, - 0x30, 0x4c, 0x32, 0x44, 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e, - 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12, 0x7b, 0xa3, 0x7c, 0x78, - 0x79, 0x8a, 0x92, 0x98, 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75, - 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98, 0x97, 0x96, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5, - 0x4c, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22, 0x22, 0x2a, 0x34, 0x35, - 0xa6, 0xa7, 0x36, 0x1f, 0x4a, 0x00, 0x00, 0x97, 0x01, 0x5a, 0xda, 0x1d, - 0x36, 0x05, 0x00, 0xc4, 0xc3, 0xc6, 0xc5, 0xc8, 0xc7, 0xca, 0xc9, 0xcc, - 0xcb, 0xc4, 0xd5, 0x45, 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2, - 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07, 0x0f, 0x80, 0x9f, 0x00, - 0x21, 0x80, 0xa3, 0xed, 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6, - 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29, 0xfd, 0x15, 0x12, 0x06, - 0x16, 0xf8, 0xdd, 0x06, 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf, - 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0, 0x6d, 0x37, 0x38, 0x39, - 0x15, 0x14, 0x17, 0x16, 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7, - 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50, 0x00, 0x00, 0x48, 0x00, - 0x00, 0x00, 0xa3, 0xa4, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, - 0x00, 0x5a, 0x00, 0x48, 0x00, 0x5b, 0x56, 0x58, 0x60, 0x5e, 0x70, 0x69, - 0x6f, 0x4b, 0x00, 0x00, 0x3b, 0x67, 0xb8, 0x45, 0xa8, 0x8a, 0x8b, 0x8c, - 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, 0x6f, 0xb2, 0x5a, 0x59, 0x5c, - 0x5b, 0x5e, 0x5d, 0x60, 0x5f, 0x62, 0x61, 0x64, 0x63, 0x66, 0x65, -}; - -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[172] = { - 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, 0x80, 0x8c, 0x80, 0x8d, - 0x81, 0x8d, 0x02, 0x80, 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, - 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30, 0x08, 0x01, 0x15, 0x20, - 0x00, 0x39, 0x99, 0x31, 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6, - 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x57, 0x76, 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, - 0xc7, 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8b, 0x61, 0x28, 0x97, 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, 0x59, 0x63, 0x99, - 0x85, 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Cased1_index[18] = { - 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, 0x40, - 0xba, 0xd4, 0x01, 0x89, 0xd7, 0x01, 0x8a, 0xf1, 0x01, -}; - -static const uint8_t unicode_prop_Case_Ignorable_table[678] = { - 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, 0x80, 0xb6, 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, 0xf6, 0xaf, 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, 0x07, 0x80, 0x8a, 0x81, 0x9d, - 0x80, 0xbc, 0x80, 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb8, 0x14, 0x10, 0x1e, - 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, - 0x9b, 0x81, 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0xe5, 0x06, 0x10, 0x80, - 0xd9, 0x01, 0x86, 0x8a, 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x85, 0xc9, - 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, 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, - 0x01, 0x8a, 0x10, 0x20, 0xac, 0x83, 0xb3, 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, 0x8e, 0xc0, 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, 0xde, 0x04, - 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, 0xec, 0x0f, 0x02, 0x03, 0x80, 0x98, 0x81, - 0x40, 0x9c, 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, 0x40, 0x84, - 0x01, 0x03, 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x8f, 0x42, 0x3d, - 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, 0x46, 0x85, - 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, - 0x42, 0x1d, 0x8a, 0x40, 0xaf, 0x80, 0xb5, 0x8e, 0xb7, 0x82, 0xb0, 0x19, - 0x09, 0x80, 0x8e, 0x80, 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b, - 0x81, 0xb3, 0x88, 0x89, 0x83, 0xe1, 0x11, 0x00, 0x0d, 0x80, 0x40, 0x9f, - 0x02, 0x87, 0x94, 0x81, 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0x40, 0xc2, - 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, 0x11, 0x18, 0x84, 0x41, 0x02, 0x88, 0x01, 0x41, 0x98, - 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7, 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, - 0xad, 0x8c, 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, 0x01, - 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, - 0x5a, 0x81, 0x55, 0x3a, 0x88, 0x60, 0x36, 0xb6, 0x84, 0xba, 0x86, 0x88, - 0x83, 0x44, 0x0a, 0x80, 0xbe, 0x90, 0xbf, 0x08, 0x80, 0x60, 0x4c, 0xb8, - 0x08, 0x83, 0x54, 0xc2, 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, 0x41, 0x04, 0x8d, 0x41, 0xad, - 0x83, 0x45, 0xdf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, - 0x80, 0x9d, 0xdf, 0xff, 0x40, 0xef, -}; - -static const uint8_t unicode_prop_Case_Ignorable_index[66] = { - 0xc0, 0x05, 0x00, 0x2e, 0x08, 0x20, 0x52, 0x0a, 0x00, 0x05, 0x0c, - 0x00, 0x4f, 0x0e, 0x20, 0x75, 0x10, 0x20, 0x44, 0x18, 0x00, 0x43, - 0x1b, 0x00, 0x00, 0x1e, 0x00, 0x7e, 0x2c, 0x00, 0x7e, 0xa6, 0x40, - 0x83, 0xa9, 0x20, 0xf7, 0xaa, 0x00, 0x41, 0xff, 0x20, 0x28, 0x0d, - 0x01, 0x3f, 0x12, 0x41, 0xde, 0x15, 0x21, 0x5c, 0x1a, 0x01, 0xf5, - 0x6a, 0x21, 0x37, 0xda, 0x01, 0x02, 0x00, 0x2e, 0xf0, 0x01, 0x0e, -}; - -static const uint8_t unicode_prop_ID_Start_table[1024] = { - 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, 0xb4, 0x94, 0x07, 0xc5, - 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, 0x14, 0x81, 0x9d, 0x03, 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, - 0x9f, 0x00, 0x81, 0x8e, 0x81, 0x91, 0x38, 0x10, 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, 0x8c, 0x03, 0x8d, 0x91, 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, 0x86, 0xb6, 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x99, - 0xa3, 0xa8, 0x82, 0x89, 0xa3, 0x81, 0x88, 0x86, 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, 0xae, 0x80, 0xae, 0x80, 0x40, 0x84, - 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, 0x9a, 0xb4, 0x8f, 0x41, 0xff, 0x59, 0xb5, 0xc9, 0x60, 0x51, - 0xef, 0x8f, 0x44, 0x8c, 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, - 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, 0x88, 0x81, 0xe6, 0x81, - 0xb4, 0x0c, 0xaf, 0x8a, 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, 0x8b, 0x87, - 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, - 0x40, 0x9b, 0x41, 0x36, 0x88, 0x95, 0x89, 0x87, 0x40, 0x97, 0x29, 0x00, - 0xab, 0x01, 0x10, 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92, 0x01, - 0x89, 0x95, 0x89, 0x99, 0xc5, 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, - 0x41, 0xdb, 0x9c, 0x89, 0x07, 0x95, 0x40, 0x99, 0x96, 0x8b, 0xb4, 0xca, - 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, 0x80, 0x8a, 0xa2, 0x10, 0x8b, 0xaf, - 0x8d, 0x83, 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0xd3, 0x30, 0x00, - 0x18, 0x8e, 0x80, 0x89, 0x86, 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, - 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x40, 0x9d, 0xb4, 0x91, 0x83, 0x93, - 0x80, 0x9f, 0xaf, 0x93, 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, - 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, 0x9a, 0x40, 0xe4, 0xab, - 0xf3, 0xbf, 0x9e, 0x80, 0x40, 0x9f, 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, - 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, 0x92, 0x80, 0xa1, 0xb8, - 0x41, 0x06, 0x88, 0x80, 0xa4, 0x90, 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, - 0xa5, 0x94, 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, 0x80, 0x41, 0x46, 0x92, - 0x41, 0x0c, 0x43, 0x99, 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0xbb, 0x44, - 0x2e, 0x4f, 0xd0, 0x42, 0x46, 0x60, 0x21, 0xb8, 0x42, 0x38, 0x86, 0x9e, - 0xf0, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, 0x84, 0x92, 0x42, 0xaf, - 0xbf, 0xff, 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, 0x57, 0xf7, - 0x87, 0x42, 0xf2, 0x60, 0x25, 0x0c, 0x41, 0x1e, 0xb0, 0x82, 0x90, 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, 0x49, - 0x33, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x70, 0xab, 0x45, 0x13, 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, 0xd6, - 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, - 0x4c, 0x1e, 0x42, 0x1d, -}; - -static const uint8_t unicode_prop_ID_Start_index[96] = { - 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xb1, 0x09, 0x00, 0xba, 0x0a, 0x00, - 0xd1, 0x0b, 0x20, 0x62, 0x0d, 0x40, 0x01, 0x0f, 0x20, 0x5e, 0x12, 0x00, - 0xf9, 0x16, 0x00, 0x17, 0x1a, 0x20, 0xc0, 0x1d, 0x20, 0x9d, 0x20, 0x00, - 0x68, 0x2d, 0x00, 0x00, 0x32, 0x20, 0xc0, 0xa7, 0x20, 0x29, 0xaa, 0x00, - 0xa4, 0xd7, 0x20, 0xc8, 0xfd, 0x20, 0x75, 0x01, 0x01, 0x37, 0x07, 0x01, - 0x36, 0x0a, 0x21, 0xf7, 0x0f, 0x21, 0xa9, 0x12, 0x01, 0x30, 0x16, 0x21, - 0x8a, 0x1a, 0x01, 0x9a, 0x23, 0x01, 0x80, 0x6e, 0x21, 0x89, 0xbc, 0x21, - 0xc1, 0xd6, 0x01, 0xc5, 0xe8, 0x21, 0x73, 0xee, 0x01, 0x1e, 0xfa, 0x02, -}; - -static const uint8_t unicode_prop_ID_Continue1_table[607] = { - 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, 0xf6, 0x8e, 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, 0x17, 0x81, 0x89, 0x09, 0x89, - 0x91, 0x80, 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, 0x8f, 0x84, - 0xb8, 0x30, 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, - 0x30, 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x8f, 0x83, 0xb6, 0x08, 0x30, - 0x10, 0x83, 0x88, 0x80, 0x89, 0x09, 0x89, 0x91, 0x81, 0xc5, 0x03, 0x28, - 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, 0x38, 0x89, 0xd6, 0x01, - 0x88, 0x8a, 0x29, 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, 0x82, 0x9c, 0x82, 0x9c, 0x81, 0x9d, - 0x81, 0xbf, 0x9f, 0x88, 0x01, 0x89, 0xa0, 0x11, 0x89, 0x40, 0x8e, 0x80, - 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, 0xbb, 0x84, 0xb8, 0x89, - 0x80, 0x9c, 0x81, 0x8a, 0x85, 0x89, 0x95, 0x8d, 0xc1, 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, 0xb9, 0x04, 0x42, 0x3e, 0x81, 0x92, 0x80, 0xfa, 0x8c, 0x18, - 0x82, 0x8b, 0x4b, 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, 0x9f, 0x42, 0x29, - 0x85, 0xe8, 0x81, 0x60, 0x75, 0x84, 0x89, 0xc4, 0x03, 0x89, 0x9f, 0x81, - 0xcf, 0x81, 0x41, 0x0f, 0x02, 0x03, 0x80, 0x96, 0x84, 0xd7, 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, - 0x42, 0xbc, 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, 0x42, 0x0b, 0x8a, 0x40, 0xae, 0x82, 0xb4, 0x8e, 0x9e, - 0x89, 0x8e, 0x83, 0xac, 0x8a, 0xb4, 0x89, 0x2a, 0xa3, 0x8d, 0x80, 0x89, - 0x21, 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x82, 0x89, 0xd1, 0x8b, - 0x28, 0x40, 0x9f, 0x8b, 0x84, 0x89, 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, - 0x88, 0x80, 0x89, 0x09, 0x32, 0x84, 0x40, 0xbf, 0x91, 0x88, 0x89, 0x18, - 0xd0, 0x93, 0x8b, 0x89, 0x40, 0xd4, 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, - 0x8e, 0x89, 0xd0, 0x8c, 0x87, 0x89, 0xd2, 0x8e, 0x83, 0x89, 0x40, 0xf1, - 0x8e, 0x40, 0xa4, 0x89, 0x40, 0xe6, 0x31, 0x32, 0x80, 0x9b, 0x89, 0xa7, - 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad, 0x8f, 0x41, 0x94, 0x38, 0x87, 0x8f, - 0x89, 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x07, 0x89, - 0xaf, 0x20, 0x08, 0x27, 0x89, 0x41, 0x48, 0x83, 0x60, 0x4b, 0x68, 0x89, - 0x40, 0x85, 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, 0xf4, 0x00, 0xb6, 0x33, - 0x60, 0x4d, 0x09, 0x81, 0x54, 0xc5, 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, - 0x41, 0x04, 0x86, 0x88, 0x89, 0x41, 0xa1, 0x8d, 0x45, 0xd5, 0x86, 0xec, - 0x34, 0x89, 0x6c, 0x17, 0xa5, 0x40, 0xef, -}; - -static const uint8_t unicode_prop_ID_Continue1_index[57] = { - 0xfa, 0x06, 0x00, 0x84, 0x09, 0x00, 0xf0, 0x0a, 0x00, 0x70, 0x0c, 0x00, - 0xf4, 0x0d, 0x00, 0x4a, 0x10, 0x20, 0x1a, 0x18, 0x20, 0x74, 0x1b, 0x00, - 0xe2, 0x20, 0x00, 0x28, 0xa8, 0x20, 0x7e, 0xaa, 0x20, 0x40, 0xff, 0x00, - 0x03, 0x10, 0x21, 0xeb, 0x12, 0x01, 0x41, 0x16, 0x01, 0x40, 0x1c, 0x61, - 0x37, 0x6b, 0x21, 0x76, 0xda, 0x01, 0xf0, 0x01, 0x0e, -}; - -#ifdef CONFIG_ALL_UNICODE - -static const uint8_t unicode_cc_table[831] = { - 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, 0x46, 0x00, 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, 0x4e, 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, 0x00, 0x09, 0x9e, 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, 0xb0, 0x45, 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, 0x80, 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, 0xb0, 0x8c, 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, 0xb1, 0xed, 0x01, 0xdc, - 0xc2, 0x00, 0xdc, 0xc0, 0x03, 0xdc, 0xb0, 0xc4, 0x00, 0x09, 0xb0, 0x07, - 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, 0x9c, 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, 0xb1, 0x74, 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, 0xb8, - 0x45, 0x27, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb8, 0x49, 0x36, 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, 0xd4, 0xc6, 0xb1, 0x84, 0xc3, 0xb5, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, - 0xc5, 0x00, 0x07, -}; - -static const uint8_t unicode_cc_index[78] = { - 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, 0x00, 0xe7, 0x06, 0x00, - 0x45, 0x07, 0x00, 0xe2, 0x08, 0x00, 0x53, 0x09, 0x00, 0xcd, 0x0b, 0x20, - 0x38, 0x0e, 0x00, 0x73, 0x0f, 0x20, 0x5d, 0x13, 0x20, 0x60, 0x1a, 0x20, - 0xe6, 0x1b, 0x20, 0xfa, 0x1c, 0x00, 0x00, 0x1e, 0x20, 0x80, 0x2d, 0x00, - 0x06, 0xa8, 0x00, 0xbe, 0xaa, 0x00, 0x76, 0x03, 0x01, 0x4d, 0x0f, 0x01, - 0xcb, 0x11, 0x21, 0x5e, 0x14, 0x01, 0x3b, 0x18, 0x21, 0xf0, 0x6a, 0x41, - 0xaa, 0xd1, 0x01, 0x4b, 0xe9, 0x01, -}; - -static const uint32_t unicode_decomp_table1[687] = { - 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, 0x29fe0103, 0x2ad70203, 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, 0x44268192, - 0x442ac092, 0x444b8112, 0x44d2c112, 0x452ec212, 0x456e8112, 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, 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, - 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910, 0xbe29872c, 0xbe2d022c, - 0xbe2e3790, 0xbe49ff90, 0xbe69bc10, -}; - -static const uint16_t unicode_decomp_table2[687] = { - 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, 0x11bb, 0x120d, 0x130b, 0x1409, 0x148d, 0x1492, - 0x1550, 0x1569, 0x156f, 0x1575, 0x157b, 0x1587, 0x1593, 0x002b, 0x159e, - 0x15b6, 0x15ba, 0x15be, 0x15c2, 0x15c6, 0x15ca, 0x15de, 0x15e2, 0x1646, - 0x165f, 0x1685, 0x168b, 0x1749, 0x174f, 0x1754, 0x1774, 0x1874, 0x187a, - 0x190e, 0x19d0, 0x1a74, 0x1a7c, 0x1a9a, 0x1a9f, 0x1ab3, 0x1abd, 0x1ac3, - 0x1ad7, 0x1adc, 0x1ae2, 0x1af0, 0x1b20, 0x1b2d, 0x1b35, 0x1b39, 0x1b4f, - 0x1bc6, 0x1bd8, 0x1bda, 0x1bdc, 0x3164, 0x1c1d, 0x1c1f, 0x1c21, 0x1c23, - 0x1c25, 0x1c27, 0x1c45, 0x1c53, 0x1c58, 0x1c61, 0x1c6a, 0x1c7c, 0x1c85, - 0x1ca5, 0x1cc0, 0x1cc2, 0x1cc4, 0x1cc6, 0x1cc8, 0x1cca, 0x1ccc, 0x1cce, - 0x1cee, 0x1cf0, 0x1cf2, 0x1cf4, 0x1cf6, 0x1cfd, 0x1cff, 0x1d01, 0x1d03, - 0x1d12, 0x1d14, 0x1d16, 0x1d18, 0x1d1a, 0x1d1c, 0x1d1e, 0x1d20, 0x1d22, - 0x1d24, 0x1d26, 0x1d28, 0x1d2a, 0x1d2c, 0x1d2e, 0x1d32, 0x03f4, 0x1d34, - 0x2207, 0x1d36, 0x2202, 0x1d38, 0x1d40, 0x03f4, 0x1d42, 0x2207, 0x1d44, - 0x2202, 0x1d46, 0x1d4e, 0x03f4, 0x1d50, 0x2207, 0x1d52, 0x2202, 0x1d54, - 0x1d5c, 0x03f4, 0x1d5e, 0x2207, 0x1d60, 0x2202, 0x1d62, 0x1d6a, 0x03f4, - 0x1d6c, 0x2207, 0x1d6e, 0x2202, 0x1d70, 0x1d7a, 0x1d7c, 0x1d7e, 0x1d80, - 0x1d82, 0x1d84, 0x1d8a, 0x1da7, 0x062d, 0x1daf, 0x1dbb, 0x062c, 0x1dcb, - 0x1e3b, 0x1e47, 0x1e5a, 0x1e6c, 0x1e7f, 0x1e81, 0x1e85, 0x1e8b, 0x1e91, - 0x1e93, 0x1e97, 0x1e99, 0x1ea1, 0x1ea4, 0x1ea6, 0x1eac, 0x1eae, 0x30b5, - 0x1eb4, 0x1f0c, 0x1f22, 0x1f26, 0x1f2b, 0x1f78, 0x1f89, 0x208a, 0x209a, - 0x20a0, 0x219a, 0x22b8, -}; - -static const uint8_t unicode_decomp_data[9158] = { - 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, 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, 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, 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, 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, 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, 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[944] = { - 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, 0x8e40, 0x8e42, 0x8e80, 0x8ec0, - 0x8ec1, 0x8f00, 0x8f01, 0x8f41, 0x8f40, 0x8f43, 0x8f80, 0x8f81, -}; - -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[3719] = { - 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, 0x05, - 0xe2, 0x13, 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, 0x00, 0x36, 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, 0xe0, 0x2d, 0xe5, 0x0d, - 0x00, 0xe5, 0x00, 0xe0, 0x0d, 0xe6, 0x07, 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, 0xe0, 0x00, 0x06, 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, 0x40, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, 0x66, 0xc0, 0x26, - 0x00, 0x45, 0x80, 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, 0xc0, 0x05, 0x00, 0x25, 0x26, 0x20, - 0xe9, 0x02, 0x00, 0x25, 0xe0, 0x05, 0x26, 0x27, 0x00, 0xe5, 0x00, 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, 0x20, 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, 0xa6, 0x20, 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, 0x05, 0x00, 0x65, 0x46, 0xe0, 0x03, 0xe5, 0x0a, 0x46, - 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, 0x00, 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, 0xe0, 0x39, 0x66, 0x07, - 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, 0x06, 0x27, 0xc5, 0x60, - 0xe9, 0x02, 0xd6, 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x40, 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, 0xc0, 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, 0x32, 0x00, - 0x86, 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, 0x18, 0xe0, 0x08, 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, 0x3d, 0xe0, 0x11, 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, 0x18, 0x20, 0xef, 0x60, - 0xe1, 0x27, 0x00, 0xe2, 0x27, 0x00, 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, - 0xe0, 0x28, 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, 0x80, 0x4e, - 0xe0, 0x12, 0xef, 0x04, 0x60, 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, - 0x13, 0x80, 0xef, 0x1c, 0xe0, 0x04, 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, 0x2e, 0xe0, 0x02, - 0xef, 0x38, 0xe5, 0xc0, 0x11, 0x68, 0xe0, 0x08, 0xe5, 0x0d, 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, 0x02, 0x20, 0x5f, 0x21, 0xe0, 0x28, 0x05, 0x24, 0x02, 0xc5, - 0x06, 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, 0x07, 0x6f, 0x60, - 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, 0x00, 0xe0, 0x00, 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, 0x08, 0xe0, 0x09, 0xe5, 0x80, 0xe3, 0x13, 0x12, 0xe0, 0x08, 0xe5, - 0x38, 0x20, 0xe5, 0x2e, 0xe0, 0x20, 0xe5, 0x04, 0x0d, 0x0f, 0x20, 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, 0x04, 0x60, 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, 0xe0, 0x80, 0x08, 0xe5, - 0x80, 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5, 0x00, 0xe0, 0x80, - 0x10, 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, 0xe0, 0x38, 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, 0xe0, 0x80, 0x9e, - 0xeb, 0x17, 0xe0, 0x79, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, 0xe5, - 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x7e, 0xe5, 0x0f, 0xe0, 0x01, 0x07, - 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, 0x60, 0xeb, 0x0c, 0xe9, 0x02, - 0xe0, 0x07, 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, 0x27, 0x26, 0x36, 0x1b, - 0x76, 0xe0, 0x03, 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, 0x46, - 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, 0xe9, 0x02, 0x76, 0x05, 0x27, - 0xe0, 0x01, 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, 0x07, 0xe5, - 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, 0x76, 0x66, 0x16, 0x20, 0xe9, 0x02, - 0x05, 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, 0xe5, 0x0a, 0x00, - 0xe5, 0x11, 0x47, 0x46, 0x27, 0x06, 0x07, 0x26, 0xb6, 0x06, 0xe0, 0x39, - 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, 0x80, 0x03, 0xe5, 0x2d, 0x47, 0xe6, 0x00, 0x27, 0x46, 0x07, 0x06, - 0x65, 0x96, 0xe9, 0x02, 0x00, 0x16, 0x00, 0x16, 0x06, 0x05, 0xe0, 0x18, - 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, 0xc0, 0xe9, 0x02, 0xe0, 0x2e, 0xe5, 0x13, - 0x20, 0x46, 0x27, 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, 0x0f, - 0xe0, 0x80, 0x38, 0xe5, 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, - 0x5c, 0xe1, 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, 0x01, 0xe0, 0x04, 0x05, - 0xe0, 0x80, 0x18, 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, - 0x15, 0xe5, 0x31, 0xe0, 0x80, 0x7f, 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, 0xe0, 0x80, 0xae, 0xe5, 0x0b, - 0x26, 0x27, 0x36, 0xe0, 0x80, 0x3f, 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, 0x8a, 0x34, 0xe5, 0x83, 0xa7, - 0x00, 0xfb, 0x01, 0xe0, 0x8f, 0x3f, 0xe5, 0x81, 0xbf, 0xe0, 0xa1, 0x31, - 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, 0x00, 0xe9, 0x02, 0x60, 0x36, 0xe0, - 0x58, 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, 0x82, 0x28, 0xe1, 0x18, 0xe2, 0x18, 0xeb, - 0x0f, 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, 0xe7, 0x2f, 0xc0, - 0x66, 0xe4, 0x05, 0xe0, 0x38, 0x24, 0x16, 0x04, 0xe0, 0x14, 0xe5, 0x97, - 0x70, 0xe0, 0x00, 0xe5, 0x82, 0x6b, 0xe0, 0xa4, 0x85, 0xe5, 0x80, 0x97, - 0xe0, 0x29, 0x45, 0xe0, 0x09, 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, 0x92, 0xd4, 0xef, 0x80, 0x6e, - 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, - 0x00, 0xe6, 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, 0xef, 0x33, 0xe0, 0x0f, - 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x80, 0x12, 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, 0x84, - 0xc8, 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, 0x00, 0x26, 0x00, 0x86, 0xe0, - 0x80, 0x4d, 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02, 0x60, 0x05, - 0x0f, 0xe0, 0x80, 0xe8, 0xe5, 0x24, 0x66, 0xe9, 0x02, 0x80, 0x0d, 0xe0, - 0x84, 0x78, 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, 0x40, 0xef, 0x55, 0x40, 0xef, - 0x35, 0xe0, 0x31, 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, 0xef, 0x01, - 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, 0x80, 0x12, 0xef, 0x80, 0x73, 0x8e, - 0xef, 0x82, 0x4e, 0xe0, 0x02, 0xef, 0x05, 0x40, 0xef, 0x03, 0x80, 0xef, - 0x6c, 0xe0, 0x04, 0xef, 0x51, 0xc0, 0xef, 0x04, 0xe0, 0x0c, 0xef, 0x04, - 0x60, 0xef, 0x30, 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, 0xe0, 0x00, - 0xef, 0x16, 0xe0, 0x4a, 0xef, 0x04, 0x00, 0xef, 0x5d, 0x00, 0x6f, 0x40, - 0xef, 0x21, 0x20, 0xaf, 0x40, 0xef, 0x15, 0x20, 0xef, 0x7f, 0xe0, 0x04, - 0xef, 0x06, 0x20, 0x6f, 0x60, 0x4f, 0x80, 0x4f, 0xe0, 0x05, 0xaf, 0xe0, - 0x84, 0xe2, 0xe5, 0xc0, 0x66, 0x4f, 0xe0, 0x21, 0xe5, 0x8f, 0xad, 0xe0, - 0x03, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c, - 0xa9, 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0, 0xca, 0xc5, 0x5b, 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_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_Common, - UNICODE_SCRIPT_Coptic, - UNICODE_SCRIPT_Cuneiform, - UNICODE_SCRIPT_Cypriot, - UNICODE_SCRIPT_Cyrillic, - UNICODE_SCRIPT_Deseret, - UNICODE_SCRIPT_Devanagari, - 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_Grantha, - UNICODE_SCRIPT_Greek, - UNICODE_SCRIPT_Gujarati, - UNICODE_SCRIPT_Gunjala_Gondi, - UNICODE_SCRIPT_Gurmukhi, - 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_Kayah_Li, - UNICODE_SCRIPT_Kharoshthi, - UNICODE_SCRIPT_Khmer, - UNICODE_SCRIPT_Khojki, - UNICODE_SCRIPT_Khudawadi, - 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_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_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_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_SignWriting, - UNICODE_SCRIPT_Sinhala, - UNICODE_SCRIPT_Sogdian, - UNICODE_SCRIPT_Sora_Sompeng, - UNICODE_SCRIPT_Soyombo, - UNICODE_SCRIPT_Sundanese, - 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_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_Ugaritic, - UNICODE_SCRIPT_Vai, - UNICODE_SCRIPT_Wancho, - UNICODE_SCRIPT_Warang_Citi, - 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" - "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" - "Common,Zyyy" - "\0" - "Coptic,Copt,Qaac" - "\0" - "Cuneiform,Xsux" - "\0" - "Cypriot,Cprt" - "\0" - "Cyrillic,Cyrl" - "\0" - "Deseret,Dsrt" - "\0" - "Devanagari,Deva" - "\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" - "Grantha,Gran" - "\0" - "Greek,Grek" - "\0" - "Gujarati,Gujr" - "\0" - "Gunjala_Gondi,Gong" - "\0" - "Gurmukhi,Guru" - "\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" - "Kayah_Li,Kali" - "\0" - "Kharoshthi,Khar" - "\0" - "Khmer,Khmr" - "\0" - "Khojki,Khoj" - "\0" - "Khudawadi,Sind" - "\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" - "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" - "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" - "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" - "SignWriting,Sgnw" - "\0" - "Sinhala,Sinh" - "\0" - "Sogdian,Sogd" - "\0" - "Sora_Sompeng,Sora" - "\0" - "Soyombo,Soyo" - "\0" - "Sundanese,Sund" - "\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" - "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" - "Ugaritic,Ugar" - "\0" - "Vai,Vaii" - "\0" - "Wancho,Wcho" - "\0" - "Warang_Citi,Wara" - "\0" - "Yi,Yiii" - "\0" - "Zanabazar_Square,Zanb" - "\0"; - -static const uint8_t unicode_script_table[2565] = { - 0xc0, 0x18, 0x99, 0x42, 0x85, 0x18, 0x99, 0x42, 0xae, 0x18, 0x80, 0x42, - 0x8e, 0x18, 0x80, 0x42, 0x84, 0x18, 0x96, 0x42, 0x80, 0x18, 0x9e, 0x42, - 0x80, 0x18, 0xe1, 0x60, 0x42, 0xa6, 0x18, 0x84, 0x42, 0x84, 0x18, 0x81, - 0x0d, 0x93, 0x18, 0xe0, 0x0f, 0x35, 0x83, 0x29, 0x80, 0x18, 0x82, 0x29, - 0x01, 0x83, 0x29, 0x80, 0x18, 0x80, 0x29, 0x03, 0x80, 0x29, 0x80, 0x18, - 0x80, 0x29, 0x80, 0x18, 0x82, 0x29, 0x00, 0x80, 0x29, 0x00, 0x93, 0x29, - 0x00, 0xbe, 0x29, 0x8d, 0x19, 0x8f, 0x29, 0xe0, 0x24, 0x1c, 0x81, 0x35, - 0xe0, 0x48, 0x1c, 0x00, 0xa5, 0x05, 0x01, 0xaf, 0x05, 0x80, 0x18, 0x80, - 0x05, 0x01, 0x82, 0x05, 0x00, 0xb6, 0x32, 0x07, 0x9a, 0x32, 0x03, 0x85, - 0x32, 0x0a, 0x84, 0x04, 0x80, 0x18, 0x85, 0x04, 0x80, 0x18, 0x8d, 0x04, - 0x80, 0x18, 0x80, 0x04, 0x00, 0x80, 0x04, 0x80, 0x18, 0x9f, 0x04, 0x80, - 0x18, 0x89, 0x04, 0x8a, 0x35, 0x99, 0x04, 0x80, 0x35, 0xe0, 0x0b, 0x04, - 0x80, 0x18, 0xa1, 0x04, 0x8d, 0x84, 0x00, 0xbb, 0x84, 0x01, 0x82, 0x84, - 0xaf, 0x04, 0xb1, 0x8e, 0x0d, 0xba, 0x60, 0x01, 0x82, 0x60, 0xad, 0x78, - 0x01, 0x8e, 0x78, 0x00, 0x9b, 0x4d, 0x01, 0x80, 0x4d, 0x00, 0x8a, 0x84, - 0x34, 0x94, 0x04, 0x00, 0x87, 0x04, 0x14, 0x8e, 0x04, 0x80, 0x18, 0x9c, - 0x04, 0xd0, 0x1e, 0x83, 0x35, 0x8e, 0x1e, 0x81, 0x18, 0x99, 0x1e, 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, 0x2c, 0x00, 0x85, 0x2c, 0x03, 0x81, - 0x2c, 0x01, 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, 0x00, 0x81, - 0x2c, 0x00, 0x81, 0x2c, 0x01, 0x80, 0x2c, 0x00, 0x84, 0x2c, 0x03, 0x81, - 0x2c, 0x01, 0x82, 0x2c, 0x02, 0x80, 0x2c, 0x06, 0x83, 0x2c, 0x00, 0x80, - 0x2c, 0x06, 0x90, 0x2c, 0x09, 0x82, 0x2a, 0x00, 0x88, 0x2a, 0x00, 0x82, - 0x2a, 0x00, 0x95, 0x2a, 0x00, 0x86, 0x2a, 0x00, 0x81, 0x2a, 0x00, 0x84, - 0x2a, 0x01, 0x89, 0x2a, 0x00, 0x82, 0x2a, 0x00, 0x82, 0x2a, 0x01, 0x80, - 0x2a, 0x0e, 0x83, 0x2a, 0x01, 0x8b, 0x2a, 0x06, 0x86, 0x2a, 0x00, 0x82, - 0x6d, 0x00, 0x87, 0x6d, 0x01, 0x81, 0x6d, 0x01, 0x95, 0x6d, 0x00, 0x86, - 0x6d, 0x00, 0x81, 0x6d, 0x00, 0x84, 0x6d, 0x01, 0x88, 0x6d, 0x01, 0x81, - 0x6d, 0x01, 0x82, 0x6d, 0x07, 0x81, 0x6d, 0x03, 0x81, 0x6d, 0x00, 0x84, - 0x6d, 0x01, 0x91, 0x6d, 0x09, 0x81, 0x8b, 0x00, 0x85, 0x8b, 0x02, 0x82, - 0x8b, 0x00, 0x83, 0x8b, 0x02, 0x81, 0x8b, 0x00, 0x80, 0x8b, 0x00, 0x81, - 0x8b, 0x02, 0x81, 0x8b, 0x02, 0x82, 0x8b, 0x02, 0x8b, 0x8b, 0x03, 0x84, - 0x8b, 0x02, 0x82, 0x8b, 0x00, 0x83, 0x8b, 0x01, 0x80, 0x8b, 0x05, 0x80, - 0x8b, 0x0d, 0x94, 0x8b, 0x04, 0x8c, 0x8d, 0x00, 0x82, 0x8d, 0x00, 0x96, - 0x8d, 0x00, 0x8f, 0x8d, 0x02, 0x87, 0x8d, 0x00, 0x82, 0x8d, 0x00, 0x83, - 0x8d, 0x06, 0x81, 0x8d, 0x00, 0x82, 0x8d, 0x04, 0x83, 0x8d, 0x01, 0x89, - 0x8d, 0x06, 0x88, 0x8d, 0x8c, 0x3a, 0x00, 0x82, 0x3a, 0x00, 0x96, 0x3a, - 0x00, 0x89, 0x3a, 0x00, 0x84, 0x3a, 0x01, 0x88, 0x3a, 0x00, 0x82, 0x3a, - 0x00, 0x83, 0x3a, 0x06, 0x81, 0x3a, 0x06, 0x80, 0x3a, 0x00, 0x83, 0x3a, - 0x01, 0x89, 0x3a, 0x00, 0x81, 0x3a, 0x0c, 0x83, 0x4c, 0x00, 0x87, 0x4c, - 0x00, 0x82, 0x4c, 0x00, 0xb2, 0x4c, 0x00, 0x82, 0x4c, 0x00, 0x85, 0x4c, - 0x03, 0x8f, 0x4c, 0x01, 0x99, 0x4c, 0x01, 0x81, 0x7e, 0x00, 0x91, 0x7e, - 0x02, 0x97, 0x7e, 0x00, 0x88, 0x7e, 0x00, 0x80, 0x7e, 0x01, 0x86, 0x7e, - 0x02, 0x80, 0x7e, 0x03, 0x85, 0x7e, 0x00, 0x80, 0x7e, 0x00, 0x87, 0x7e, - 0x05, 0x89, 0x7e, 0x01, 0x82, 0x7e, 0x0b, 0xb9, 0x8f, 0x03, 0x80, 0x18, - 0x9b, 0x8f, 0x24, 0x81, 0x41, 0x00, 0x80, 0x41, 0x00, 0x84, 0x41, 0x00, - 0x97, 0x41, 0x00, 0x80, 0x41, 0x00, 0x96, 0x41, 0x01, 0x84, 0x41, 0x00, - 0x80, 0x41, 0x00, 0x85, 0x41, 0x01, 0x89, 0x41, 0x01, 0x83, 0x41, 0x1f, - 0xc7, 0x90, 0x00, 0xa3, 0x90, 0x03, 0xa6, 0x90, 0x00, 0xa3, 0x90, 0x00, - 0x8e, 0x90, 0x00, 0x86, 0x90, 0x83, 0x18, 0x81, 0x90, 0x24, 0xe0, 0x3f, - 0x5b, 0xa5, 0x25, 0x00, 0x80, 0x25, 0x04, 0x80, 0x25, 0x01, 0xaa, 0x25, - 0x80, 0x18, 0x83, 0x25, 0xe0, 0x9f, 0x2e, 0xc8, 0x24, 0x00, 0x83, 0x24, - 0x01, 0x86, 0x24, 0x00, 0x80, 0x24, 0x00, 0x83, 0x24, 0x01, 0xa8, 0x24, - 0x00, 0x83, 0x24, 0x01, 0xa0, 0x24, 0x00, 0x83, 0x24, 0x01, 0x86, 0x24, - 0x00, 0x80, 0x24, 0x00, 0x83, 0x24, 0x01, 0x8e, 0x24, 0x00, 0xb8, 0x24, - 0x00, 0x83, 0x24, 0x01, 0xc2, 0x24, 0x01, 0x9f, 0x24, 0x02, 0x99, 0x24, - 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, 0xe2, 0x1f, 0x12, 0x9c, 0x63, - 0x02, 0xca, 0x77, 0x82, 0x18, 0x8a, 0x77, 0x06, 0x8c, 0x85, 0x00, 0x86, - 0x85, 0x0a, 0x94, 0x30, 0x81, 0x18, 0x08, 0x93, 0x11, 0x0b, 0x8c, 0x86, - 0x00, 0x82, 0x86, 0x00, 0x81, 0x86, 0x0b, 0xdd, 0x3e, 0x01, 0x89, 0x3e, - 0x05, 0x89, 0x3e, 0x05, 0x81, 0x58, 0x81, 0x18, 0x80, 0x58, 0x80, 0x18, - 0x88, 0x58, 0x00, 0x89, 0x58, 0x05, 0xd8, 0x58, 0x06, 0xaa, 0x58, 0x04, - 0xc5, 0x12, 0x09, 0x9e, 0x44, 0x00, 0x8b, 0x44, 0x03, 0x8b, 0x44, 0x03, - 0x80, 0x44, 0x02, 0x8b, 0x44, 0x9d, 0x87, 0x01, 0x84, 0x87, 0x0a, 0xab, - 0x5e, 0x03, 0x99, 0x5e, 0x05, 0x8a, 0x5e, 0x02, 0x81, 0x5e, 0x9f, 0x3e, - 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x88, 0x00, 0x9c, 0x88, 0x01, 0x8a, - 0x88, 0x05, 0x89, 0x88, 0x05, 0x8d, 0x88, 0x01, 0x8e, 0x35, 0x40, 0xcb, - 0x07, 0x03, 0xac, 0x07, 0x02, 0xbf, 0x82, 0xb3, 0x0a, 0x07, 0x83, 0x0a, - 0xb7, 0x43, 0x02, 0x8e, 0x43, 0x02, 0x82, 0x43, 0xaf, 0x64, 0x88, 0x1c, - 0x06, 0xaa, 0x25, 0x01, 0x82, 0x25, 0x87, 0x82, 0x07, 0x82, 0x35, 0x80, - 0x18, 0x8c, 0x35, 0x80, 0x18, 0x86, 0x35, 0x83, 0x18, 0x80, 0x35, 0x85, - 0x18, 0x80, 0x35, 0x82, 0x18, 0x81, 0x35, 0x80, 0x18, 0x04, 0xa5, 0x42, - 0x84, 0x29, 0x80, 0x1c, 0xb0, 0x42, 0x84, 0x29, 0x83, 0x42, 0x84, 0x29, - 0x8c, 0x42, 0x80, 0x1c, 0xc5, 0x42, 0x80, 0x29, 0xb9, 0x35, 0x00, 0x84, - 0x35, 0xe0, 0x9f, 0x42, 0x95, 0x29, 0x01, 0x85, 0x29, 0x01, 0xa5, 0x29, - 0x01, 0x85, 0x29, 0x01, 0x87, 0x29, 0x00, 0x80, 0x29, 0x00, 0x80, 0x29, - 0x00, 0x80, 0x29, 0x00, 0x9e, 0x29, 0x01, 0xb4, 0x29, 0x00, 0x8e, 0x29, - 0x00, 0x8d, 0x29, 0x01, 0x85, 0x29, 0x00, 0x92, 0x29, 0x01, 0x82, 0x29, - 0x00, 0x88, 0x29, 0x00, 0x8b, 0x18, 0x81, 0x35, 0xd6, 0x18, 0x00, 0x8a, - 0x18, 0x80, 0x42, 0x01, 0x8a, 0x18, 0x80, 0x42, 0x8e, 0x18, 0x00, 0x8c, - 0x42, 0x02, 0x9f, 0x18, 0x0f, 0xa0, 0x35, 0x0e, 0xa5, 0x18, 0x80, 0x29, - 0x82, 0x18, 0x81, 0x42, 0x85, 0x18, 0x80, 0x42, 0x9a, 0x18, 0x80, 0x42, - 0x90, 0x18, 0xa8, 0x42, 0x82, 0x18, 0x03, 0xe2, 0x36, 0x18, 0x18, 0x8a, - 0x18, 0x14, 0xe3, 0x3f, 0x18, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x18, 0x01, - 0x9f, 0x18, 0x01, 0xe0, 0x07, 0x18, 0xae, 0x26, 0x00, 0xae, 0x26, 0x00, - 0x9f, 0x42, 0xe0, 0x13, 0x19, 0x04, 0x86, 0x19, 0xa5, 0x25, 0x00, 0x80, - 0x25, 0x04, 0x80, 0x25, 0x01, 0xb7, 0x91, 0x06, 0x81, 0x91, 0x0d, 0x80, - 0x91, 0x96, 0x24, 0x08, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, - 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, - 0x00, 0x86, 0x24, 0x00, 0x9f, 0x1c, 0xcf, 0x18, 0x2f, 0x99, 0x2d, 0x00, - 0xd8, 0x2d, 0x0b, 0xe0, 0x75, 0x2d, 0x19, 0x8b, 0x18, 0x03, 0x84, 0x18, - 0x80, 0x2d, 0x80, 0x18, 0x80, 0x2d, 0x98, 0x18, 0x88, 0x2d, 0x83, 0x35, - 0x81, 0x2e, 0x87, 0x18, 0x83, 0x2d, 0x83, 0x18, 0x00, 0xd5, 0x33, 0x01, - 0x81, 0x35, 0x81, 0x18, 0x82, 0x33, 0x80, 0x18, 0xd9, 0x3b, 0x81, 0x18, - 0x82, 0x3b, 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x2e, 0x00, 0x8f, 0x18, 0x9a, - 0x0d, 0x04, 0xa3, 0x18, 0x0b, 0x8f, 0x3b, 0x9e, 0x2e, 0x00, 0xbf, 0x18, - 0x9e, 0x2e, 0xd0, 0x18, 0xae, 0x3b, 0x80, 0x18, 0xd7, 0x3b, 0xe0, 0x47, - 0x18, 0xf0, 0x09, 0x55, 0x2d, 0x09, 0xbf, 0x18, 0xf0, 0x41, 0x8f, 0x2d, - 0x0f, 0xe4, 0x2c, 0x97, 0x02, 0xb6, 0x97, 0x08, 0xaf, 0x47, 0xe0, 0xcb, - 0x94, 0x13, 0xdf, 0x1c, 0xd7, 0x08, 0x07, 0xa1, 0x18, 0xe0, 0x05, 0x42, - 0x82, 0x18, 0xb4, 0x42, 0x01, 0x84, 0x42, 0x2f, 0x88, 0x42, 0xab, 0x83, - 0x03, 0x89, 0x18, 0x05, 0xb7, 0x73, 0x07, 0xc5, 0x79, 0x07, 0x8b, 0x79, - 0x05, 0x9f, 0x1e, 0xad, 0x3c, 0x80, 0x18, 0x80, 0x3c, 0xa3, 0x76, 0x0a, - 0x80, 0x76, 0x9c, 0x2e, 0x02, 0xcd, 0x38, 0x00, 0x80, 0x18, 0x89, 0x38, - 0x03, 0x81, 0x38, 0x9e, 0x5b, 0x00, 0xb6, 0x16, 0x08, 0x8d, 0x16, 0x01, - 0x89, 0x16, 0x01, 0x83, 0x16, 0x9f, 0x5b, 0xc2, 0x89, 0x17, 0x84, 0x89, - 0x96, 0x52, 0x09, 0x85, 0x24, 0x01, 0x85, 0x24, 0x01, 0x85, 0x24, 0x08, - 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0xaa, 0x42, 0x80, 0x18, 0x88, 0x42, - 0x80, 0x29, 0x81, 0x42, 0x07, 0xcf, 0x17, 0xad, 0x52, 0x01, 0x89, 0x52, - 0x05, 0xf0, 0x1b, 0x43, 0x2e, 0x0b, 0x96, 0x2e, 0x03, 0xb0, 0x2e, 0x70, - 0x10, 0xa3, 0xe1, 0x0d, 0x2d, 0x01, 0xe0, 0x09, 0x2d, 0x25, 0x86, 0x42, - 0x0b, 0x84, 0x05, 0x04, 0x99, 0x32, 0x00, 0x84, 0x32, 0x00, 0x80, 0x32, - 0x00, 0x81, 0x32, 0x00, 0x81, 0x32, 0x00, 0x89, 0x32, 0xe0, 0x11, 0x04, - 0x10, 0xe1, 0x0a, 0x04, 0x81, 0x18, 0x0f, 0xbf, 0x04, 0x01, 0xb5, 0x04, - 0x27, 0x8d, 0x04, 0x01, 0x8f, 0x35, 0x89, 0x18, 0x05, 0x8d, 0x35, 0x81, - 0x1c, 0xa2, 0x18, 0x00, 0x92, 0x18, 0x00, 0x83, 0x18, 0x03, 0x84, 0x04, - 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, 0x18, 0x00, 0x9f, 0x18, 0x99, 0x42, - 0x85, 0x18, 0x99, 0x42, 0x8a, 0x18, 0x89, 0x3b, 0x80, 0x18, 0xac, 0x3b, - 0x81, 0x18, 0x9e, 0x2e, 0x02, 0x85, 0x2e, 0x01, 0x85, 0x2e, 0x01, 0x85, - 0x2e, 0x01, 0x82, 0x2e, 0x02, 0x86, 0x18, 0x00, 0x86, 0x18, 0x09, 0x84, - 0x18, 0x01, 0x8b, 0x46, 0x00, 0x99, 0x46, 0x00, 0x92, 0x46, 0x00, 0x81, - 0x46, 0x00, 0x8e, 0x46, 0x01, 0x8d, 0x46, 0x21, 0xe0, 0x1a, 0x46, 0x04, - 0x82, 0x18, 0x03, 0xac, 0x18, 0x02, 0x88, 0x18, 0xce, 0x29, 0x00, 0x8b, - 0x18, 0x03, 0x80, 0x29, 0x2e, 0xac, 0x18, 0x80, 0x35, 0x60, 0x21, 0x9c, - 0x48, 0x02, 0xb0, 0x13, 0x0e, 0x80, 0x35, 0x9a, 0x18, 0x03, 0xa3, 0x66, - 0x08, 0x82, 0x66, 0x9a, 0x27, 0x04, 0xaa, 0x68, 0x04, 0x9d, 0x93, 0x00, - 0x80, 0x93, 0xa3, 0x69, 0x03, 0x8d, 0x69, 0x29, 0xcf, 0x1d, 0xaf, 0x7b, - 0x9d, 0x6f, 0x01, 0x89, 0x6f, 0x05, 0xa3, 0x6e, 0x03, 0xa3, 0x6e, 0x03, - 0xa7, 0x22, 0x07, 0xb3, 0x14, 0x0a, 0x80, 0x14, 0x60, 0x2f, 0xe0, 0xd6, - 0x45, 0x08, 0x95, 0x45, 0x09, 0x87, 0x45, 0x60, 0x37, 0x85, 0x1b, 0x01, - 0x80, 0x1b, 0x00, 0xab, 0x1b, 0x00, 0x81, 0x1b, 0x02, 0x80, 0x1b, 0x01, - 0x80, 0x1b, 0x95, 0x34, 0x00, 0x88, 0x34, 0x9f, 0x71, 0x9e, 0x5c, 0x07, - 0x88, 0x5c, 0x2f, 0x92, 0x31, 0x00, 0x81, 0x31, 0x04, 0x84, 0x31, 0x9b, - 0x74, 0x02, 0x80, 0x74, 0x99, 0x49, 0x04, 0x80, 0x49, 0x3f, 0x9f, 0x55, - 0x97, 0x54, 0x03, 0x93, 0x54, 0x01, 0xad, 0x54, 0x83, 0x3d, 0x00, 0x81, - 0x3d, 0x04, 0x87, 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x9c, 0x3d, 0x01, 0x82, - 0x3d, 0x03, 0x89, 0x3d, 0x06, 0x88, 0x3d, 0x06, 0x9f, 0x6b, 0x9f, 0x67, - 0x1f, 0xa6, 0x4e, 0x03, 0x8b, 0x4e, 0x08, 0xb5, 0x06, 0x02, 0x86, 0x06, - 0x95, 0x37, 0x01, 0x87, 0x37, 0x92, 0x36, 0x04, 0x87, 0x36, 0x91, 0x75, - 0x06, 0x83, 0x75, 0x0b, 0x86, 0x75, 0x4f, 0xc8, 0x6c, 0x36, 0xb2, 0x65, - 0x0c, 0xb2, 0x65, 0x06, 0x85, 0x65, 0xa7, 0x2f, 0x07, 0x89, 0x2f, 0x60, - 0xc5, 0x9e, 0x04, 0x60, 0x20, 0xa7, 0x6a, 0x07, 0xa9, 0x7f, 0x60, 0x25, - 0x96, 0x23, 0x08, 0xcd, 0x0e, 0x03, 0x9d, 0x0e, 0x0e, 0x80, 0x0e, 0xc1, - 0x39, 0x0a, 0x80, 0x39, 0x01, 0x98, 0x80, 0x06, 0x89, 0x80, 0x05, 0xb4, - 0x15, 0x00, 0x90, 0x15, 0x08, 0xa6, 0x4b, 0x08, 0xcd, 0x7a, 0x01, 0x8f, - 0x7a, 0x00, 0x93, 0x7e, 0x0a, 0x91, 0x3f, 0x00, 0xab, 0x3f, 0x40, 0x86, - 0x5a, 0x00, 0x80, 0x5a, 0x00, 0x83, 0x5a, 0x00, 0x8e, 0x5a, 0x00, 0x8a, - 0x5a, 0x05, 0xba, 0x40, 0x04, 0x89, 0x40, 0x05, 0x83, 0x28, 0x00, 0x87, - 0x28, 0x01, 0x81, 0x28, 0x01, 0x95, 0x28, 0x00, 0x86, 0x28, 0x00, 0x81, - 0x28, 0x00, 0x84, 0x28, 0x00, 0x80, 0x35, 0x88, 0x28, 0x01, 0x81, 0x28, - 0x01, 0x82, 0x28, 0x01, 0x80, 0x28, 0x05, 0x80, 0x28, 0x04, 0x86, 0x28, - 0x01, 0x86, 0x28, 0x02, 0x84, 0x28, 0x60, 0x2a, 0xd9, 0x5f, 0x00, 0x80, - 0x5f, 0x00, 0x82, 0x5f, 0x1f, 0xc7, 0x92, 0x07, 0x89, 0x92, 0x60, 0x45, - 0xb5, 0x7c, 0x01, 0xa5, 0x7c, 0x21, 0xc4, 0x57, 0x0a, 0x89, 0x57, 0x05, - 0x8c, 0x58, 0x12, 0xb8, 0x8a, 0x06, 0x89, 0x8a, 0x35, 0x9a, 0x02, 0x01, - 0x8e, 0x02, 0x03, 0x8f, 0x02, 0x60, 0x5f, 0xbb, 0x1f, 0x60, 0x03, 0xd2, - 0x96, 0x0b, 0x80, 0x96, 0x60, 0x3f, 0x87, 0x5d, 0x01, 0xad, 0x5d, 0x01, - 0x8a, 0x5d, 0x1a, 0xc7, 0x98, 0x07, 0xd2, 0x81, 0x1c, 0xb8, 0x72, 0x60, - 0xa6, 0x88, 0x0c, 0x00, 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, - 0x02, 0x9f, 0x4f, 0x01, 0x95, 0x4f, 0x00, 0x8d, 0x4f, 0x48, 0x86, 0x50, - 0x00, 0x81, 0x50, 0x00, 0xab, 0x50, 0x02, 0x80, 0x50, 0x00, 0x81, 0x50, - 0x00, 0x88, 0x50, 0x07, 0x89, 0x50, 0x05, 0x85, 0x2b, 0x00, 0x81, 0x2b, - 0x00, 0xa4, 0x2b, 0x00, 0x81, 0x2b, 0x00, 0x85, 0x2b, 0x06, 0x89, 0x2b, - 0x60, 0xd5, 0x98, 0x4a, 0x60, 0x66, 0xb1, 0x8b, 0x0c, 0x80, 0x8b, 0xe3, - 0x39, 0x1a, 0x60, 0x05, 0xe0, 0x0e, 0x1a, 0x00, 0x84, 0x1a, 0x0a, 0xe0, - 0x63, 0x1a, 0x6a, 0x5b, 0xe3, 0xce, 0x21, 0x00, 0x88, 0x21, 0x6f, 0x66, - 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, 0xe1, 0xd8, 0x08, 0x06, 0x9e, 0x59, - 0x00, 0x89, 0x59, 0x03, 0x81, 0x59, 0x5f, 0x9d, 0x09, 0x01, 0x85, 0x09, - 0x09, 0xc5, 0x70, 0x09, 0x89, 0x70, 0x00, 0x86, 0x70, 0x00, 0x94, 0x70, - 0x04, 0x92, 0x70, 0x62, 0x4f, 0xda, 0x51, 0x60, 0x04, 0xca, 0x56, 0x03, - 0xb8, 0x56, 0x06, 0x90, 0x56, 0x3f, 0x80, 0x8c, 0x80, 0x61, 0x81, 0x18, - 0x1b, 0xf0, 0x07, 0x97, 0x8c, 0x07, 0xe2, 0x92, 0x8c, 0x70, 0x14, 0xac, - 0x80, 0x3b, 0xe0, 0xbd, 0x33, 0x30, 0x82, 0x33, 0x10, 0x83, 0x3b, 0x07, - 0xe1, 0x2b, 0x61, 0x68, 0xa3, 0xe0, 0x0a, 0x20, 0x04, 0x8c, 0x20, 0x02, - 0x88, 0x20, 0x06, 0x89, 0x20, 0x01, 0x83, 0x20, 0x83, 0x18, 0x70, 0x02, - 0xfb, 0xe0, 0x95, 0x18, 0x09, 0xa6, 0x18, 0x01, 0xbd, 0x18, 0x82, 0x35, - 0x90, 0x18, 0x87, 0x35, 0x81, 0x18, 0x86, 0x35, 0x9d, 0x18, 0x83, 0x35, - 0xba, 0x18, 0x16, 0xc5, 0x29, 0x60, 0x39, 0x93, 0x18, 0x0b, 0xd6, 0x18, - 0x08, 0x98, 0x18, 0x60, 0x26, 0xd4, 0x18, 0x00, 0xc6, 0x18, 0x00, 0x81, - 0x18, 0x01, 0x80, 0x18, 0x01, 0x81, 0x18, 0x01, 0x83, 0x18, 0x00, 0x8b, - 0x18, 0x00, 0x80, 0x18, 0x00, 0x86, 0x18, 0x00, 0xc0, 0x18, 0x00, 0x83, - 0x18, 0x01, 0x87, 0x18, 0x00, 0x86, 0x18, 0x00, 0x9b, 0x18, 0x00, 0x83, - 0x18, 0x00, 0x84, 0x18, 0x00, 0x80, 0x18, 0x02, 0x86, 0x18, 0x00, 0xe0, - 0xf3, 0x18, 0x01, 0xe0, 0xc3, 0x18, 0x01, 0xb1, 0x18, 0xe2, 0x2b, 0x7d, - 0x0e, 0x84, 0x7d, 0x00, 0x8e, 0x7d, 0x64, 0xef, 0x86, 0x26, 0x00, 0x90, - 0x26, 0x01, 0x86, 0x26, 0x00, 0x81, 0x26, 0x00, 0x84, 0x26, 0x60, 0x74, - 0xac, 0x62, 0x02, 0x8d, 0x62, 0x01, 0x89, 0x62, 0x03, 0x81, 0x62, 0x61, - 0x0f, 0xb9, 0x95, 0x04, 0x80, 0x95, 0x64, 0x9f, 0xe0, 0x64, 0x53, 0x01, - 0x8f, 0x53, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, 0x03, 0x81, 0x01, 0x62, - 0xb0, 0xc3, 0x18, 0x4b, 0xbc, 0x18, 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, 0x18, 0x03, 0xe0, 0x03, 0x18, 0x0b, 0x8e, 0x18, - 0x01, 0x8e, 0x18, 0x00, 0x8e, 0x18, 0x00, 0xa4, 0x18, 0x09, 0x8c, 0x18, - 0x02, 0xdc, 0x18, 0x02, 0xbc, 0x18, 0x38, 0x99, 0x18, 0x80, 0x33, 0x81, - 0x18, 0x0c, 0xab, 0x18, 0x03, 0x88, 0x18, 0x06, 0x81, 0x18, 0x0d, 0x85, - 0x18, 0x60, 0x39, 0xe3, 0x75, 0x18, 0x09, 0x8c, 0x18, 0x02, 0x8a, 0x18, - 0x04, 0xe0, 0x13, 0x18, 0x0b, 0xd8, 0x18, 0x06, 0x8b, 0x18, 0x13, 0x8b, - 0x18, 0x03, 0xb7, 0x18, 0x07, 0x89, 0x18, 0x05, 0xa7, 0x18, 0x07, 0x9d, - 0x18, 0x51, 0x8b, 0x18, 0x00, 0xe0, 0x04, 0x18, 0x00, 0x83, 0x18, 0x02, - 0xa8, 0x18, 0x01, 0x85, 0x18, 0x02, 0x9c, 0x18, 0x01, 0xe0, 0x26, 0x18, - 0x0b, 0x8d, 0x18, 0x01, 0x83, 0x18, 0x03, 0x82, 0x18, 0x04, 0x82, 0x18, - 0x0c, 0x85, 0x18, 0x65, 0x09, 0xf0, 0x96, 0x76, 0x2d, 0x28, 0xef, 0xd4, - 0x2d, 0x0a, 0xe0, 0x7d, 0x2d, 0x01, 0xf0, 0x06, 0x21, 0x2d, 0x0d, 0xf0, - 0x0c, 0xd0, 0x2d, 0x6b, 0xbe, 0xe1, 0xbd, 0x2d, 0x7a, 0xf5, 0x82, 0x80, - 0x18, 0x1d, 0xdf, 0x18, 0x60, 0x1f, 0xe0, 0x8f, 0x35, -}; - -static const uint8_t unicode_script_ext_table[789] = { - 0x82, 0xc1, 0x00, 0x00, 0x01, 0x29, 0x01, 0x00, 0x00, 0x01, 0x29, 0x1c, - 0x00, 0x0c, 0x01, 0x42, 0x80, 0x92, 0x00, 0x00, 0x02, 0x1c, 0x68, 0x00, - 0x02, 0x1c, 0x26, 0x01, 0x02, 0x1c, 0x42, 0x00, 0x02, 0x1c, 0x26, 0x80, - 0x80, 0x00, 0x00, 0x02, 0x05, 0x25, 0x80, 0x01, 0x00, 0x00, 0x04, 0x04, - 0x2f, 0x84, 0x8e, 0x0d, 0x00, 0x00, 0x04, 0x04, 0x2f, 0x84, 0x8e, 0x00, - 0x03, 0x04, 0x84, 0x8e, 0x01, 0x00, 0x00, 0x04, 0x04, 0x2f, 0x84, 0x8e, - 0x1f, 0x00, 0x00, 0x08, 0x01, 0x04, 0x4d, 0x4e, 0x75, 0x2f, 0x7f, 0x84, - 0x09, 0x00, 0x0a, 0x02, 0x04, 0x84, 0x09, 0x00, 0x09, 0x02, 0x04, 0x8e, - 0x05, 0x00, 0x00, 0x02, 0x04, 0x84, 0x62, 0x00, 0x00, 0x02, 0x04, 0x2f, - 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x1e, 0x28, 0x2a, 0x2c, 0x3a, 0x42, - 0x4c, 0x6d, 0x7a, 0x8b, 0x8d, 0x92, 0x00, 0x0c, 0x0b, 0x1e, 0x28, 0x2a, - 0x2c, 0x3a, 0x42, 0x4c, 0x6d, 0x8b, 0x8d, 0x92, 0x10, 0x00, 0x00, 0x14, - 0x0b, 0x1e, 0x1f, 0x2b, 0x50, 0x28, 0x2a, 0x2c, 0x3a, 0x4b, 0x4c, 0x5d, - 0x6d, 0x40, 0x7e, 0x83, 0x8a, 0x8b, 0x8d, 0x92, 0x00, 0x15, 0x0b, 0x1e, - 0x1f, 0x2b, 0x50, 0x28, 0x2a, 0x2c, 0x3a, 0x44, 0x4b, 0x4c, 0x5d, 0x6d, - 0x40, 0x7e, 0x83, 0x8a, 0x8b, 0x8d, 0x92, 0x09, 0x04, 0x1e, 0x1f, 0x39, - 0x4b, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x83, 0x75, 0x00, 0x09, 0x02, - 0x2c, 0x5a, 0x75, 0x00, 0x09, 0x02, 0x2a, 0x3f, 0x80, 0x75, 0x00, 0x0d, - 0x02, 0x28, 0x8b, 0x80, 0x71, 0x00, 0x09, 0x02, 0x3a, 0x5d, 0x82, 0xcf, - 0x00, 0x09, 0x03, 0x15, 0x5b, 0x87, 0x80, 0x30, 0x00, 0x00, 0x02, 0x25, - 0x42, 0x85, 0xb8, 0x00, 0x01, 0x04, 0x11, 0x30, 0x86, 0x85, 0x80, 0x4a, - 0x00, 0x01, 0x02, 0x58, 0x73, 0x00, 0x00, 0x00, 0x02, 0x58, 0x73, 0x84, - 0x49, 0x00, 0x00, 0x04, 0x0b, 0x1e, 0x28, 0x3a, 0x00, 0x01, 0x1e, 0x00, - 0x04, 0x0b, 0x1e, 0x28, 0x3a, 0x00, 0x02, 0x1e, 0x28, 0x00, 0x01, 0x1e, - 0x01, 0x02, 0x0b, 0x1e, 0x00, 0x02, 0x1e, 0x7a, 0x00, 0x02, 0x0b, 0x1e, - 0x00, 0x02, 0x1e, 0x7a, 0x00, 0x06, 0x1e, 0x3a, 0x4c, 0x6d, 0x8b, 0x8d, - 0x00, 0x01, 0x1e, 0x01, 0x02, 0x1e, 0x7a, 0x01, 0x01, 0x1e, 0x00, 0x02, - 0x1e, 0x7a, 0x00, 0x02, 0x0b, 0x1e, 0x06, 0x01, 0x1e, 0x00, 0x02, 0x1e, - 0x5d, 0x00, 0x02, 0x0b, 0x1e, 0x01, 0x01, 0x1e, 0x00, 0x02, 0x0b, 0x1e, - 0x03, 0x01, 0x1e, 0x00, 0x08, 0x0b, 0x1e, 0x28, 0x3a, 0x5d, 0x6d, 0x8d, - 0x92, 0x00, 0x02, 0x1e, 0x28, 0x00, 0x03, 0x1e, 0x28, 0x3a, 0x01, 0x02, - 0x0b, 0x1e, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x1e, 0x28, 0x00, 0x01, 0x5d, - 0x80, 0x44, 0x00, 0x01, 0x01, 0x29, 0x81, 0xec, 0x00, 0x00, 0x02, 0x42, - 0x58, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x1e, 0x28, 0x42, 0x8c, 0xd1, 0x00, - 0x00, 0x02, 0x1c, 0x26, 0x81, 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x2e, 0x2d, - 0x33, 0x3b, 0x97, 0x00, 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x01, 0x00, - 0x00, 0x01, 0x2d, 0x00, 0x00, 0x09, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, - 0x97, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x07, 0x06, - 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x03, 0x05, 0x0d, 0x2e, 0x2d, 0x33, - 0x3b, 0x09, 0x00, 0x03, 0x02, 0x0d, 0x2d, 0x01, 0x00, 0x00, 0x05, 0x0d, - 0x2e, 0x2d, 0x33, 0x3b, 0x04, 0x02, 0x33, 0x3b, 0x00, 0x00, 0x00, 0x05, - 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x03, 0x00, 0x01, 0x03, 0x2d, 0x33, 0x3b, - 0x01, 0x01, 0x2d, 0x58, 0x00, 0x03, 0x02, 0x33, 0x3b, 0x02, 0x00, 0x00, - 0x02, 0x33, 0x3b, 0x59, 0x00, 0x00, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, - 0x97, 0x00, 0x02, 0x33, 0x3b, 0x80, 0x12, 0x00, 0x0f, 0x01, 0x2d, 0x1f, - 0x00, 0x23, 0x01, 0x2d, 0x3b, 0x00, 0x27, 0x01, 0x2d, 0x37, 0x00, 0x30, - 0x01, 0x2d, 0x0e, 0x00, 0x0b, 0x01, 0x2d, 0x32, 0x00, 0x00, 0x01, 0x2d, - 0x57, 0x00, 0x18, 0x01, 0x2d, 0x09, 0x00, 0x04, 0x01, 0x2d, 0x5f, 0x00, - 0x1e, 0x01, 0x2d, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1c, 0x26, 0x81, - 0x3f, 0x00, 0x02, 0x0e, 0x1e, 0x1f, 0x2a, 0x2c, 0x3f, 0x3a, 0x39, 0x4b, - 0x4c, 0x57, 0x5d, 0x40, 0x8a, 0x92, 0x02, 0x0d, 0x1e, 0x1f, 0x2a, 0x2c, - 0x3f, 0x3a, 0x39, 0x4b, 0x57, 0x5d, 0x40, 0x8a, 0x92, 0x03, 0x0b, 0x1e, - 0x1f, 0x2a, 0x2c, 0x3f, 0x39, 0x4b, 0x57, 0x40, 0x8a, 0x92, 0x80, 0x36, - 0x00, 0x00, 0x02, 0x0b, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x1e, 0x8b, 0x39, - 0x00, 0x00, 0x03, 0x3c, 0x42, 0x5b, 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10, - 0x38, 0xc0, 0x13, 0xa1, 0x00, 0x00, 0x02, 0x04, 0x8e, 0x09, 0x00, 0x00, - 0x02, 0x04, 0x8e, 0x46, 0x00, 0x01, 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, - 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x09, - 0x00, 0x00, 0x02, 0x33, 0x3b, 0x2c, 0x00, 0x01, 0x02, 0x33, 0x3b, 0x80, - 0xdf, 0x00, 0x02, 0x02, 0x1b, 0x46, 0x03, 0x00, 0x2c, 0x03, 0x1b, 0x45, - 0x46, 0x02, 0x00, 0x08, 0x02, 0x1b, 0x46, 0x81, 0x1f, 0x00, 0x1b, 0x02, - 0x04, 0x19, 0x8f, 0x84, 0x00, 0x00, 0x02, 0x28, 0x8b, 0x00, 0x00, 0x00, - 0x02, 0x28, 0x8b, 0x36, 0x00, 0x01, 0x02, 0x28, 0x8b, 0x8c, 0x12, 0x00, - 0x01, 0x02, 0x28, 0x8b, 0x00, 0x00, 0x00, 0x02, 0x28, 0x8b, 0xc0, 0x5c, - 0x4b, 0x00, 0x03, 0x01, 0x20, 0x96, 0x3b, 0x00, 0x11, 0x01, 0x2d, 0x9e, - 0x5d, 0x00, 0x01, 0x01, 0x2d, 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[396] = { - 0x43, 0x44, 0x80, 0x42, 0x69, 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, 0x40, 0xa6, 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, 0x83, 0xb9, - 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, 0xb9, 0x30, 0x10, 0x17, - 0x81, 0x8a, 0x81, 0x9b, 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89, - 0x81, 0x9d, 0x81, 0xca, 0x28, 0x00, 0x87, 0x91, 0x81, 0xbc, 0x01, 0x86, - 0x91, 0x80, 0xe2, 0x01, 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, 0x90, 0x8a, - 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, 0x40, 0x8a, 0x84, - 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, 0xb8, 0x8a, 0xb1, 0x92, 0x41, - 0xaf, 0x8d, 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, 0x42, 0xd7, - 0x82, 0xb4, 0x8d, 0xbb, 0x80, 0xac, 0x88, 0xc6, 0x82, 0xa3, 0x8b, 0x91, - 0x81, 0xb8, 0x82, 0xaf, 0x8c, 0xeb, 0x88, 0x08, 0x28, 0x40, 0x9f, 0x89, - 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, 0x40, 0xd0, - 0x8c, 0x02, 0xe9, 0x91, 0x40, 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e, - 0x00, 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c, 0x41, 0x97, 0x31, 0x2b, - 0x80, 0x9b, 0x89, 0xa9, 0x20, 0x83, 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, - 0x38, 0x86, 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x10, 0x02, - 0x80, 0xc1, 0x20, 0x08, 0x83, 0x41, 0x5b, 0x83, 0x60, 0x50, 0x57, 0x00, - 0xb6, 0x33, 0x60, 0x4d, 0x0a, 0x80, 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, - 0x01, 0x04, 0x49, 0x1b, 0x80, 0x47, 0xe7, 0x99, 0x85, 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Other_Lowercase_table[51] = { - 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, 0x31, 0x9d, 0x84, - 0xdf, 0x80, 0xb3, 0x80, 0x59, 0xb0, 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, 0x40, 0x86, 0x81, 0x43, 0x61, 0x83, -}; - -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[62] = { - 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, 0x97, 0x80, 0xe5, - 0x80, 0x97, 0x80, 0x40, 0xe9, 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, - 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, 0x80, - 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, 0x53, 0x9d, 0x80, 0x97, - 0x80, 0x41, 0x57, 0x80, 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x60, 0xbb, - 0xb4, 0x07, 0x84, 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[12] = { - 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, 0x88, 0x46, 0x67, 0x80, -}; - -static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[17] = { - 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, 0x41, - 0xd1, 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[33] = { - 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, 0x3c, 0x80, 0x59, - 0x11, 0x80, 0x40, 0xe4, 0x3f, 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, - 0x11, 0x80, 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, 0x84, -}; - -static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[436] = { - 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, 0x10, 0x82, 0x9f, 0x80, - 0xcf, 0x01, 0x80, 0x8b, 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80, - 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, 0x80, 0xa3, - 0x04, 0x02, 0x04, 0x08, 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, - 0x40, 0x93, 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, - 0x83, 0x45, 0x1c, 0x10, 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, - 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x3f, 0x3f, 0x87, 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, - 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 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, 0x40, 0x86, 0x81, 0x43, 0x61, 0x83, 0x60, 0x4d, 0x9f, - 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, - 0xe9, 0xa5, 0x86, 0x8b, 0x24, 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, 0x60, 0xbc, 0xa6, 0x83, 0x54, 0xb9, 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, 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, - 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, - 0x82, 0x8c, 0xab, 0x83, 0x88, 0x31, 0x61, 0x05, 0xad, 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[50] = { - 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, 0x41, 0xda, 0x80, 0x92, 0x80, 0xee, 0x80, 0x60, - 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, 0x80, 0x40, 0xa8, 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[350] = { - 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, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, 0x40, 0x85, 0x3b, 0x81, - 0x40, 0x85, 0x0b, 0x0a, 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, - 0x81, 0x40, 0xc8, 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, 0x80, 0xfe, 0x80, 0xfe, 0x80, 0xed, 0x80, 0x8f, - 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, 0x80, 0xfb, 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, 0x44, 0x68, 0x8a, 0x88, 0x80, 0x41, 0x5a, 0x82, - 0x41, 0x38, 0x39, 0x80, 0xaf, 0x8d, 0xf5, 0x80, 0x8e, 0x80, 0xa5, 0x88, - 0xb5, 0x81, 0x40, 0x89, 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, - 0xb1, 0xbe, 0xd8, 0x8b, 0xa4, 0x22, 0x82, 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, 0xec, - 0x81, 0x40, 0xc9, 0x80, 0x9a, 0x91, 0xb8, 0x83, 0xa3, 0x80, 0xde, 0x80, - 0x8b, 0x80, 0xa3, 0x80, 0x40, 0x94, 0x82, 0xc0, 0x83, 0xb2, 0x80, 0xe3, - 0x84, 0x40, 0x8b, 0x81, 0x60, 0x4f, 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, - 0x0d, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, 0x80, 0x42, 0xfb, 0x80, - 0x48, 0x03, 0x81, 0x42, 0x3a, 0x85, 0x42, 0x1d, 0x8a, 0x41, 0x67, 0x81, - 0xf7, 0x81, 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7, 0x81, 0x40, 0xb1, - 0x81, 0xd0, 0x80, 0x8f, 0x80, 0x97, 0x32, 0x84, 0x40, 0xcc, 0x02, 0x80, - 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, 0xf5, 0x81, 0xf2, 0x80, 0x41, - 0x0c, 0x81, 0x41, 0xa4, 0x80, 0xd2, 0x80, 0x91, 0x80, 0xd0, 0x80, 0x41, - 0xa4, 0x80, 0x41, 0x01, 0x00, 0x81, 0xd0, 0x80, 0x60, 0x4d, 0x57, 0x84, - 0xba, 0x86, 0x44, 0x57, 0x90, 0x60, 0x61, 0xc6, 0x12, 0x2f, 0x39, 0x86, - 0x9d, 0x83, 0x4f, 0x81, 0x86, 0x41, 0xb4, 0x83, 0x45, 0xdf, 0x86, 0xec, - 0x10, 0x82, -}; - -static const uint8_t unicode_prop_Extender_table[86] = { - 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, 0x80, 0x41, 0xb8, - 0x80, 0x46, 0x4a, 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, 0x53, 0xeb, 0x80, 0x42, - 0x67, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, 0x81, 0x44, 0x9b, - 0x08, 0x80, 0x60, 0x71, 0x57, 0x81, 0x48, 0x05, 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_Binary_Operator_table[5] = { - 0x60, 0x2f, 0xef, 0x09, 0x87, -}; - -static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { - 0x60, - 0x2f, - 0xf1, - 0x81, -}; - -static const uint8_t unicode_prop_Ideographic_table[58] = { - 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, 0x43, 0xc4, 0x59, 0xb5, - 0xc9, 0x60, 0x51, 0xef, 0x60, 0x59, 0x0f, 0x41, 0x6d, 0x81, 0xe9, 0x60, - 0x75, 0x25, 0x57, 0xf7, 0x87, 0x42, 0xf2, 0x60, 0x26, 0x7c, 0x41, 0x8b, - 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xd6, 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, - 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, -}; - -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_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[184] = { - 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, 0x80, 0x40, 0x93, 0x81, - 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, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41, - 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x40, 0xda, 0x81, 0xc0, 0x81, 0x43, - 0xbb, 0x81, 0x88, 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x41, 0xc4, 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, 0x65, 0x02, 0x81, 0x40, - 0xa8, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, - 0xfc, 0x84, 0x40, 0xec, 0x81, 0xf4, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, - 0x80, 0x8f, 0x81, 0xd7, 0x08, 0x81, 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, - 0x74, 0x0c, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x43, 0x02, 0x81, 0xd6, - 0x81, 0x41, 0xa3, 0x81, 0x42, 0xb3, 0x81, 0x60, 0x4b, 0x74, 0x81, 0x40, - 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80, 0x43, 0x52, 0x80, 0x60, 0x4e, 0x05, - 0x80, 0x5d, 0xe7, 0x80, -}; - -static const uint8_t unicode_prop_Soft_Dotted_table[71] = { - 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, -}; - -static const uint8_t unicode_prop_Terminal_Punctuation_table[237] = { - 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, 0x43, 0x3d, 0x07, 0x80, - 0x42, 0x00, 0x80, 0xb8, 0x80, 0xc7, 0x80, 0x8d, 0x01, 0x81, 0x40, 0xb3, - 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, 0x8e, 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, - 0xad, 0x08, 0x82, 0x40, 0xda, 0x84, 0xbd, 0x81, 0x43, 0xbb, 0x81, 0x88, - 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x81, 0x41, 0xb0, - 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, 0x63, 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, - 0x40, 0xec, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, - 0x81, 0xd7, 0x84, 0xeb, 0x80, 0x41, 0xa0, 0x82, 0x8c, 0x80, 0x41, 0x65, - 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x43, 0x02, 0x81, 0xd6, 0x0b, - 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0x45, 0x76, 0x84, - 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80, 0xc0, 0x82, 0x89, 0x80, 0x43, - 0x51, 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, -}; - -static const uint8_t unicode_prop_Unified_Ideograph_table[38] = { - 0x60, 0x33, 0xff, 0x59, 0xb5, 0xc9, 0x60, 0x51, 0xef, 0x60, - 0x5a, 0x1d, 0x08, 0x00, 0x81, 0x89, 0x00, 0x00, 0x09, 0x82, - 0x61, 0x05, 0xd5, 0x60, 0xa6, 0xd6, 0xa8, 0x50, 0x34, 0x8a, - 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, -}; - -static const uint8_t unicode_prop_Variation_Selector_table[12] = { - 0x58, 0x0a, 0x82, 0x60, 0xe5, 0xf1, 0x8f, 0x6d, 0x02, 0xef, 0x40, 0xef, -}; - -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_Bidi_Mirrored_table[171] = { - 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, 0x39, 0x9e, 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, 0x41, 0xdd, 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[236] = { - 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, - 0x0f, 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, 0x08, 0x89, 0x2a, 0x00, 0x0a, 0x01, 0x87, 0x40, 0xe4, 0x8b, 0x41, - 0x20, 0xad, 0x80, 0x89, 0x80, 0xaa, 0x03, 0x82, 0xa8, 0x0d, 0x82, 0x9c, - 0x81, 0xb2, 0xef, 0x1b, 0x14, 0x82, 0x8c, 0x85, -}; - -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[63] = { - 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, 0x41, 0x07, 0x3d, 0x80, 0x88, 0x89, - 0x0a, 0xf5, 0x08, 0x08, 0x80, 0x90, 0x10, 0x8c, -}; - -static const uint8_t unicode_prop_Emoji_Presentation_table[143] = { - 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, 0x08, 0x94, 0x0e, 0x86, 0x40, - 0xe4, 0x8b, 0x41, 0x20, 0xad, 0x80, 0x89, 0x80, 0xaa, 0x03, 0x82, 0xa8, - 0x0d, 0x82, 0x9c, 0x81, 0xb2, 0xef, 0x1b, 0x14, 0x82, 0x8c, 0x85, -}; - -static const uint8_t unicode_prop_Extended_Pictographic_table[152] = { - 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, - 0x95, 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, - 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, - 0x89, 0x80, 0x88, 0x80, 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, - 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, 0x81, 0x8e, 0x01, 0x03, - 0x00, 0x03, 0x10, 0x80, 0x8a, 0x84, 0xac, 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, - 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, 0x85, 0x8b, 0x81, 0x8d, - 0x01, 0x89, 0x91, 0xb8, 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, - 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, 0x09, 0xaf, 0xff, 0xf3, - 0x8b, 0xd4, 0xaa, 0x8b, 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, - 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x46, 0xb6, -}; - -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, 0x83, 0x47, 0xfb, 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_Binary_Operator, - UNICODE_PROP_IDS_Trinary_Operator, - UNICODE_PROP_Ideographic, - UNICODE_PROP_Join_Control, - UNICODE_PROP_Logical_Order_Exception, - 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_Lowercase, - UNICODE_PROP_Math, - UNICODE_PROP_Uppercase, - UNICODE_PROP_XID_Continue, - UNICODE_PROP_XID_Start, - UNICODE_PROP_Cased1, - 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_Binary_Operator,IDSB" - "\0" - "IDS_Trinary_Operator,IDST" - "\0" - "Ideographic,Ideo" - "\0" - "Join_Control,Join_C" - "\0" - "Logical_Order_Exception,LOE" - "\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" - "\0" - "Emoji_Modifier" - "\0" - "Emoji_Modifier_Base" - "\0" - "Emoji_Presentation" - "\0" - "Extended_Pictographic" - "\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" - "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_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_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_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_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), -}; - -#endif /* CONFIG_ALL_UNICODE */ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/primjs_monitor.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/primjs_monitor.h deleted file mode 100644 index d26cdc79..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/primjs_monitor.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ -#define SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ - -#define MODULE_PRIMJS "primjs" -#define MODULE_QUICK "quickjs" - -#define MODULE_NAPI "napi" -#define DEFAULT_BIZ_NAME "unknown_biz_name" - -void MonitorEvent(const char* moduleName, const char* bizName, - const char* dataKey, const char* dataValue); -bool GetSettingsWithKey(const char* key); -int GetSettingsFlag(); - -#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-atom.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-atom.h deleted file mode 100644 index c6c6367d..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-atom.h +++ /dev/null @@ -1,286 +0,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. - */ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#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(length, "length") -DEF(fileName, "fileName") -DEF(lineNumber, "lineNumber") -DEF(message, "message") -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(_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(_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(status, "status") -DEF(reason, "reason") -#ifdef CONFIG_BIGNUM -DEF(bigint, "bigint") -DEF(bigfloat, "bigfloat") -#endif -#ifdef CONFIG_ATOMICS -DEF(not_equal, "not-equal") -DEF(timed_out, "timed-out") -DEF(ok, "ok") -#endif -DEF(toJSON, "toJSON") -/* 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") -#ifdef CONFIG_BIGNUM -DEF(BigInt64Array, "BigInt64Array") -DEF(BigUint64Array, "BigUint64Array") -#endif -DEF(Float32Array, "Float32Array") -DEF(Float64Array, "Float64Array") -DEF(DataView, "DataView") -#ifdef CONFIG_BIGNUM -DEF(BigInt, "BigInt") -DEF(BigFloat, "BigFloat") -DEF(BigFloatEnv, "BigFloatEnv") -#endif -DEF(Map, "Map") -DEF(Set, "Set") /* Map + 1 */ -DEF(WeakMap, "WeakMap") /* Map + 2 */ -DEF(WeakSet, "WeakSet") /* Map + 3 */ -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") -/* 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") -#ifdef CONFIG_BIGNUM -DEF(Symbol_operatorOrder, "Symbol.operatorOrder") -DEF(Symbol_operatorAdd, "Symbol.operatorAdd") -DEF(Symbol_operatorSub, "Symbol.operatorSub") -DEF(Symbol_operatorMul, "Symbol.operatorMul") -DEF(Symbol_operatorDiv, "Symbol.operatorDiv") -DEF(Symbol_operatorMod, "Symbol.operatorMod") -DEF(Symbol_operatorPow, "Symbol.operatorPow") -DEF(Symbol_operatorShl, "Symbol.operatorShl") -DEF(Symbol_operatorShr, "Symbol.operatorShr") -DEF(Symbol_operatorAnd, "Symbol.operatorAnd") -DEF(Symbol_operatorOr, "Symbol.operatorOr") -DEF(Symbol_operatorXor, "Symbol.operatorXor") -DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT") -DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE") -DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ") -DEF(Symbol_operatorPlus, "Symbol.operatorPlus") -DEF(Symbol_operatorNeg, "Symbol.operatorNeg") -DEF(Symbol_operatorNot, "Symbol.operatorNot") -DEF(Symbol_operatorInc, "Symbol.operatorInc") -DEF(Symbol_operatorDec, "Symbol.operatorDec") -DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod") -#endif - -#endif /* DEF */ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-inner.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-inner.h deleted file mode 100644 index c3dd6e48..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-inner.h +++ /dev/null @@ -1,3204 +0,0 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2019 Fabrice Bellard - * Copyright (c) 2017-2019 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. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_INNER_H_ -#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_INNER_H_ - -#include "quickjs/include/base_export.h" -#ifdef __cplusplus -extern "C" { -#endif - -#include "quickjs/include/cutils.h" -#include "quickjs/include/list.h" -#include "quickjs/include/quickjs.h" - -#ifdef CONFIG_BIGNUM -#include "quickjs/include/libbf.h" -#endif - -#ifdef __cplusplus -} -#endif - -#include "gc/allocator.h" -#include "quickjs/include/primjs_monitor.h" -#include "quickjs/include/quickjs_queue.h" - -#ifndef _WIN32 -#include - -#include -#endif -#include - -#ifdef ENABLE_GC_DEBUG_TOOLS -#include -#ifndef DCHECK -#define DCHECK(condition) \ - if (!(condition)) abort(); -#endif -#else -#ifndef DCHECK -#define DCHECK(condition) ((void)0) -#endif -#endif - -typedef int BOOL; -#define SYSCALL_CHECK(condition) \ - if ((condition) == -1) { \ - /*abort()*/ \ - } - -#ifdef ENABLE_QUICKJS_DEBUGGER -#include "inspector/debugger_inner.h" -#endif - -#include "gc/qjsvaluevalue-space.h" - -#if defined(CONFIG_BIGNUM) and defined(ENABLE_LEPUSNG) -#error bignum and lepusng are now conflict! -#endif -#if defined(QJS_UNITTEST) || defined(__WASI_SDK__) -#define QJS_STATIC -#else -#define QJS_STATIC static -#endif - -#define OPTIMIZE 1 -#define SHORT_OPCODES 1 - -#if !defined(__aarch64__) -#if defined(ENABLE_PRIMJS_SNAPSHOT) -#undef ENABLE_PRIMJS_SNAPSHOT -#endif -#if defined(ENABLE_COMPATIBLE_MM) -#undef ENABLE_COMPATIBLE_MM -#endif -#endif - -#ifdef ENABLE_COMPATIBLE_MM -#undef DUMP_LEAKS -#undef DEBUG_MEMORY -#endif - -#define KB (1024) -#define MB (1024 * KB) -#define MS (1000) - -#define BUF_LEN (100) - -#ifdef ENABLE_GC_DEBUG_TOOLS -size_t get_cur_cnt(void *runtime, void *ptr); -size_t get_del_cnt(void *runtime, void *ptr); -#endif - -#define __exception __attribute__((warn_unused_result)) - -enum JS_CLASS_ID { - /* 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_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) */ -#ifdef CONFIG_BIGNUM - JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ - JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ -#endif - JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ - JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ - JS_CLASS_DATAVIEW, /* u.typed_array */ -#ifdef CONFIG_BIGNUM - JS_CLASS_BIG_INT, /* u.object_data */ - JS_CLASS_BIG_FLOAT, /* u.object_data */ - JS_CLASS_FLOAT_ENV, /* u.float_env */ -#endif - 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_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_WeakRef, - JS_CLASS_FinalizationRegistry, - - JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ -}; - -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 */ -} JSErrorEnum; - -#define BUILD_ASYNC_STACK - -typedef struct JSShape JSShape; -typedef struct JSString JSString; -typedef struct JSString JSAtomStruct; - -typedef struct JSLepusType { - int32_t array_typeid_; - int32_t table_typeid_; - int32_t refcounted_typeid_; - LEPUSClassID refcounted_cid_; -} JSLepusType; - -typedef struct PrimjsCallbacks { - void (*print_by_alog)(char *msg); - int32_t (*js_has_property)(LEPUSContext *, LEPUSValue, JSAtom, int32_t); - int32_t (*js_delete_property)(LEPUSContext *, LEPUSValue, JSAtom, int32_t); - int32_t (*js_get_own_property_names)(LEPUSContext *, LEPUSValue, uint32_t *, - struct LEPUSPropertyEnum **, int32_t); - int32_t (*js_deep_equal_callback)(LEPUSContext *, LEPUSValue, LEPUSValue); - LEPUSValue (*jsarray_push)(LEPUSContext *, LEPUSValue, int32_t, - LEPUSValueConst *, int32_t); - LEPUSValue (*jsarray_pop)(LEPUSContext *, LEPUSValue, int32_t); - int64_t (*jsarray_find)(LEPUSContext *, LEPUSValue, LEPUSValue, int64_t, - int32_t); - LEPUSValue (*jsarray_reverse)(LEPUSContext *, LEPUSValue); - LEPUSValue (*jsarray_slice)(LEPUSContext *, LEPUSValue, size_t, size_t, - size_t, LEPUSValue *, int32_t); -} PrimjsCallbacks; - -typedef struct JSMallocState { - size_t malloc_count; - // - uint64_t malloc_size; - uint64_t malloc_limit; - struct malloc_state allocate_state; - // - void *opaque; /* user opaque */ -} JSMallocState; - -typedef struct LEPUSMallocFunctions { - void *(*lepus_malloc)(JSMallocState *s, size_t size, int alloc_tag); - void (*lepus_free)(JSMallocState *s, void *ptr); - void *(*lepus_realloc)(JSMallocState *s, void *ptr, size_t size, - int alloc_tag); - size_t (*lepus_malloc_usable_size)(const void *ptr); -} LEPUSMallocFunctions; - -#ifdef ENABLE_QUICKJS_DEBUGGER -typedef struct QJSDebuggerCallbacks2 { - // callbacks for quickjs debugger - void (*run_message_loop_on_pause)(LEPUSContext *ctx); - void (*quit_message_loop_on_pause)(LEPUSContext *ctx); - void (*get_messages)(LEPUSContext *ctx); - void (*send_response)(LEPUSContext *ctx, int32_t message_id, - const char *message); - void (*send_notification)(LEPUSContext *ctx, const char *message); - void (*free_messages)(LEPUSContext *ctx, char **messages, int32_t size); - - void (*inspector_check)(LEPUSContext *ctx); - void (*debugger_exception)(LEPUSContext *ctx); - void (*console_message)(LEPUSContext *ctx, int tag, LEPUSValueConst *argv, - int argc); - void (*script_parsed_ntfy)(LEPUSContext *ctx, LEPUSScriptSource *source); - void (*console_api_called_ntfy)(LEPUSContext *ctx, LEPUSValue *msg); - void (*script_fail_parse_ntfy)(LEPUSContext *ctx, LEPUSScriptSource *source); - void (*debugger_paused)(LEPUSContext *ctx, const uint8_t *cur_pc); - uint8_t (*is_devtool_on)(LEPUSRuntime *rt); - void (*send_response_with_view_id)(LEPUSContext *ctx, int32_t message_id, - const char *message, int32_t view_id); - void (*send_ntfy_with_view_id)(LEPUSContext *ctx, const char *message, - int32_t view_id); - void (*script_parsed_ntfy_with_view_id)(LEPUSContext *ctx, - LEPUSScriptSource *source, - int32_t view_id); - void (*script_fail_parse_ntfy_with_view_id)(LEPUSContext *ctx, - LEPUSScriptSource *source, - int32_t view_id); - void (*set_session_enable_state)(LEPUSContext *ctx, int32_t view_id, - int32_t protocol_type); - void (*get_session_state)(LEPUSContext *ctx, int32_t view_id, - bool *is_already_enabled, bool *is_paused); - void (*console_api_called_ntfy_with_rid)(LEPUSContext *ctx, LEPUSValue *msg); - void (*get_session_enable_state)(LEPUSContext *ctx, int32_t view_id, - int32_t protocol_type, bool *ret); - void (*get_console_stack_trace)(LEPUSContext *ctx, LEPUSValue *ret); - void (*on_console_message)(LEPUSContext *ctx, LEPUSValue console_message, - int32_t); -} QJSDebuggerCallbacks2; -#endif - -typedef struct SettingsOption { - /* - If this value is true, quickjs will not adjust stack size - when stack size is unconsistent. - */ - bool disable_adjust_stacksize; - bool disable_json_opt; - bool disable_deepclone_opt; - bool disable_separable_string; -} SettingsOption; -class GlobalHandles; -class GarbageCollector; -class PtrHandles; -class ByteThreadPool; -class napi_handle_scope__; - -struct LEPUSRuntime { - LEPUSMallocFunctions mf; - const char *rt_info; - - // for trace gc - - 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 */ - - int class_count; /* size of class_array */ - LEPUSClass *class_array; - - struct list_head context_list; /* list of LEPUSContext.link */ - /* list of JSGCObjectHeader.link. List of allocated GC objects (used - by the garbage collector) */ - /* list of allocated objects (used by the garbage collector) */ - struct list_head obj_list; /* list of LEPUSObject.link */ - // - struct list_head gc_bytecode_list; - struct list_head gc_obj_list; - // - struct list_head tmp_obj_list; /* used during gc */ - struct list_head free_obj_list; /* used during gc */ - struct list_head *el_next; /* used during gc */ - BOOL in_gc_sweep : 8; - int c_stack_depth; - uint64_t malloc_gc_threshold; - /* stack limitation */ - const uint8_t *stack_top; - size_t stack_size; /* in bytes */ - - LEPUSValue current_exception; - /* true if a backtrace needs to be added to the current exception - (the backtrace generation cannot be done immediately in a bytecode - function) */ - BOOL exception_needs_backtrace; - /* true if inside an out of memory error, to avoid recursing */ - BOOL in_out_of_memory : 8; - - struct LEPUSStackFrame *current_stack_frame; - - LEPUSInterruptHandler *interrupt_handler; - void *interrupt_opaque; - - struct list_head job_list; /* list of JSJobEntry.link */ - - LEPUSModuleNormalizeFunc *module_normalize_func; - LEPUSModuleLoaderFunc *module_loader_func; - void *module_loader_opaque; - - BOOL can_block : 8; /* TRUE if Atomics.wait can block */ - - /* Shape hash table */ - int shape_hash_bits; - int shape_hash_size; - int shape_hash_count; /* number of hashed shapes */ - JSShape **shape_hash; - PrimjsCallbacks primjs_callbacks_; - - struct list_head - unhandled_rejections; // record the first unhandled rejection error - struct list_head async_func_sf; // record all async functions' stack frame. - -#ifdef BUILD_ASYNC_STACK - LEPUSValue *current_micro_task; -#endif - -#ifdef ENABLE_LEPUSNG - LEPUSLepusRefCallbacks js_callbacks_; - JSLepusType js_type_; -#endif - -#ifdef ENABLE_QUICKJS_DEBUGGER - QJSDebuggerCallbacks2 debugger_callbacks_; - int32_t next_script_id; // next script id that can be used -#endif -#ifdef CONFIG_BIGNUM - bf_context_t bf_ctx; -#endif - // -#ifdef ENABLE_PRIMJS_SNAPSHOT - bool use_primjs; -#endif -#ifdef DUMP_LEAKS - struct list_head string_list; /* list of JSString.link */ -#endif - // - void (*update_gc_info)(const char *, int); - char gc_info_start_[BUF_LEN]; - char gc_info_end_[BUF_LEN]; - int64_t init_time; - - ByteThreadPool *workerThreadPool; - GlobalHandles *global_handles_ = nullptr; - - QJSValueValueSpace *qjsvaluevalue_allocator = nullptr; - PtrHandles *ptr_handles; - GarbageCollector *gc; - size_t gc_cnt; - void *mem_for_oom; - bool gc_enable; - bool is_lepusng; - void *user_opaque; - // Primjs begin - SettingsOption settings_option; - // Primjs end - JSMallocState malloc_state; -#ifdef ENABLE_TRACING_GC - LEPUSObject *boilerplateArg0; - LEPUSObject *boilerplateArg1; - LEPUSObject *boilerplateArg2; - LEPUSObject *boilerplateArg3; -#endif -}; - -static const char *const native_error_name[JS_NATIVE_ERROR_COUNT] = { - "EvalError", "RangeError", "ReferenceError", "SyntaxError", - "TypeError", "URIError", "InternalError", "AggregateError"}; - -/* Set/Map/WeakSet/WeakMap */ - -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; - -typedef struct JSUnhandledRejectionEntry { - struct list_head link; - LEPUSValue error; - LEPUSValue promise; -} JSUnhandledRejectionEntry; - -struct LEPUSClass { - uint32_t class_id; /* 0 means free entry */ - JSAtom class_name; - LEPUSClassFinalizer *finalizer; - LEPUSClassGCMark *gc_mark; - LEPUSClassCall *call; - /* pointers for exotic behavior, can be NULL if none are present */ - const LEPUSClassExoticMethods *exotic; -}; - -#define JS_MODE_STRICT (1 << 0) -#define JS_MODE_STRIP (1 << 1) -#define JS_MODE_BIGINT (1 << 2) -#define JS_MODE_MATH (1 << 3) - -typedef struct LEPUSStackFrame { - struct LEPUSStackFrame *prev_frame; /* NULL if first stack frame */ - LEPUSValue - cur_func; /* current function, LEPUS_UNDEFINED if the frame is detached */ - LEPUSValue *arg_buf; /* arguments */ - LEPUSValue *var_buf; /* variables */ - struct list_head var_ref_list; /* list of JSVarRef.link */ - const uint8_t *cur_pc; /* only used in bytecode functions : PC of the - instruction after the call */ - int arg_count; - int js_mode; /* for C functions: 0 */ - /* only used in generators. Current stack pointer value. NULL if - the function is running. */ - LEPUSValue *cur_sp; - LEPUSValue *sp = nullptr; -#ifdef ENABLE_QUICKJS_DEBUGGER - // for debugger: this_obj of the stack frame - LEPUSValue pthis; -#endif - struct JSVarRef **var_refs = nullptr; - uint32_t ref_size = 0; -} LEPUSStackFrame; - -enum TOK { - 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, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW_ASSIGN, -#endif - TOK_POW_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, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW, -#endif - 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 -struct JSString { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - uint32_t len : 31; - uint8_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 : 30; - uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ - uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ -#ifdef DUMP_LEAKS - struct list_head link; /* string list */ -#endif - -#ifdef ENABLE_LEPUSNG - // Primjs add - void *cache_; // add to convert to jsString -#endif - union { - uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ - uint16_t str16[0]; - } u; -}; - -typedef struct JSGCHeader { - uint8_t mark; -} JSGCHeader; - -typedef struct JSVarRef { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ - uint8_t is_arg : 1; - /* - * 0: the VarRef is on the stack. - * 1: the VarRef is detached, pvalue == &value; - */ - uint8_t is_detached : 1; - int var_idx; /* index of the corresponding function variable on - the stack */ - struct list_head link; - LEPUSValue *pvalue; /* pointer to the value, either on the stack or - to 'value' */ - LEPUSValue value; /* used when the variable is no longer on the stack */ -} JSVarRef; - -#ifdef CONFIG_BIGNUM -typedef struct JSFloatEnv { - limb_t prec; - bf_flags_t flags; - unsigned int status; -} JSFloatEnv; - -typedef struct JSBigFloat { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - bf_t num; -} JSBigFloat; - -/* the same structure is used for big integers and big floats. Big - integers are never infinite or NaNs */ -#else -#ifdef ENABLE_LEPUSNG -// -typedef struct JSBigFloat { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - uint64_t num; -} JSBigFloat; - -// -#endif -#endif - -/* 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 -// -#define DEFAULT_VIRTUAL_STACK_SIZE 1024 * 1024 * 4 -#define FALLBACK_VIRTUAL_STACK_SIZE 1024 * 1024 * 1 -#define MINIFY_VIRTUAL_STACK_SIZE 1024 * 1024 * 2 -// - -// -typedef unsigned char u_char; -typedef u_char *address; -// - -typedef enum OPCodeFormat { -#define FMT(f) OP_FMT_##f, -#define DEF(id, size, n_pop, n_push, f) -#include "quickjs/include/quickjs-opcode.h" -#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) -#include "quickjs/include/quickjs-opcode.h" -#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, -#include "quickjs/include/quickjs-opcode.h" -#undef def -#undef DEF -#undef FMT - OP_TEMP_END, - -// The following opcode cannot be dumped into the bianry production. -#define COMPILE_TIME_OPCODE(id, size, n_pop, n_push, f) OP_##id, -#include "quickjs/include/quickjs-opcode.h" -#undef COMPILE_TIME_OPCODE - -} OPCodeEnum; - -struct FinalizationRegistryContext; - -struct LEPUSContext { -#ifdef ENABLE_PRIMJS_SNAPSHOT - address (*dispatch_table)[OP_COUNT]; -#endif -// -#ifndef ALLOCATE_WINDOWS - mstate allocate_state; -#endif - LEPUSRuntime *rt; - struct list_head link; - - uint16_t binary_object_count; - int binary_object_size; - - JSShape *array_shape; /* initial shape for Array objects */ - - LEPUSValue *class_proto; - LEPUSValue function_proto; - LEPUSValue function_ctor; - LEPUSValue regexp_ctor; - LEPUSValue promise_ctor; - LEPUSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; - LEPUSValue iterator_proto; - LEPUSValue async_iterator_proto; - LEPUSValue array_proto_values; - LEPUSValue throw_type_error; - LEPUSValue eval_obj; - - LEPUSValue global_obj; /* global object */ - LEPUSValue global_var_obj; /* contains the global let/const definitions */ - - uint64_t random_state; -#ifdef CONFIG_BIGNUM - bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ - JSFloatEnv fp_env; /* global FP environment */ -#endif - /* when the counter reaches zero, JSRutime.interrupt_handler is called */ - int interrupt_counter; - BOOL is_error_property_enabled; - - struct list_head loaded_modules; /* list of LEPUSModuleDef.link */ - - /* if NULL, RegExp compilation is not supported */ - LEPUSValue (*compile_regexp)(LEPUSContext *ctx, LEPUSValueConst pattern, - LEPUSValueConst flags); - /* if NULL, eval is not supported */ - LEPUSValue (*eval_internal)(LEPUSContext *ctx, LEPUSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int flags, int scope_idx, - bool debugger_eval, LEPUSStackFrame *sf); - - void *user_opaque; - // - int64_t napi_env; - BOOL no_lepus_strict_mode; -#if defined(__APPLE__) && !defined(GEN_ANDROID_EMBEDDED) - uint32_t stack_pos; - uint8_t *stack; -#endif -#ifdef ENABLE_QUICKJS_DEBUGGER - LEPUSDebuggerInfo *debugger_info; // structure for quickjs debugger -#endif - uint32_t next_function_id; // for lepusng debugger encode. - uint8_t - debuginfo_outside; // for lepusng debugger encode to avoid break change. - char *lynx_target_sdk_version; - BOOL debugger_mode; - BOOL debugger_parse_script; // for shared context debugger - BOOL debugger_need_polling; - BOOL console_inspect; - // - - PtrHandles *ptr_handles; - napi_handle_scope__ *napi_scope; - bool gc_enable; - bool is_lepusng; - uint64_t binary_version; - struct FinalizationRegistryContext *fg_ctx = nullptr; -}; - -typedef union JSFloat64Union { - double d; - uint64_t u64; - uint32_t u32[2]; -} JSFloat64Union; - -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; - -#define JS_ATOM_HASH_MASK ((1 << 30) - 1) - -typedef struct LEPUSClosureVar { - uint8_t is_local : 1; - uint8_t is_arg : 1; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* 9 bits available */ - uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the - parent function. otherwise: index to a closure - variable of the parent function */ - JSAtom var_name; -} LEPUSClosureVar; - -#define ARG_SCOPE_INDEX 1 -#define ARG_SCOPE_END (-2) -#define DEBUG_SCOPE_INDEX (-3) - -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, - 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; - -typedef struct JSVarDef { - JSAtom var_name; - int scope_level; /* index into fd->scopes of this variable lexical scope */ - int scope_next; /* index into fd->vars of the next variable in the - * same or enclosing lexical scope */ - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t is_captured : 1; - uint8_t var_kind : 4; /* see JSVarKindEnum */ - int func_pool_idx : 24; /* only used during compilation */ -} 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) - -// -#define LINE_NUMBER_BITS_COUNT 24 -#define COLUMN_NUMBER_BITS_COUNT 40 -// for compatibility -#define OLD_LINE_NUMBER_BITS_COUNT 12 -// use 2 bits for type. -#define LINE_COLUMN_TYPE_SHIFT 62 -// - -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; - -// -enum class EntryMode { INTERPRETER, BASELINE }; - -#define JIT_THRESHOLD 6 - -// - -// settings key opt - -enum { - PRIMJS_SNAPSHOT_ENABLE = 0b000000001, - JSON_OPT_DISABLE = 0b000000010, - GC_INFO_ENABLE = 0b000000100, - DEEPCLONE_OPT_DISABLE = 0b000001000, - LEPUSNG_HEAP_20 = 0b000010000, - LEPUSNG_HEAP_24 = 0b000100000, - DISABLE_ADJUST_STACKSIZE = 0b001000000, - DISABLE_SEPARABLE_STRING = 0b010000000, - GC_ENABLE = 0b100000000, - EFFECT_ENABLE = 0b1000000000, - MINIFY_STACK_ENABLE = 0b10000000000, - ENABLE_LEPUSNG_STRAGETY = 0b100000000000, - LEPUSNG_HEAP_12 = 0b1000000000000, - LEPUSNG_GC_DISABLE = 0b10000000000000, -}; - -inline int settingsFlag = 0; -// settings key opt end - -typedef struct CallerStrSlot { - uint32_t pc; - uint32_t size : 31; - uint32_t is_str : 1; - union { - const char *str; - uint32_t off; - }; -} CallerStrSlot; - -typedef struct LEPUSFunctionBytecode { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after header, 8-bit */ - uint8_t js_mode; - 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 has_debug : 1; - uint8_t read_only_bytecode : 1; - /* XXX: 4 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) */ - LEPUSClosureVar - *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 */ - LEPUSValue *cpool; /* constant pool (self pointer) */ - int cpool_count; - int closure_var_count; - -#ifdef ENABLE_QUICKJS_DEBUGGER - DebuggerFuncLevelState func_level_state; - struct list_head link; /*ctx->debugger_info->bytecode_list*/ - LEPUSScriptSource *script; - int32_t bp_num; -#endif - // - struct list_head gc_link; - uint32_t function_id; // for lepusNG debugger encode - // - struct { - /* debug info, move to separate structure to save memory? */ - JSAtom filename; - int line_num; - int source_len; - int pc2line_len; -#ifdef ENABLE_QUICKJS_DEBUGGER - int64_t column_num; -#endif - uint8_t *pc2line_buf; - char *source; - struct list_head link; - // for cpu profiler to use. - JSString *file_name; - JSString *func_name; - - CallerStrSlot *caller_slots; - size_t caller_size; - // end. - } debug; - // ATTENTION: NEW MEMBERS MUST BE ADDED IN FRONT OF DEBUG FIELD! -} LEPUSFunctionBytecode; - -typedef struct JSBoundFunction { - LEPUSValue func_obj; - LEPUSValue this_val; - int argc; - LEPUSValue argv[0]; -} JSBoundFunction; - -typedef enum JSIteratorKindEnum { - JS_ITERATOR_KIND_KEY, - JS_ITERATOR_KIND_VALUE, - JS_ITERATOR_KIND_KEY_AND_VALUE, -} JSIteratorKindEnum; - -typedef struct JSForInIterator { - LEPUSValue 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 { - LEPUSValue target; - LEPUSValue handler; - LEPUSValue proto; - uint8_t is_func; - uint8_t is_revoked; -} JSProxyData; - -typedef struct JSArrayBuffer { - int byte_length; /* 0 if detached */ - uint8_t detached; - uint8_t shared; /* if shared, the array buffer cannot be detached */ - uint8_t *data; /* NULL if detached */ - struct list_head array_list; - void *opaque; - LEPUSFreeArrayBufferDataFunc *free_func; - BOOL from_js_heap; -} JSArrayBuffer; - -typedef struct JSTypedArray { - struct list_head link; /* link to arraybuffer */ - LEPUSObject *obj; /* back pointer to the TypedArray/DataView object */ - LEPUSObject *buffer; /* based array buffer */ - uint32_t offset; /* offset in the array buffer */ - uint32_t length; /* length in the array buffer */ -} JSTypedArray; - -typedef struct JSAsyncFunctionState { - LEPUSValue this_val; /* 'this' generator argument */ - int argc; /* number of function arguments */ - BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ - LEPUSStackFrame frame; - list_head link; -#ifdef ENABLE_PRIMJS_SNAPSHOT - LEPUSValue *_arg_buf; -#endif -} JSAsyncFunctionState; - -/* XXX: could use an object instead to avoid the - LEPUS_TAG_ASYNC_FUNCTION tag for the GC */ -typedef struct JSAsyncFunctionData { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ - LEPUSValue resolving_funcs[2]; - BOOL is_active; /* true if the async function state is valid */ - JSAsyncFunctionState func_state; -} JSAsyncFunctionData; - -typedef struct JSReqModuleEntry { - JSAtom module_name; - LEPUSModuleDef *module; /* used using resolution */ -} 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; - -struct LEPUSModuleDef { - LEPUSRefCountHeader 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; - - LEPUSValue module_ns; - LEPUSValue func_obj; /* only used for LEPUS modules */ - LEPUSModuleInitFunc *init_func; /* only used for C modules */ - BOOL resolved : 8; - BOOL instantiated : 8; - BOOL evaluated : 8; - BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ - /* true if evaluation yielded an exception. It is saved in - eval_exception */ - BOOL eval_has_exception : 8; - LEPUSValue eval_exception; -}; - -typedef struct JSJobEntry { - struct list_head link; - LEPUSContext *ctx; - LEPUSJobFunc *job_func; - int argc; - LEPUSValue argv[0]; -} JSJobEntry; - -typedef enum { - WEAK_REF_KIND_WEAK_MAP, - WEAK_REF_KIND_WEAK_REF, - WEAK_REF_KIND_FINALIZATION_REGISTRY, -} WeakRefRecordKind; - -typedef struct FinalizationRegistryContext { - int32_t ref_count; - LEPUSContext *ctx; -} FinalizationRegistryContext; - -typedef struct FinalizationRegistryData { - struct FinalizationRegistryContext *fg_ctx; - list_head entries; - LEPUSValue cbs; -} FinalizationRegistryData; - -typedef struct FinalizationRegistryEntry { - struct list_head link; - FinalizationRegistryData *data; // FinalizationRegistryObj - LEPUSValue target; // registerd object - LEPUSValue held_value; - LEPUSValue token; -} FinalizationRegistryEntry; - -typedef struct WeakRefData { - LEPUSValue target; -} WeakRefData; - -typedef struct WeakRefRecord { - WeakRefRecordKind kind; - struct WeakRefRecord *next_weak_ref; - union { - JSMapRecord *map_record; - FinalizationRegistryEntry *fin_node; - WeakRefData *weak_ref; - void *ptr; - } u; -} WeakRefRecord; - -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; - -typedef struct JSProperty { - union { - LEPUSValue value; /* LEPUS_PROP_NORMAL */ - struct { /* LEPUS_PROP_GETSET */ - LEPUSObject *getter; /* NULL if undefined */ - LEPUSObject *setter; /* NULL if undefined */ - } getset; - JSVarRef *var_ref; /* LEPUS_PROP_VARREF */ - struct { /* LEPUS_PROP_AUTOINIT */ - LEPUSValue (*init_func)(LEPUSContext *ctx, LEPUSObject *obj, JSAtom prop, - void *opaque); - 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 { - uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1 - before the start of the structure. */ - LEPUSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ - /* true if the shape is inserted in the shape hash table. If not, - JSShape.hash is not valid */ - uint8_t is_hashed; - /* If true, the shape may have small array index properties 'n' with 0 - <= n <= 2^31-1. If false, the shape is guaranteed not to have - small array index properties */ - uint8_t has_small_array_index; - uint32_t hash; /* current hash value */ - uint32_t prop_hash_mask; - int prop_size; /* allocated properties */ - int prop_count; - JSShape *shape_hash_next; /* in LEPUSRuntime.shape_hash[h] list */ - LEPUSObject *proto; - JSShapeProperty prop[0]; /* prop_size elements */ -}; -struct LEPUSObject { - LEPUSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ - 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 */ - 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 is_class : 1; /* TRUE if object is a class constructor */ - uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ - uint16_t class_id; /* see JS_CLASS_x */ - /* byte offsets: 8/8 */ - struct list_head link; /* object list */ - /* byte offsets: 16/24 */ - JSShape *shape; /* prototype and property names + flag */ - JSProperty *prop; /* array of properties */ - /* byte offsets: 24/40 */ - struct WeakRefRecord - *first_weak_ref; /* XXX: use a bit and an external hash table? */ - /* 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 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 */ -#ifdef CONFIG_BIGNUM - struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ -#endif - 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 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 FinalizationRegistryData - *fin_reg_data; // JS_CLASS_FinalizationRegistry - struct WeakRefData *weak_ref_data; - 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 LEPUSFunctionBytecode *function_bytecode; - JSVarRef **var_refs; - LEPUSObject *home_object; /* for 'super' access */ - } func; - struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ - LEPUSCFunctionType 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_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - union { - uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - struct JSTypedArray - *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY - */ - } u1; - union { - LEPUSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_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 */ - 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 */ - LEPUSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ - } u; - /* byte sizes: 40/48/72 */ -}; - -constexpr const char *lepusjs_filename = "file://lepus.js"; -constexpr const char *lepusng_functionid_str = "__lepusNG_function_id__"; - -enum { - JS_ATOM_NULL, -#define DEF(name, str) JS_ATOM_##name, -#include "quickjs/include/quickjs-atom.h" -#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" -#include "quickjs/include/quickjs-atom.h" -#undef DEF - ; - -typedef struct JSOpCode { -#if defined(ENABLE_PRIMJS_SNAPSHOT) || defined(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; - -extern const JSOpCode opcode_info[]; - -#if SHORT_OPCODES -/* 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)] -#else -#define short_opcode_info(op) opcode_info[op] -#endif - -// - -#if defined(ANDROID) || defined(__ANDROID__) -#include - -#define LOG_TAG "primjs" - -#ifndef LOGD -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#endif -#ifndef LOGE -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#endif -#ifndef LOGI -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#endif -#endif - -#define PRINT_LOG_TO_FILE 0 - -#ifdef ENABLE_PRIMJS_TRACE -#if defined(ANDROID) || defined(__ANDROID__) -#if PRINT_LOG_TO_FILE -extern FILE *log_f; -#define PRIM_LOG(...) \ - do { \ - fprintf(log_f, __VA_ARGS__); \ - fflush(log_f); \ - } while (0) -#else -#define PRIM_LOG(...) \ - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#endif // PRINT_LOG_TO_FILE -#else -#define PRIM_LOG printf -#endif // ANDROID -#else -#define PRIM_LOG(...) -#endif // ENABLE_PRIMJS_TRACE - -#define OP_DEFINE_METHOD_METHOD 0 -#define OP_DEFINE_METHOD_GETTER 1 -#define OP_DEFINE_METHOD_SETTER 2 -#define OP_DEFINE_METHOD_ENUMERABLE 4 - -#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 LEPUS_CALL_FLAG_CONSTRUCTOR (1 << 0) -#define JS_CALL_FLAG_COPY_ARGV (1 << 1) -#define JS_CALL_FLAG_GENERATOR (1 << 2) - -#define __exception __attribute__((warn_unused_result)) - -/* 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) - -/* 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, -} OPSpecialObjectEnum; - -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 - -#define HINT_STRING 0 -#define HINT_NUMBER 1 -#define HINT_NONE 2 -#ifdef CONFIG_BIGNUM -#define HINT_INTEGER 3 -#endif -/* don't try Symbol.toPrimitive */ -#define HINT_FORCE_ORDINARY (1 << 4) - -#define prim_abort() \ - { \ - printf("[%s:%d] Abort\n", __FILE__, __LINE__); \ - abort(); \ - } - -#ifdef __cplusplus -extern "C" { -#endif - -inline void *system_malloc(size_t size) { - void *ptr; - ptr = malloc(size); - if (!ptr) return NULL; - return ptr; -} -inline void *system_mallocz(size_t size) { - void *ptr; - ptr = system_malloc(size); - if (!ptr) return NULL; - return memset(ptr, 0, size); -} -inline __attribute__((unused)) void *system_realloc(void *ptr, size_t size) { - if (!ptr) { - if (size == 0) return NULL; - return system_malloc(size); - } - if (size == 0) { - free(ptr); - return NULL; - } - - ptr = realloc(ptr, size); - if (!ptr) return NULL; - return ptr; -} -inline void system_free(void *ptr) { - if (!ptr) return; - free(ptr); -} - -typedef struct JSSeparableString { - LEPUSRefCountHeader header; /* ref count for gc*/ - uint32_t len : 31; - uint8_t is_wide_char : 1; - uint32_t depth; - LEPUSValue left_op; - LEPUSValue right_op; - LEPUSValue flat_content; -} JSSeparableString; - -class CStack { - using element = JSSeparableString *; - element *base_ = nullptr; - element *top_ = nullptr; - size_t stack_size_ = 0; - LEPUSRuntime *runtime_ = nullptr; - static constexpr size_t STACK_INIT_SIZE = 64; - - public: - explicit CStack(LEPUSRuntime *rt, uint32_t depth = STACK_INIT_SIZE) { - runtime_ = rt; - base_ = static_cast(system_malloc(sizeof(element) * depth)); - if (!base_) return; - top_ = base_; - stack_size_ = depth; - } - - ~CStack() { system_free(base_); } - - void Push(element node) { - if (static_cast(top_ - base_) >= stack_size_) { - base_ = static_cast( - system_realloc(base_, 2 * stack_size_ * sizeof(element))); - if (!base_) { - stack_size_ = 0; - top_ = nullptr; - return; - } - top_ = base_ + stack_size_; - stack_size_ *= 2; - } - *top_++ = node; - } - - void Pop() { - if (!Empty()) { - --top_; - } - } - - element Top() { - if (!Empty()) { - return *(top_ - 1); - } - return nullptr; - } - - int32_t Empty() { return top_ == base_; } -}; - -QJS_STATIC inline int32_t JS_IsSeparableString(LEPUSValue val) { - return LEPUS_VALUE_IS_SEPARABLE_STRING(val); -} - -QJS_STATIC inline JSSeparableString *JS_GetSeparableString(LEPUSValue val) { - return reinterpret_cast(LEPUS_VALUE_GET_PTR(val)); -} - -LEPUSValue JS_GetSeparableStringContent(LEPUSContext *ctx, LEPUSValue val); -LEPUSValue JS_GetSeparableStringContentNotDup(LEPUSContext *ctx, - LEPUSValue val); - -QJS_HIDE LEPUSValue JS_GetPropertyInternalImpl(LEPUSContext *ctx, - LEPUSValueConst obj, JSAtom prop, - LEPUSValueConst this_obj, - BOOL throw_ref_error); -QJS_HIDE LEPUSValue JS_GetPropertyInternalImpl_GC(LEPUSContext *ctx, - LEPUSValueConst obj, - JSAtom prop, - LEPUSValueConst this_obj, - BOOL throw_ref_error); -QJS_HIDE void prim_js_print(const char *msg); -QJS_HIDE void prim_js_print_gc(const char *msg); - -QJS_HIDE void prim_js_print_register(uint64_t reg_val); - -QJS_HIDE void prim_js_print_trace(int bytecode, int tos); -QJS_HIDE void prim_js_print_trace_gc(int bytecode, int tos); - -QJS_HIDE void prim_js_print_func(LEPUSContext *ctx, LEPUSValue func_obj); -QJS_HIDE void prim_js_print_func_gc(LEPUSContext *ctx, LEPUSValue func_obj); - -QJS_HIDE void JS_FreeValueRef(LEPUSContext *ctx, LEPUSValue v); - -QJS_HIDE LEPUSValue js_closure(LEPUSContext *ctx, LEPUSValue bfunc, - JSVarRef **cur_var_refs, LEPUSStackFrame *sf); -LEPUSValue js_closure_gc(LEPUSContext *ctx, LEPUSValue bfunc, - JSVarRef **cur_var_refs, LEPUSStackFrame *sf); - -QJS_HIDE void DebuggerPause(LEPUSContext *ctx, LEPUSValue val, - const uint8_t *pc); - -QJS_HIDE void DebuggerCallEachOp(LEPUSContext *ctx, const uint8_t *pc, - LEPUSFunctionBytecode *b); - -QJS_HIDE void DebuggerCallEachFunc(LEPUSContext *ctx, const uint8_t *pc); - -QJS_HIDE LEPUSValue __JS_AtomToValue(LEPUSContext *ctx, JSAtom atom, - BOOL force_string); -QJS_HIDE LEPUSValue __JS_AtomToValue_GC(LEPUSContext *ctx, JSAtom atom, - BOOL force_string); -QJS_HIDE BOOL js_check_stack_overflow(LEPUSContext *ctx, size_t alloca_size); -QJS_HIDE LEPUSValue JS_ThrowStackOverflow(LEPUSContext *ctx); -QJS_HIDE LEPUSValue JS_ThrowStackOverflow_GC(LEPUSContext *ctx); -QJS_HIDE void build_backtrace( - LEPUSContext *ctx, LEPUSValueConst error_obj, const char *filename, - /* */ int64_t line_num, /* */ - const uint8_t *cur_pc, int backtrace_flags, uint8_t is_parse_error = 0); -QJS_HIDE LEPUSValue JS_NewSymbolFromAtom(LEPUSContext *ctx, JSAtom descr, - int atom_type); -QJS_HIDE LEPUSValue JS_NewSymbolFromAtom_GC(LEPUSContext *ctx, JSAtom descr, - int atom_type); -QJS_HIDE LEPUSValue JS_ToObject_GC(LEPUSContext *ctx, LEPUSValueConst val); -QJS_HIDE LEPUSValue PRIM_JS_NewObject(LEPUSContext *ctx); -QJS_HIDE LEPUSValue PRIM_JS_NewObject_GC(LEPUSContext *ctx); -QJS_HIDE LEPUSValue js_build_arguments(LEPUSContext *ctx, int argc, - LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_build_arguments_gc(LEPUSContext *ctx, int argc, - LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_build_mapped_arguments(LEPUSContext *ctx, int argc, - LEPUSValueConst *argv, - LEPUSStackFrame *sf, - int arg_count); -QJS_HIDE LEPUSValue js_build_mapped_arguments_gc(LEPUSContext *ctx, int argc, - LEPUSValueConst *argv, - LEPUSStackFrame *sf, - int arg_count); -QJS_HIDE void prim_close_var_refs(LEPUSContext *ctx, LEPUSStackFrame *sf); -QJS_HIDE void prim_close_var_refs_gc(LEPUSContext *ctx, LEPUSStackFrame *sf); -QJS_HIDE LEPUSValue js_build_rest(LEPUSContext *ctx, int first, int argc, - LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_build_rest_gc(LEPUSContext *ctx, int first, int argc, - LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_function_apply(LEPUSContext *ctx, - LEPUSValueConst this_val, int argc, - LEPUSValueConst *argv, int magic); -QJS_HIDE LEPUSValue js_function_apply_gc(LEPUSContext *ctx, - LEPUSValueConst this_val, int argc, - LEPUSValueConst *argv, int magic); -QJS_HIDE int JS_CheckBrand(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst func); -QJS_HIDE int JS_CheckBrand_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst func); -QJS_HIDE int JS_AddBrand(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst home_obj); -QJS_HIDE int JS_AddBrand_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst home_obj); -QJS_HIDE int JS_ThrowTypeErrorReadOnly(LEPUSContext *ctx, int flags, - JSAtom atom); -QJS_HIDE LEPUSValue JS_ThrowSyntaxErrorVarRedeclaration(LEPUSContext *ctx, - JSAtom prop); -QJS_HIDE LEPUSValue JS_ThrowSyntaxErrorVarRedeclaration_GC(LEPUSContext *ctx, - JSAtom prop); -QJS_HIDE LEPUSValue JS_ThrowReferenceErrorUninitialized(LEPUSContext *ctx, - JSAtom name); -QJS_HIDE LEPUSValue JS_ThrowReferenceErrorUninitialized_GC(LEPUSContext *ctx, - JSAtom name); -QJS_HIDE int JS_IteratorClose(LEPUSContext *ctx, LEPUSValueConst enum_obj, - BOOL is_exception_pending); - -QJS_HIDE LEPUSValue JS_CallConstructorInternal(LEPUSContext *ctx, - LEPUSValueConst func_obj, - LEPUSValueConst new_target, - int argc, LEPUSValue *argv, - int flags); -QJS_HIDE LEPUSValue JS_CallConstructorInternal_GC(LEPUSContext *ctx, - LEPUSValueConst func_obj, - LEPUSValueConst new_target, - int argc, LEPUSValue *argv, - int flags); -QJS_HIDE int JS_CheckGlobalVar(LEPUSContext *ctx, JSAtom prop); -QJS_HIDE int JS_CheckGlobalVar_GC(LEPUSContext *ctx, JSAtom prop); -QJS_HIDE LEPUSValue JS_GetPropertyValue(LEPUSContext *ctx, - LEPUSValueConst this_obj, - LEPUSValue prop); - -QJS_HIDE LEPUSValue JS_GetPropertyValue_GC(LEPUSContext *ctx, - LEPUSValueConst this_obj, - LEPUSValue prop); -QJS_HIDE int JS_DefineGlobalVar(LEPUSContext *ctx, JSAtom prop, int def_flags); -QJS_HIDE int JS_DefineGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, - int def_flags); -QJS_HIDE LEPUSValue PRIM_JS_NewArray(LEPUSContext *ctx); -QJS_HIDE LEPUSValue PRIM_JS_NewArray_GC(LEPUSContext *ctx); - -QJS_HIDE LEPUSValue js_regexp_constructor_internal(LEPUSContext *ctx, - LEPUSValueConst ctor, - LEPUSValue pattern, - LEPUSValue bc); -QJS_HIDE LEPUSValue js_regexp_constructor_internal_gc(LEPUSContext *ctx, - LEPUSValueConst ctor, - LEPUSValue pattern, - LEPUSValue bc); -QJS_HIDE int JS_SetPropertyValue(LEPUSContext *ctx, LEPUSValueConst this_obj, - LEPUSValue prop, LEPUSValue val, int flags); -QJS_HIDE int JS_SetPropertyValue_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - LEPUSValue prop, LEPUSValue val, int flags); -QJS_HIDE int JS_CheckDefineGlobalVar(LEPUSContext *ctx, JSAtom prop, int flags); -QJS_HIDE int JS_CheckDefineGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, - int flags); -QJS_HIDE int JS_DefineGlobalFunction(LEPUSContext *ctx, JSAtom prop, - LEPUSValueConst func, int def_flags); -QJS_HIDE int JS_DefineGlobalFunction_GC(LEPUSContext *ctx, JSAtom prop, - LEPUSValueConst func, int def_flags); -QJS_HIDE LEPUSValue JS_GetPrivateField(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst name); -QJS_HIDE LEPUSValue JS_GetPrivateField_GC(LEPUSContext *ctx, - LEPUSValueConst obj, - LEPUSValueConst name); -QJS_HIDE int JS_DefinePrivateField(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst name, LEPUSValue val); -QJS_HIDE int JS_DefinePrivateField_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst name, LEPUSValue val); -QJS_HIDE int JS_DefineObjectName(LEPUSContext *ctx, LEPUSValueConst obj, - JSAtom name, int flags); -QJS_HIDE int JS_DefineObjectNameComputed(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst str, int flags); -QJS_HIDE int JS_DefineObjectNameComputed_GC(LEPUSContext *ctx, - LEPUSValueConst obj, - LEPUSValueConst str, int flags); -QJS_HIDE int JS_SetPrototypeInternal(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst proto_val, - BOOL throw_flag); -QJS_HIDE int JS_SetPrototypeInternal_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst proto_val, - BOOL throw_flag); -QJS_HIDE void js_method_set_home_object(LEPUSContext *ctx, - LEPUSValueConst func_obj, - LEPUSValueConst home_obj); -QJS_HIDE void js_method_set_home_object_gc(LEPUSContext *ctx, - LEPUSValueConst func_obj, - LEPUSValueConst home_obj); -QJS_HIDE int JS_DefinePropertyValueValue(LEPUSContext *ctx, - LEPUSValueConst this_obj, - LEPUSValue prop, LEPUSValue val, - int flags); -QJS_HIDE int JS_DefinePropertyValueValue_GC(LEPUSContext *ctx, - LEPUSValueConst this_obj, - LEPUSValue prop, LEPUSValue val, - int flags); -QJS_HIDE __exception int js_append_enumerate(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE __exception int js_append_enumerate_gc(LEPUSContext *ctx, - LEPUSValue *sp); -QJS_HIDE int js_method_set_properties(LEPUSContext *ctx, - LEPUSValueConst func_obj, JSAtom name, - int flags, LEPUSValueConst home_obj); -QJS_HIDE int js_method_set_properties_gc(LEPUSContext *ctx, - LEPUSValueConst func_obj, JSAtom name, - int flags, LEPUSValueConst home_obj); -QJS_HIDE int prim_js_copy_data_properties(LEPUSContext *ctx, LEPUSValue *sp, - int mask); -QJS_HIDE int prim_js_copy_data_properties_gc(LEPUSContext *ctx, LEPUSValue *sp, - int mask); -QJS_HIDE int JS_ToBoolFree(LEPUSContext *ctx, LEPUSValue val); -QJS_HIDE int JS_ToBoolFree_GC(LEPUSContext *ctx, LEPUSValue val); -QJS_HIDE int js_op_define_class(LEPUSContext *ctx, LEPUSValue *sp, - JSAtom class_name, int class_flags, - JSVarRef **cur_var_refs, LEPUSStackFrame *sf); -QJS_HIDE void close_lexical_var(LEPUSContext *ctx, LEPUSStackFrame *sf, - int idx); -QJS_HIDE int JS_SetPropertyGeneric(LEPUSContext *ctx, LEPUSObject *p, - JSAtom prop, LEPUSValue val, - LEPUSValueConst this_obj, int flags); -QJS_HIDE int JS_SetPropertyGeneric_GC(LEPUSContext *ctx, LEPUSObject *p, - JSAtom prop, LEPUSValue val, - LEPUSValueConst this_obj, int flags); -QJS_HIDE int JS_SetPrivateField(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst name, LEPUSValue val); -QJS_HIDE int JS_SetPrivateField_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst name, LEPUSValue val); -QJS_HIDE LEPUSValue JS_ThrowTypeErrorNotAnObject(LEPUSContext *ctx); -QJS_HIDE int prim_js_with_op_gc(LEPUSContext *ctx, LEPUSValue *sp, JSAtom atom, - int is_with, int opcode); -QJS_HIDE JSVarRef *get_var_ref(LEPUSContext *ctx, LEPUSStackFrame *sf, - int var_idx, BOOL is_arg); -QJS_HIDE JSVarRef *get_var_ref_gc(LEPUSContext *ctx, LEPUSStackFrame *sf, - int var_idx, BOOL is_arg); -QJS_HIDE void free_var_ref(LEPUSRuntime *rt, JSVarRef *var_ref); - -QJS_HIDE JSProperty *add_property(LEPUSContext *ctx, LEPUSObject *p, - JSAtom prop, int prop_flags); -QJS_HIDE JSProperty *add_property_gc(LEPUSContext *ctx, LEPUSObject *p, - JSAtom prop, int prop_flags); -QJS_HIDE LEPUSValue prim_js_op_eval(LEPUSContext *ctx, int scope_idx, - LEPUSValue op1); -QJS_HIDE LEPUSValue prim_js_op_eval_gc(LEPUSContext *ctx, int scope_idx, - LEPUSValue op1); -QJS_HIDE int prim_js_with_op(LEPUSContext *ctx, LEPUSValue *sp, JSAtom atom, - int is_with, int opcode); - -QJS_HIDE LEPUSValue JS_GetGlobalVarImpl(LEPUSContext *ctx, JSAtom prop, - BOOL throw_ref_error); -QJS_HIDE LEPUSValue JS_GetGlobalVarImpl_GC(LEPUSContext *ctx, JSAtom prop, - BOOL throw_ref_error); -QJS_HIDE int JS_GetGlobalVarRef(LEPUSContext *ctx, JSAtom prop, LEPUSValue *sp); -QJS_HIDE int JS_GetGlobalVarRef_GC(LEPUSContext *ctx, JSAtom prop, - LEPUSValue *sp); -QJS_HIDE LEPUSValue prim_js_for_in_start(LEPUSContext *ctx, LEPUSValue op); -QJS_HIDE LEPUSValue prim_js_for_in_start_gc(LEPUSContext *ctx, LEPUSValue op); -QJS_HIDE int js_for_in_next(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int js_for_in_next_gc(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int js_for_await_of_next(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int js_for_await_of_next_gc(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int js_iterator_get_value_done(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int js_iterator_get_value_done_gc(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int js_for_of_start(LEPUSContext *ctx, LEPUSValue *sp, BOOL is_async); -QJS_HIDE int js_for_of_start_gc(LEPUSContext *ctx, LEPUSValue *sp, - BOOL is_async); -QJS_HIDE int js_for_of_next(LEPUSContext *ctx, LEPUSValue *sp, int offset); -QJS_HIDE int js_for_of_next_gc(LEPUSContext *ctx, LEPUSValue *sp, int offset); -QJS_HIDE LEPUSValue *prim_js_iterator_close_return(LEPUSContext *ctx, - LEPUSValue *sp, - LEPUSValue *stack_buf); -QJS_HIDE LEPUSValue *prim_js_iterator_close_return_gc(LEPUSContext *ctx, - LEPUSValue *sp); -QJS_HIDE int prim_js_async_iterator_close(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int prim_js_async_iterator_close_gc(LEPUSContext *ctx, LEPUSValue *sp); -QJS_HIDE int prim_js_async_iterator_get(LEPUSContext *ctx, LEPUSValue *sp, - int flags); -QJS_HIDE int prim_js_async_iterator_get_gc(LEPUSContext *ctx, LEPUSValue *sp, - int flags); -QJS_HIDE LEPUSValue primjs_get_super_ctor(LEPUSContext *ctx, LEPUSValue op); -QJS_HIDE LEPUSValue primjs_get_super_ctor_gc(LEPUSContext *ctx, LEPUSValue op); -QJS_HIDE int prim_js_iterator_call(LEPUSContext *ctx, LEPUSValue *sp, - int flags); - -QJS_HIDE LEPUSValue JS_ToPrimitiveFree(LEPUSContext *ctx, LEPUSValue val, - int hint); -QJS_HIDE LEPUSValue JS_ToPrimitiveFree_GC(LEPUSContext *ctx, LEPUSValue val, - int hint); -QJS_HIDE LEPUSValue JS_ConcatString(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue JS_ConcatString_GC(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_unary_arith_slow(LEPUSContext *ctx, LEPUSValue op1, - OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_unary_arith_slow_gc(LEPUSContext *ctx, - LEPUSValue op1, OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_add_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_add_slow_gc(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE no_inline LEPUSValue prim_js_not_slow(LEPUSContext *ctx, - LEPUSValue op); -QJS_HIDE no_inline LEPUSValue prim_js_not_slow_gc(LEPUSContext *ctx, - LEPUSValue op); -QJS_HIDE LEPUSValue prim_js_binary_arith_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_binary_arith_slow_gc(LEPUSContext *ctx, - LEPUSValue op1, LEPUSValue op2, - OPCodeEnum op); -QJS_HIDE double prim_js_fmod_double(double a, double b); -QJS_HIDE double prim_js_fmod_double_gc(double a, double b); -QJS_HIDE int js_post_inc_slow(LEPUSContext *ctx, LEPUSValue *sp, OPCodeEnum op); -QJS_HIDE int js_post_inc_slow_gc(LEPUSContext *ctx, LEPUSValue *sp, - OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_binary_logic_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_binary_logic_slow_gc(LEPUSContext *ctx, - LEPUSValue op1, LEPUSValue op2, - OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_shr_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_shr_slow_gc(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_relation_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_relation_slow_gc(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, OPCodeEnum op); -QJS_HIDE LEPUSValue prim_js_eq_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, int is_neq); -QJS_HIDE LEPUSValue prim_js_eq_slow_gc(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, int is_neq); -QJS_HIDE LEPUSValue prim_js_strict_eq_slow(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, BOOL is_neq); -QJS_HIDE LEPUSValue prim_js_strict_eq_slow_gc(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2, BOOL is_neq); -QJS_HIDE LEPUSValue prim_js_operator_instanceof(LEPUSContext *ctx, - LEPUSValue op1, LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_operator_instanceof_gc(LEPUSContext *ctx, - LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_operator_in(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_operator_in_gc(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE __exception int js_operator_typeof(LEPUSContext *ctx, LEPUSValue op1); -QJS_HIDE __exception int js_operator_typeof_gc(LEPUSContext *ctx, - LEPUSValue op1); -QJS_HIDE LEPUSValue prim_js_operator_delete(LEPUSContext *ctx, LEPUSValue op1, - LEPUSValue op2); -QJS_HIDE LEPUSValue prim_js_operator_delete_gc(LEPUSContext *ctx, - LEPUSValue op1, LEPUSValue op2); -QJS_HIDE int JS_SetPropertyInternalImpl(LEPUSContext *ctx, - LEPUSValueConst this_obj, JSAtom prop, - LEPUSValue val, int flags); -QJS_HIDE int JS_SetPropertyInternalImpl_GC(LEPUSContext *ctx, - LEPUSValueConst this_obj, - JSAtom prop, LEPUSValue val, - int flags); - -QJS_HIDE int set_array_length(LEPUSContext *ctx, LEPUSObject *p, - JSProperty *prop, LEPUSValue val, int flags); - -QJS_HIDE BOOL JS_IsUncatchableError(LEPUSContext *ctx, LEPUSValueConst val); -QJS_HIDE BOOL JS_IsUncatchableError_GC(LEPUSContext *ctx, LEPUSValueConst val); -QJS_HIDE JSAtom js_value_to_atom(LEPUSContext *ctx, LEPUSValueConst val); -QJS_HIDE JSAtom js_value_to_atom_gc(LEPUSContext *ctx, LEPUSValueConst val); -QJS_HIDE LEPUSValue JS_ThrowReferenceErrorNotDefined(LEPUSContext *ctx, - JSAtom name); -QJS_HIDE LEPUSValue JS_ThrowReferenceErrorNotDefined_GC(LEPUSContext *ctx, - JSAtom name); -LEPUSValue JS_ThrowTypeErrorNotFunction(LEPUSContext *ctx); - -#ifdef ENABLE_PRIMJS_SNAPSHOT -typedef LEPUSValue (*QuickJsCallStub)(LEPUSValue this_arg, - LEPUSValue new_target, - LEPUSValue func_obj, address entry_point, - int argc, LEPUSValue *argv, int flags); - -extern QuickJsCallStub entry; -QJS_HIDE void compile_function(LEPUSContext *, LEPUSFunctionBytecode *bytecode); -#endif - -// - -typedef struct JSToken { - int val; - int line_num; /* line number of token start */ - const uint8_t *ptr; - union { - struct { - LEPUSValue str; - int sep; - } str; - struct { - LEPUSValue val; -#ifdef CONFIG_BIGNUM - slimb_t exponent; /* may be != 0 only if val is a float */ -#endif - } num; - struct { - JSAtom atom; - BOOL has_escape; - BOOL is_reserved; - } ident; - struct { - LEPUSValue body; - LEPUSValue flags; - } regexp; - } u; -} JSToken; - -#ifndef NO_QUICKJS_COMPILER -LEPUSValue js_dynamic_import(LEPUSContext *ctx, LEPUSValueConst specifier); -#endif - -#ifdef __cplusplus -} -#endif - -/* */ -static __attribute__((unused)) int JS_DefineProperty_RC( - LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop, - LEPUSValueConst val, LEPUSValueConst getter, LEPUSValueConst setter, - int flags); -static __attribute__((unused)) int JS_DefinePropertyValue_RC( - LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop, LEPUSValue val, - int flags); -static __attribute__((unused)) void JS_MarkValue_RC(LEPUSRuntime *rt, - LEPUSValueConst val, - LEPUS_MarkFunc *mark_func, - int local_idx = -1); -static __attribute__((unused)) int JS_ToBoolFree_RC(LEPUSContext *ctx, - LEPUSValue val); -static __attribute__((unused)) int JS_IsInstanceOf_RC(LEPUSContext *ctx, - LEPUSValueConst val, - LEPUSValueConst obj); -static __attribute__((unused)) LEPUSValue JS_ToString_RC(LEPUSContext *ctx, - LEPUSValueConst val); -/* */ - -/* */ -LEPUSValue JS_GetSeparableStringContent_GC(LEPUSContext *ctx, LEPUSValue val); -LEPUSValue JS_GetSeparableStringContentNotDup_GC(LEPUSContext *ctx, - LEPUSValue val); -QJS_HIDE int set_array_length_gc(LEPUSContext *ctx, LEPUSObject *p, - JSProperty *prop, LEPUSValue val, int flags); -bool gc_enabled(); -LEPUSRuntime *JS_NewRuntime_GC(); -LEPUSRuntime *JS_NewRuntime2_GC(const LEPUSMallocFunctions *mf, void *opaque); -void JS_FreeRuntime_GC(LEPUSRuntime *rt); -void JS_FreeRuntimeForEffect(LEPUSRuntime *rt); -LEPUSContext *JS_NewContext_GC(LEPUSRuntime *rt); -void JS_FreeContext_GC(LEPUSContext *ctx); -LEPUSContext *JS_NewContextRaw_GC(LEPUSRuntime *rt); - -void JS_SetMemoryLimit_GC(LEPUSRuntime *rt, size_t limit); -void JS_RunGC_GC(LEPUSRuntime *rt); - -void JS_AddIntrinsicBaseObjects_GC(LEPUSContext *ctx); -void JS_AddIntrinsicDate_GC(LEPUSContext *ctx); -void JS_AddIntrinsicEval_GC(LEPUSContext *ctx); -void JS_AddIntrinsicStringNormalize_GC(LEPUSContext *ctx); -void JS_AddIntrinsicRegExp_GC(LEPUSContext *ctx); -void JS_AddIntrinsicJSON_GC(LEPUSContext *ctx); -void JS_AddIntrinsicProxy_GC(LEPUSContext *ctx); -void JS_AddIntrinsicMapSet_GC(LEPUSContext *ctx); -void JS_AddIntrinsicTypedArrays_GC(LEPUSContext *ctx); -void JS_AddIntrinsicPromise_GC(LEPUSContext *ctx); - -void JS_SetClassProto_GC(LEPUSContext *ctx, LEPUSClassID class_id, - LEPUSValue obj); -LEPUSValue JS_GetClassProto_GC(LEPUSContext *ctx, LEPUSClassID class_id); -int JS_MoveUnhandledRejectionToException_GC(LEPUSContext *ctx); -void JS_AddIntrinsicRegExpCompiler_GC(LEPUSContext *ctx); -#ifdef QJS_UNITTEST -LEPUSValue js_string_codePointRange_GC(LEPUSContext *ctx, - LEPUSValueConst this_val, int argc, - LEPUSValueConst *argv); -#endif - -JSAtom LEPUS_NewAtom(LEPUSContext *ctx, const char *str); -JSAtom JS_NewAtomUInt32_GC(LEPUSContext *ctx, uint32_t n); -LEPUSValue JS_AtomToValue_GC(LEPUSContext *ctx, JSAtom atom); -LEPUSValue JS_AtomToString_GC(LEPUSContext *ctx, JSAtom atom); -typedef struct JSClassShortDef { - JSAtom class_name; - LEPUSClassFinalizer *finalizer; - LEPUSClassGCMark *gc_mark; -} JSClassShortDef; -QJS_HIDE int init_class_range(LEPUSRuntime *rt, JSClassShortDef const *tab, - int start, int count); -const char *JS_AtomToCString_GC(LEPUSContext *ctx, JSAtom atom); - -void JS_MarkValue_GC(LEPUSRuntime *rt, LEPUSValueConst val, - LEPUS_MarkFunc *mark_func, int local_idx = -1); - -LEPUSValue JS_NewInt64_GC(LEPUSContext *ctx, int64_t v); -LEPUSValue JS_NewError_GC(LEPUSContext *ctx); - -void JS_SetVirtualStackSize_GC(LEPUSContext *ctx, uint32_t stack_size); -int JS_ToBool_GC(LEPUSContext *ctx, LEPUSValueConst val); -int JS_ToInt32_GC(LEPUSContext *ctx, int32_t *pres, LEPUSValueConst val); -int JS_ToInt64_GC(LEPUSContext *ctx, int64_t *pres, LEPUSValueConst val); -int JS_ToIndex_GC(LEPUSContext *ctx, uint64_t *plen, LEPUSValueConst val); -int JS_ToFloat64_GC(LEPUSContext *ctx, double *pres, LEPUSValueConst val); -int JS_ToBigInt64_GC(LEPUSContext *ctx, int64_t *pres, LEPUSValueConst val); -LEPUSValue JS_NewStringLen_GC(LEPUSContext *ctx, const char *buf, - size_t buf_len); -LEPUSValue JS_NewString_GC(LEPUSContext *ctx, const char *str); -LEPUSValue JS_NewAtomString_GC(LEPUSContext *ctx, const char *str); -LEPUSValue JS_ToString_GC(LEPUSContext *ctx, LEPUSValueConst val); -LEPUSValue JS_ToPropertyKey_GC(LEPUSContext *ctx, LEPUSValueConst val); -const char *JS_ToCStringLen2_GC(LEPUSContext *ctx, size_t *plen, - LEPUSValueConst val1, BOOL cesu8); - -LEPUSValue __attribute__((always_inline)) -JS_NewObjectProtoClass_GC(LEPUSContext *ctx, LEPUSValueConst proto_val, - LEPUSClassID class_id); -LEPUSValue JS_NewObjectClass_GC(LEPUSContext *ctx, int class_id); -LEPUSValue JS_NewObjectProto_GC(LEPUSContext *ctx, LEPUSValueConst proto); -LEPUSValue JS_NewObject_GC(LEPUSContext *ctx); -LEPUSValue JS_NewArray_GC(LEPUSContext *ctx); -int JS_IsArray_GC(LEPUSContext *ctx, LEPUSValueConst val); - -LEPUSValue JS_GetPropertyInternal_GC(LEPUSContext *ctx, LEPUSValueConst obj, - JSAtom prop, LEPUSValueConst this_obj, - BOOL throw_ref_error); -LEPUSValue JS_GetPropertyStr_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - const char *prop); -LEPUSValue JS_GetPropertyUint32_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - uint32_t idx); -int JS_SetPropertyInternal_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - JSAtom prop, LEPUSValue val, int flags); - -int JS_SetPropertyUint32_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - uint32_t idx, LEPUSValue val); -int JS_SetPropertyInt64_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - int64_t idx, LEPUSValue val); - -int JS_SetPropertyStr_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - const char *prop, LEPUSValue val); -int JS_HasProperty_GC(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom prop); -int JS_IsExtensible_GC(LEPUSContext *ctx, LEPUSValueConst obj); -int JS_PreventExtensions_GC(LEPUSContext *ctx, LEPUSValueConst obj); -int JS_DeleteProperty_GC(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom prop, - int flags); -int JS_SetPrototype_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst proto_val); -LEPUSValueConst JS_GetPrototype_GC(LEPUSContext *ctx, LEPUSValueConst val); -int JS_GetOwnPropertyNames_GC(LEPUSContext *ctx, LEPUSPropertyEnum **ptab, - uint32_t *plen, LEPUSValueConst obj, int flags); -int JS_GetOwnProperty_GC(LEPUSContext *ctx, LEPUSPropertyDescriptor *desc, - LEPUSValueConst obj, JSAtom prop); -LEPUSValue JS_Call_GC(LEPUSContext *ctx, LEPUSValueConst func_obj, - LEPUSValueConst this_obj, int argc, - LEPUSValueConst *argv); -LEPUSValue JS_CallInternalTI_GC(LEPUSContext *caller_ctx, LEPUSValue func_obj, - LEPUSValue this_obj, LEPUSValue new_target, - int argc, LEPUSValue *argv, int flags); - -LEPUSValue JS_Invoke_GC(LEPUSContext *ctx, LEPUSValueConst this_val, - JSAtom atom, int argc, LEPUSValueConst *argv); -LEPUSValue JS_CallConstructor_GC(LEPUSContext *ctx, LEPUSValueConst func_obj, - int argc, LEPUSValueConst *argv); -LEPUSValue JS_CallConstructor2_GC(LEPUSContext *ctx, LEPUSValueConst func_obj, - LEPUSValueConst new_target, int argc, - LEPUSValueConst *argv); -LEPUSValue JS_Eval_GC(LEPUSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags); -LEPUSValue JS_EvalBinary_GC(LEPUSContext *ctx, const uint8_t *buf, - size_t buf_len, int flags); -LEPUSValue JS_GetGlobalObject_GC(LEPUSContext *ctx); -LEPUSValue JS_GetGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, - BOOL throw_ref_error); -void JS_SetStringCache_GC(LEPUSContext *ctx, LEPUSValue val, void *p); -int JS_SetGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, LEPUSValue val, - int flag); -LEPUSValue JS_DeepEqual_GC(LEPUSContext *ctx, LEPUSValueConst obj1, - LEPUSValueConst obj2); -LEPUSValue JS_DeepCopy_GC(LEPUSContext *ctx, LEPUSValueConst obj); -void JS_IterateObject_GC(LEPUSContext *ctx, LEPUSValue obj, - IterateObject callback, void *pfunc, void *raw_data); -int JS_GetLength_GC(LEPUSContext *ctx, LEPUSValue val); - -int JS_IsInstanceOf_GC(LEPUSContext *ctx, LEPUSValueConst val, - LEPUSValueConst obj); - -int JS_DefineProperty_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - JSAtom prop, LEPUSValueConst val, - LEPUSValueConst getter, LEPUSValueConst setter, - int flags); -int JS_DefinePropertyValue_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - JSAtom prop, LEPUSValue val, int flags); -int JS_DefinePropertyValueUint32_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - uint32_t idx, LEPUSValue val, int flags); -int JS_DefinePropertyValueStr_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - const char *prop, LEPUSValue val, int flags); -int JS_DefinePropertyGetSet_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, - JSAtom prop, LEPUSValue getter, - LEPUSValue setter, int flags); -void *JS_GetOpaque2_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSClassID class_id); -LEPUSValue JS_NewArrayBuffer_GC(LEPUSContext *ctx, uint8_t *buf, size_t len, - LEPUSFreeArrayBufferDataFunc *free_func, - void *opaque, BOOL is_shared); -LEPUSValue JS_NewArrayBufferCopy_GC(LEPUSContext *ctx, const uint8_t *buf, - size_t len); -void JS_DetachArrayBuffer_GC(LEPUSContext *ctx, LEPUSValueConst obj); -uint8_t *JS_GetArrayBuffer_GC(LEPUSContext *ctx, size_t *psize, - LEPUSValueConst obj); -uint8_t *JS_MoveArrayBuffer_GC(LEPUSContext *ctx, size_t *psize, - LEPUSValueConst obj); -LEPUS_BOOL JS_StrictEq_GC(LEPUSContext *ctx, LEPUSValueConst op1, - LEPUSValueConst op2); -LEPUS_BOOL JS_SameValue_GC(LEPUSContext *ctx, LEPUSValueConst op1, - LEPUSValueConst op2); -LEPUSValue JS_NewPromiseCapability_GC(LEPUSContext *ctx, - LEPUSValue *resolving_funcs); -int JS_EnqueueJob_GC(LEPUSContext *ctx, LEPUSJobFunc *job_func, int argc, - LEPUSValueConst *argv); -int JS_ExecutePendingJob_GC(LEPUSRuntime *rt, LEPUSContext **pctx); - -uint8_t *JS_WriteObject_GC(LEPUSContext *ctx, size_t *psize, - LEPUSValueConst obj, int flags); -LEPUSValue JS_EvalFunction_GC(LEPUSContext *ctx, LEPUSValue fun_obj, - LEPUSValueConst this_obj); -LEPUSValue JS_NewWString_GC(LEPUSContext *ctx, const uint16_t *buf, - size_t length); -QJS_HIDE LEPUSValue js_json_stringify_opt_GC(LEPUSContext *, LEPUSValue, - int32_t, LEPUSValue *); -JSAtom JS_ValueToAtom_GC(LEPUSContext *ctx, LEPUSValueConst val); -const uint16_t *JS_GetStringChars_GC(LEPUSContext *ctx, LEPUSValueConst str); -QJS_HIDE LEPUSValue JS_NewArrayWithValue_GC(LEPUSContext *ctx, uint32_t length, - LEPUSValueConst *values); -QJS_HIDE LEPUSValue JS_NewTypedArray_GC(LEPUSContext *ctx, uint32_t length, - LEPUSClassID class_id); -QJS_HIDE LEPUSValue JS_NewTypedArrayWithBuffer_GC(LEPUSContext *ctx, - LEPUSValueConst buffer, - uint32_t byteOffset, - uint32_t length, - LEPUSClassID class_id); - -QJS_HIDE LEPUSValue JS_CallConstructorV_GC(LEPUSContext *ctx, - LEPUSValueConst func_obj, int argc, - LEPUSValue *argv); -QJS_HIDE LEPUSValue JS_NewCFunction2_GC(LEPUSContext *ctx, LEPUSCFunction *func, - const char *name, int length, - LEPUSCFunctionEnum cproto, int magic); -QJS_HIDE LEPUSValue JS_NewCFunctionData_GC(LEPUSContext *ctx, - LEPUSCFunctionData *func, int length, - int magic, int data_len, - LEPUSValueConst *data); -QJS_HIDE void JS_SetPropertyFunctionList_GC(LEPUSContext *ctx, - LEPUSValueConst obj, - const LEPUSCFunctionListEntry *tab, - int len); -QJS_HIDE LEPUSValue js_object_getOwnPropertyDescriptor_GC( - LEPUSContext *ctx, LEPUSValueConst this_val, int argc, - LEPUSValueConst *argv, int magic); -QJS_HIDE int js_get_length32_gc(LEPUSContext *ctx, uint32_t *pres, - LEPUSValueConst obj); - -QJS_HIDE LEPUSValue JS_NewObjectWithArgs_GC(LEPUSContext *ctx, int32_t size, - const char **keys, - LEPUSValue *values); -QJS_HIDE LEPUSValue JS_NewArrayWithArgs_GC(LEPUSContext *ctx, int32_t size, - LEPUSValue *values); - -QJS_HIDE __exception int js_append_enumerate_gc(LEPUSContext *ctx, - LEPUSValue *sp); -QJS_HIDE __exception int js_iterator_get_value_done_gc(LEPUSContext *ctx, - LEPUSValue *sp); - -QJS_HIDE int JS_ToBoolFree_GC(LEPUSContext *ctx, LEPUSValue val); -QJS_HIDE LEPUSValue JS_ToPrimitiveFree_GC(LEPUSContext *ctx, LEPUSValue val, - int hint); -QJS_HIDE LEPUSValue JS_NewBigUint64_GC(LEPUSContext *ctx, uint64_t v); - -QJS_HIDE void JS_VisitLEPUSValue_GC(LEPUSRuntime *rt, LEPUSValue *val, - int local_idx); -QJS_HIDE void DisposeGlobal_GC(LEPUSRuntime *runtime, - LEPUSValue *global_handle); -QJS_HIDE LEPUSValue *GlobalizeReference_GC(LEPUSRuntime *runtime, - LEPUSValue val, bool is_weak); -QJS_HIDE void FreeNapiScope_GC(LEPUSContext *ctx); -QJS_HIDE void ClearGlobalWeak_GC(LEPUSRuntime *runtime, - LEPUSValue *global_handle); -QJS_HIDE void SetGlobalWeak_GC(LEPUSRuntime *runtime, LEPUSValue *global_handle, - void *data, void (*cb)(void *)); -QJS_HIDE void *GetNapiScope_GC(LEPUSContext *ctx); -QJS_HIDE void InitNapiScope_GC(LEPUSContext *ctx); -QJS_HIDE void SetNapiScope_GC(LEPUSContext *ctx, void *scope); -QJS_HIDE void SetWeakState_GC(LEPUSRuntime *runtime, LEPUSValue *global_handle); - -QJS_HIDE void JS_SetGCPauseSuppressionMode_GC(LEPUSRuntime *rt, bool mode); -QJS_HIDE bool JS_GetGCPauseSuppressionMode_GC(LEPUSRuntime *rt); -QJS_HIDE __attribute__((unused)) bool CheckValidPtr_GC(void *runtime, - void *ptr); -QJS_HIDE JSString *js_alloc_string(LEPUSContext *ctx, int max_len, - int is_wide_char); -QJS_HIDE int JS_InitAtoms(LEPUSRuntime *rt); -QJS_HIDE int JS_isConcatSpreadable(LEPUSContext *ctx, LEPUSValueConst obj); -typedef struct StringBuffer { - LEPUSContext *ctx; - JSString *str; - int len; - int size; - int is_wide_char; - int error_status; -} StringBuffer; -QJS_HIDE int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len); -QJS_HIDE int string_buffer_concat(StringBuffer *s, const JSString *p, - uint32_t from, uint32_t to); -QJS_HIDE int string_buffer_putc8(StringBuffer *s, uint32_t c); -QJS_HIDE int string_buffer_putc16(StringBuffer *s, uint32_t c); -QJS_HIDE int string_getc(const JSString *p, int *pidx); -QJS_HIDE BOOL test_final_sigma(JSString *p, int sigma_pos); -QJS_HIDE int string_buffer_putc(StringBuffer *s, uint32_t c); -QJS_HIDE int string_buffer_fill(StringBuffer *s, int c, int count); -QJS_HIDE int seal_template_obj_GC(LEPUSContext *ctx, LEPUSValueConst obj); -struct BCReaderState; -QJS_HIDE LEPUSValue JS_ReadError_GC(BCReaderState *s); -QJS_HIDE LEPUSValue JS_ReadRegExp(BCReaderState *s); -QJS_HIDE LEPUSValue JS_ReadMap_GC(BCReaderState *s); -QJS_HIDE LEPUSValue js_typed_array_constructor_GC(LEPUSContext *ctx, - LEPUSValueConst new_target, - int argc, - LEPUSValueConst *argv, - int classid); -QJS_HIDE LEPUSValue JS_ReadObjectRec(BCReaderState *s); -QJS_HIDE LEPUSValue JS_ReadRegExp_GC(BCReaderState *s); -QJS_HIDE JSAtom __JS_NewAtom(LEPUSRuntime *rt, JSString *str, int atom_type); -QJS_HIDE int js_string_compare(LEPUSContext *ctx, const JSString *p1, - const JSString *p2); - -QJS_HIDE JSAtom JS_NewAtomStr(LEPUSContext *ctx, JSString *p); -QJS_HIDE BOOL JS_AtomIsArrayIndex(LEPUSContext *ctx, uint32_t *pval, - JSAtom atom); - -QJS_HIDE JSAtom js_get_atom_index(LEPUSRuntime *rt, JSAtomStruct *p); -QJS_HIDE LEPUSModuleDef *js_new_module_def(LEPUSContext *ctx, JSAtom name); -struct JSFunctionDef; -QJS_HIDE int resolve_labels(LEPUSContext *ctx, JSFunctionDef *s); -QJS_HIDE int resolve_variables(LEPUSContext *ctx, JSFunctionDef *s); -QJS_HIDE int new_label_fd(JSFunctionDef *fd, int label); -struct JSParseState; -QJS_HIDE int js_parse_unary_GC(JSParseState *s, int parse_flags); -QJS_HIDE int js_parse_array_literal(JSParseState *s); -QJS_HIDE int js_parse_cond_expr(JSParseState *s, int parse_flags); -QJS_HIDE int js_parse_assign_expr(JSParseState *s, int parse_flags); -QJS_HIDE int js_parse_expr(JSParseState *s); -QJS_HIDE int js_parse_expr2(JSParseState *s, int parse_flags); -QJS_HIDE __exception int next_token(JSParseState *s); -struct JSToken; -QJS_HIDE int js_parse_string(JSParseState *s, int sep, BOOL do_throw, - const uint8_t *p, JSToken *token, - const uint8_t **pp); - -QJS_HIDE int cpool_add(JSParseState *s, LEPUSValue val); -QJS_HIDE int emit_push_const(JSParseState *s, LEPUSValueConst val, - BOOL as_atom); -QJS_HIDE void emit_op(JSParseState *s, uint8_t val); -QJS_HIDE int emit_label(JSParseState *s, int label); -QJS_HIDE void emit_return(JSParseState *s, BOOL hasval); -QJS_HIDE int emit_goto(JSParseState *s, int opcode, int label); -QJS_HIDE int emit_break(JSParseState *s, JSAtom name, int is_cont); -QJS_HIDE void optional_chain_test(JSParseState *s, - int *poptional_chaining_label, - int drop_count); -QJS_HIDE int js_parse_template_part(JSParseState *s, const uint8_t *p); -QJS_HIDE int js_parse_template(JSParseState *s, int call, int *argc); -struct JSParsePos; -QJS_HIDE int js_parse_get_pos(JSParseState *s, JSParsePos *sp); -QJS_HIDE int js_parse_seek_token(JSParseState *s, const JSParsePos *sp); -QJS_HIDE int js_parse_regexp(JSParseState *s); -QJS_HIDE int js_parse_skip_parens_token(JSParseState *s, int *pbits, - BOOL no_line_terminator, - BOOL *has_ellipsis = nullptr); -QJS_HIDE int js_resize_array(LEPUSContext *ctx, void **parray, int elem_size, - int *psize, int *pcount, int new_count); -QJS_HIDE JSExportEntry *find_export_entry(LEPUSContext *ctx, LEPUSModuleDef *m, - JSAtom export_name); - -QJS_HIDE JSExportEntry *add_export_entry(JSParseState *s, LEPUSModuleDef *m, - JSAtom local_name, JSAtom export_name, - JSExportTypeEnum export_type); -QJS_HIDE void set_object_name_computed(JSParseState *s); -QJS_HIDE BOOL set_object_name(JSParseState *s, JSAtom name); -QJS_HIDE int add_scope_var(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name, - JSVarKindEnum var_kind); -QJS_HIDE int add_var(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); -typedef struct JSHoistedDef { // called JSGlobalVar in latest version - int cpool_idx; /* -1 means variable global definition */ - uint8_t force_init : 1; /* initialize to undefined */ - uint8_t is_lexical : 1; /* global let/const definition */ - uint8_t is_const : 1; /* const definition */ - int var_idx; /* function object index if cpool_idx >= 0 */ - int scope_level; /* scope of definition */ - JSAtom var_name; /* variable name if cpool_idx < 0 */ -} JSHoistedDef; -QJS_HIDE JSHoistedDef *add_hoisted_def(LEPUSContext *ctx, JSFunctionDef *s, - int cpool_idx, JSAtom name, int var_idx, - BOOL is_lexical); -QJS_HIDE int peek_token(JSParseState *s, BOOL no_line_terminator); -QJS_HIDE int push_scope(JSParseState *s); -QJS_HIDE int find_private_class_field(LEPUSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_level); -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_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 enum { - JS_VAR_DEF_WITH, - JS_VAR_DEF_LET, - JS_VAR_DEF_CONST, - JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ - JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ - JS_VAR_DEF_CATCH, - JS_VAR_DEF_VAR, -} JSVarDefEnum; -QJS_HIDE int js_parse_function_decl2_GC( - JSParseState *s, JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, JSAtom func_name, const uint8_t *ptr, - int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd); -QJS_HIDE int js_parse_class_default_ctor(JSParseState *s, BOOL has_super, - JSFunctionDef **pfd); - -QJS_HIDE int define_var_GC(JSParseState *s, JSFunctionDef *fd, JSAtom name, - JSVarDefEnum var_def_type); - -QJS_HIDE uint64_t compute_column(JSParseState *s, BOOL is_get_var); -QJS_HIDE BOOL js_is_live_code(JSParseState *s); -QJS_HIDE int js_parse_directives(JSParseState *s); -QJS_HIDE int add_closure_var(LEPUSContext *ctx, JSFunctionDef *s, BOOL is_local, - BOOL is_arg, int var_idx, JSAtom var_name, - BOOL is_const, BOOL is_lexical, - JSVarKindEnum var_kind); -QJS_HIDE BOOL is_var_in_arg_scope(LEPUSContext *ctx, const JSVarDef *vd); -QJS_HIDE LEPUSValue js_create_function(LEPUSContext *ctx, JSFunctionDef *fd); -QJS_HIDE void skip_shebang(JSParseState *s); -QJS_HIDE const char *JS_AtomGetStrRT(LEPUSRuntime *rt, char *buf, int buf_size, - JSAtom atom); -QJS_HIDE const char *JS_AtomGetStr(LEPUSContext *ctx, char *buf, int buf_size, - JSAtom atom); -QJS_HIDE int js_parse_error_reserved_identifier(JSParseState *s); -QJS_HIDE BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom); -QJS_HIDE BOOL token_is_ident(int tok); -QJS_HIDE int js_parse_expect(JSParseState *s, int tok); -QJS_HIDE int js_parse_object_literal_GC(JSParseState *s); -QJS_HIDE int js_parse_expr_paren(JSParseState *s); -QJS_HIDE __exception JSAtom js_parse_from_clause(JSParseState *s); -QJS_HIDE int add_req_module_entry(LEPUSContext *ctx, LEPUSModuleDef *m, - JSAtom module_name); -QJS_HIDE int js_parse_expect_semi(JSParseState *s); -QJS_HIDE __exception int js_parse_export(JSParseState *s); -QJS_HIDE __exception int js_parse_import(JSParseState *s); -QJS_HIDE LEPUSValue JS_CallFree_GC(LEPUSContext *ctx, LEPUSValue func_obj, - LEPUSValueConst this_obj, int argc, - LEPUSValueConst *argv); -QJS_HIDE int add_star_export_entry(LEPUSContext *ctx, LEPUSModuleDef *m, - int req_module_idx); -QJS_HIDE int js_define_var(JSParseState *s, JSAtom name, int tok); -QJS_HIDE int get_prev_opcode(JSFunctionDef *fd); -QJS_HIDE int update_label(JSFunctionDef *s, int label, int delta); -QJS_HIDE int get_lvalue(JSParseState *s, int *popcode, int *pscope, - JSAtom *pname, int *plabel, int *pdepth, BOOL keep, - int tok); -typedef enum { - PUT_LVALUE_NOKEEP, /* [depth] v -> */ - PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently - just disable optimizations) */ - PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ - PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ - PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ -} PutLValueEnum; -QJS_HIDE void put_lvalue(JSParseState *s, int opcode, int scope, JSAtom name, - int label, PutLValueEnum special, BOOL is_let); -QJS_HIDE JSAtom js_parse_destructing_var(JSParseState *s, int tok, int is_arg); -QJS_HIDE int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name); -QJS_HIDE int js_parse_destructing_element_GC(JSParseState *s, int tok, - int is_arg, int hasval, - int has_ellipsis, - BOOL allow_initializer); -QJS_HIDE int js_parse_var(JSParseState *s, int parse_flags, int tok, - BOOL export_flag); -QJS_HIDE LEPUSValue js_evaluate_module(LEPUSContext *ctx, LEPUSModuleDef *m); -QJS_HIDE JSVarRef *js_create_module_var(LEPUSContext *ctx, BOOL is_lexical); -QJS_HIDE int js_create_module_function(LEPUSContext *ctx, LEPUSModuleDef *m); -QJS_HIDE void set_value_gc(LEPUSContext *ctx, LEPUSValue *pval, - LEPUSValue new_val); -QJS_HIDE int JS_DefineAutoInitProperty_GC( - LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop, - LEPUSValue (*init_func)(LEPUSContext *ctx, LEPUSObject *obj, JSAtom prop, - void *opaque), - void *opaque, int flags); -QJS_HIDE int js_link_module(LEPUSContext *ctx, LEPUSModuleDef *m); -QJS_HIDE int skip_spaces(const char *pc); -int JS_DefineObjectName_GC(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom name, - int flags); -QJS_HIDE LEPUSValue js_atod(LEPUSContext *ctx, const char *str, const char **pp, - int radix, int flags); -QJS_HIDE int js_resolve_module(LEPUSContext *ctx, LEPUSModuleDef *m); -QJS_HIDE void close_scopes(JSParseState *s, int scope, int scope_stop); -QJS_HIDE JSFunctionDef *js_new_function_def_GC(LEPUSContext *ctx, - JSFunctionDef *parent, - BOOL is_eval, BOOL is_func_expr, - const char *filename, - int line_num); -QJS_HIDE void pop_scope(JSParseState *s); -QJS_HIDE JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name); -QJS_HIDE BOOL is_child_scope(LEPUSContext *ctx, JSFunctionDef *fd, int scope, - int parent_scope); -QJS_HIDE int find_lexical_decl(LEPUSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_idx, - BOOL check_catch_var); -QJS_HIDE int find_arg(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); -QJS_HIDE int find_var(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); -QJS_HIDE int js_parse_class(JSParseState *s, BOOL is_class_expr, - JSParseExportEnum export_flag); -QJS_HIDE int js_parse_left_hand_side_expr_GC(JSParseState *s); -QJS_HIDE int js_parse_property_name_GC(JSParseState *s, JSAtom *pname, - BOOL allow_method, BOOL allow_var, - BOOL allow_private); -QJS_HIDE int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, - JSAtom func_name); -QJS_HIDE int is_let(JSParseState *s, int decl_mask); -QJS_HIDE int __attribute__((format(printf, 2, 3))) QJS_HIDE js_parse_error( - JSParseState *s, const char *fmt, ...); -QJS_HIDE int find_var_in_child_scope(LEPUSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_level); -QJS_HIDE int add_arg(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); -#ifndef NO_QUICKJS_COMPILER -typedef struct JSParseState { - LEPUSContext *ctx; - int last_line_num; /* line number of last token */ - int line_num; /* line 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_ptr; - const uint8_t *buf_end; - // - int debugger_last_line_num; - const uint8_t *line_begin_ptr; - const uint8_t *last_line_begin_ptr; - const uint8_t *last_emit_ptr; - const uint8_t *func_call_ptr; - const uint8_t *utf8_parse_front; - int utf8_adapte_size; - int func_call_adapte_size; - int last_utf8_adapte_size; - const uint8_t *last_last_ptr; - // - /* current function code */ - JSFunctionDef *cur_func; - BOOL is_module; /* parsing a module */ - BOOL allow_html_comments; -} JSParseState; -#endif -QJS_HIDE LEPUSValue js_finalizationRegistry_unregister(LEPUSContext *ctx, - LEPUSValueConst this_val, - int argc, - LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_finalizationRegistry_unregister_gc( - LEPUSContext *ctx, LEPUSValueConst this_val, int argc, - LEPUSValueConst *argv); - -QJS_HIDE LEPUSValue js_finalizationRegistry_register(LEPUSContext *ctx, - LEPUSValueConst this_val, - int argc, - LEPUSValueConst *argv); -QJS_HIDE LEPUSValue -js_finalizationRegistry_register_gc(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); - -QJS_HIDE void AddReferenceRecord(LEPUSContext *ctx, LEPUSObject *obj, - LEPUSValue val); -QJS_HIDE const char *generate_json_str(LEPUSContext *ctx, LEPUSValue obj, - size_t &str_len, const char ***str_arr, - size_t &ts, size_t &cs); -typedef union json_val_uni { - int64_t i64; - double f64; - const char *str; - size_t ofs; - LEPUSValue bigf; // for JSON.stringify bigfloat - LEPUSValue num; // for JSON.parse number -} json_val_uni; - -struct json_val { - uint64_t tag; /**< type, subtype and length */ - json_val_uni uni; /**< payload */ -}; -QJS_HIDE int make_json_val_incr(LEPUSContext *ctx, size_t &alc_len, - json_val **val_hdr, json_val **val, - json_val **ctn, json_val **val_end); -QJS_HIDE uint8_t *json_val_write_format(LEPUSContext *ctx, json_val *root, - const char *gap_str); -QJS_HIDE uint8_t *json_val_write(LEPUSContext *ctx, json_val *root); -QJS_HIDE void parse_json_space(JSParseState *s, const uint8_t **cur); -QJS_HIDE bool read_string(JSParseState *s, uint8_t **ptr, json_val *val); -QJS_HIDE json_val *json_parse_value(JSParseState *s, size_t dat_len); -QJS_HIDE void js_parse_init(LEPUSContext *ctx, JSParseState *s, - const char *input, size_t input_len, - const char *filename); -QJS_HIDE LEPUSValue JS_ParseJSONOPT(LEPUSContext *ctx, const char *buf, - size_t buf_len, const char *filename); -QJS_HIDE LEPUSObject *get_typed_array(LEPUSContext *ctx, - LEPUSValueConst this_val, - int is_dataview); -QJS_HIDE BOOL typed_array_is_detached(LEPUSContext *ctx, LEPUSObject *p); -QJS_HIDE LEPUSValue js_dataview_getValue(LEPUSContext *ctx, - LEPUSValueConst this_obj, int argc, - LEPUSValueConst *argv, int class_id); -QJS_HIDE LEPUSValue js_dataview_setValue(LEPUSContext *ctx, - LEPUSValueConst this_obj, int argc, - LEPUSValueConst *argv, int class_id); -QJS_HIDE JSArrayBuffer *js_get_array_buffer(LEPUSContext *ctx, - LEPUSValueConst obj); -QJS_HIDE LEPUSValue js_create_from_ctor_GC(LEPUSContext *ctx, - LEPUSValueConst ctor, int class_id); -QJS_HIDE LEPUSValue js_dataview_constructor(LEPUSContext *ctx, - LEPUSValueConst new_target, - int argc, LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_typed_array_get_buffer(LEPUSContext *ctx, - LEPUSValueConst this_val, - int is_dataview); -QJS_HIDE LEPUSValue js_typed_array_get_length(LEPUSContext *ctx, - LEPUSValueConst this_val); -QJS_HIDE LEPUSValue js_typed_array_get_byteLength(LEPUSContext *ctx, - LEPUSValueConst this_val, - int is_dataview); -QJS_HIDE LEPUSValue js_typed_array_get_byteOffset(LEPUSContext *ctx, - LEPUSValueConst this_val, - int is_dataview); -const LEPUSCFunctionListEntry js_dataview_proto_funcs[] = { - LEPUS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1), - LEPUS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, - 1), - LEPUS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, - 1), - LEPUS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, - JS_CLASS_INT8_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, - JS_CLASS_UINT8_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, - JS_CLASS_INT16_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, - JS_CLASS_UINT16_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, - JS_CLASS_INT32_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, - JS_CLASS_UINT32_ARRAY), -#ifdef CONFIG_BIGNUM - LEPUS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, - JS_CLASS_BIG_INT64_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, - JS_CLASS_BIG_UINT64_ARRAY), -#endif - LEPUS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, - JS_CLASS_FLOAT32_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, - JS_CLASS_FLOAT64_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, - JS_CLASS_INT8_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, - JS_CLASS_UINT8_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, - JS_CLASS_INT16_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, - JS_CLASS_UINT16_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, - JS_CLASS_INT32_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, - JS_CLASS_UINT32_ARRAY), -#ifdef CONFIG_BIGNUM - LEPUS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, - JS_CLASS_BIG_INT64_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, - JS_CLASS_BIG_UINT64_ARRAY), -#endif - LEPUS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, - JS_CLASS_FLOAT32_ARRAY), - LEPUS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, - JS_CLASS_FLOAT64_ARRAY), - LEPUS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", - LEPUS_PROP_CONFIGURABLE), -}; -QJS_HIDE void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags); -QJS_HIDE LEPUSValue js_closure2(LEPUSContext *ctx, LEPUSValue func_obj, - LEPUSFunctionBytecode *b, - JSVarRef **cur_var_refs, LEPUSStackFrame *sf); -QJS_HIDE void js_random_init(LEPUSContext *ctx); -QJS_HIDE LEPUSValue js_math_random(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -QJS_HIDE int getTimezoneOffset(int64_t time); -QJS_HIDE int JS_SetPrototypeInternal_GC(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst proto_val, - BOOL throw_flag); -QJS_HIDE LEPUSValue js_reflect_setPrototypeOf(LEPUSContext *ctx, - LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_reflect_has(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_reflect_set(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_reflect_get(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -QJS_HIDE LEPUSValue js_reflect_deleteProperty(LEPUSContext *ctx, - LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -QJS_HIDE int JS_SetPropertyGeneric_GC(LEPUSContext *ctx, LEPUSObject *p, - JSAtom prop, LEPUSValue val, - LEPUSValueConst this_obj, int flags); -QJS_HIDE void js_shape_hash_unlink(LEPUSRuntime *rt, JSShape *sh); -QJS_HIDE void js_shape_hash_link(LEPUSRuntime *rt, JSShape *sh); -QJS_HIDE int resize_shape_hash(LEPUSRuntime *rt, int new_shape_hash_bits); -QJS_HIDE int resize_properties(LEPUSContext *ctx, JSShape **psh, LEPUSObject *p, - uint32_t count); -QJS_HIDE BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); -QJS_HIDE void build_backtrace_frame(LEPUSContext *ctx, LEPUSStackFrame *sf, - DynBuf *dbuf, const uint8_t *cur_pc, - BOOL is_async, BOOL is_debug_mode, - LEPUSValueConst error_obj); -QJS_HIDE void get_backtrace(LEPUSContext *ctx, DynBuf *dbuf, BOOL is_debug_mode, - LEPUSValueConst error_obj, const uint8_t *cur_pc, - int backtrace_flags); -QJS_HIDE LEPUSValue JS_GetIterator(LEPUSContext *ctx, LEPUSValueConst obj, - BOOL is_async); -QJS_HIDE LEPUSValue JS_GetIterator2(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSValueConst method); -QJS_HIDE LEPUSValue JS_IteratorNext2(LEPUSContext *ctx, - LEPUSValueConst enum_obj, - LEPUSValueConst method, int argc, - LEPUSValueConst *argv, int *pdone); -QJS_HIDE LEPUSValue JS_IteratorNext(LEPUSContext *ctx, LEPUSValueConst enum_obj, - LEPUSValueConst method, int argc, - LEPUSValueConst *argv, BOOL *pdone); -QJS_HIDE JSShapeProperty *find_own_property1(LEPUSObject *p, JSAtom atom); -QJS_HIDE LEPUSValue JS_ThrowError(LEPUSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap); -QJS_HIDE int __attribute__((format(printf, 3, 4))) -JS_ThrowTypeErrorOrFalse(LEPUSContext *ctx, int flags, const char *fmt, ...); -QJS_HIDE int __attribute__((format(printf, 2, 3))) QJS_HIDE js_throw_URIError( - LEPUSContext *ctx, const char *fmt, ...); -QJS_HIDE void *LEPUS_GetOpaque2(LEPUSContext *ctx, LEPUSValueConst obj, - LEPUSClassID class_id); -QJS_HIDE JSRegExp *js_get_regexp(LEPUSContext *ctx, LEPUSValueConst obj, - BOOL throw_error); -QJS_HIDE LEPUSValue __attribute__((format(printf, 3, 4))) -__JS_ThrowTypeErrorAtom(LEPUSContext *ctx, JSAtom atom, const char *fmt, ...); -QJS_HIDE LEPUSValue JS_ThrowTypeErrorPrivateNotFound(LEPUSContext *ctx, - JSAtom atom); -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; - int has_iterator; -} BlockEnv; - -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 LineNumberSlot { - uint32_t pc; - // - uint64_t line_num; - // -} LineNumberSlot; - -typedef struct JSFunctionDef { - LEPUSContext *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; - - BOOL is_eval; /* TRUE if eval code */ - int eval_type; /* only valid if is_eval = TRUE */ - BOOL is_global_var; /* TRUE if variables are not defined locally: - eval global, eval module or non strict eval */ - BOOL is_func_expr; /* TRUE if function expression */ - BOOL has_home_object; /* TRUE if the home object is available */ - BOOL has_prototype; /* true if a prototype field is necessary */ - BOOL has_simple_parameter_list; - BOOL has_parameter_expressions; /* if true, an argument scope is created */ - BOOL has_use_strict; /* to reject directive in special cases */ - BOOL has_eval_call; /* true if the function contains a call to eval() */ - BOOL has_arguments_binding; /* true if the 'arguments' binding is - available in the function */ - BOOL has_this_binding; /* true if the 'this' and new.target binding are - available in the function */ - BOOL new_target_allowed; /* true if the 'new.target' does not - throw a syntax error */ - BOOL super_call_allowed; /* true if super() is allowed */ - BOOL super_allowed; /* true if super. or super[] is allowed */ - BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ - BOOL is_derived_class_constructor; - BOOL in_function_body; - JSFunctionKindEnum func_kind : 8; - JSParseFunctionEnum func_type : 8; - uint8_t js_mode; /* bitmap of JS_MODE_x */ - JSAtom func_name; /* JS_ATOM_NULL if no name */ - - JSVarDef *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_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; - BOOL need_home_object; - - 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 hoisted_def_count; - int hoisted_def_size; - JSHoistedDef *hoisted_def; - - DynBuf byte_code; - int last_opcode_pos; /* -1 if no last opcode */ - int last_opcode_line_num; - BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ - - 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) */ - LEPUSValue *cpool; - uint32_t cpool_count; - uint32_t cpool_size; - - /* list of variables in the closure */ - int closure_var_count; - int closure_var_size; - LEPUSClosureVar *closure_var; - - JumpSlot *jump_slots; - int jump_size; - int jump_count; - - LineNumberSlot *line_number_slots; - int line_number_size; - int line_number_count; - // - int64_t line_number_last; - int64_t line_number_last_pc; - // - - /* pc2line table */ - JSAtom filename; - int line_num; -#ifdef ENABLE_QUICKJS_DEBUGGER - int64_t column_num; - LEPUSScriptSource *script; -#endif - DynBuf pc2line; - - CallerStrSlot *caller_slots; - int32_t caller_size; - int32_t caller_count; - int32_t resolve_caller_count; - bool should_add_slot; - - const char *src_start; - char *source; /* raw source, utf-8 encoded */ - int source_len; - - LEPUSModuleDef *module; /* != NULL when parsing a module */ -} JSFunctionDef; - -QJS_HIDE int add_closure_variables(LEPUSContext *ctx, JSFunctionDef *s, - LEPUSFunctionBytecode *b, int scope_idx); -QJS_HIDE int JS_DefinePropertyValueInt64_GC(LEPUSContext *ctx, - LEPUSValueConst this_obj, - int64_t idx, LEPUSValue val, - int flags); -QJS_HIDE LEPUSValue js_error_constructor(LEPUSContext *ctx, - LEPUSValueConst new_target, int argc, - LEPUSValueConst *argv, int magic); - -QJS_HIDE BOOL JS_AtomIsString(LEPUSContext *ctx, JSAtom v); - -QJS_HIDE LEPUSValue JS_NewObjectFromShape(LEPUSContext *, JSShape *, - LEPUSClassID); - -QJS_HIDE LEPUSValue JS_NewObjectFromShape_GC(LEPUSContext *, JSShape *, - LEPUSClassID); - -QJS_HIDE LEPUSFunctionBytecode *JS_GetFunctionBytecode(LEPUSValueConst); -QJS_HIDE JSAtom js_symbol_to_atom(LEPUSContext *, LEPUSValue); -QJS_HIDE LEPUSValueConst JS_GetActiveFunction(LEPUSContext *ctx); -QJS_HIDE LEPUSValue js_array_buffer_get_byteLength(LEPUSContext *, - LEPUSValueConst, int32_t); -#ifndef NO_QUICKJS_COMPILER -// the last two parameters are needed for qjs debugger, default value: false, -// NULL -QJS_HIDE LEPUSValue JS_EvalInternal(LEPUSContext *, LEPUSValue, const char *, - size_t, const char *, int, int, - bool = false, LEPUSStackFrame * = nullptr); -#endif - -LEPUSValue js_array_reduce_gc(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv, int special); - -LEPUSValue js_array_concat_gc(LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv); -/* Shape support */ -QJS_STATIC inline JSShapeProperty *get_shape_prop(JSShape *sh) { - return sh->prop; -} - -QJS_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); -} - -QJS_STATIC inline JSShape *get_shape_from_alloc(void *sh_alloc, - size_t hash_size) { - return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); -} - -QJS_STATIC inline void *get_alloc_from_shape(JSShape *sh) { - return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1); -} - -QJS_STATIC inline BOOL atom_is_free(const JSAtomStruct *p) { - return (uintptr_t)p & 1; -} - -QJS_STATIC inline BOOL __JS_AtomIsTaggedInt(JSAtom v) { - return (v & JS_ATOM_TAG_INT) != 0; -} - -QJS_STATIC inline JSAtom __JS_AtomFromUInt32(uint32_t v) { - return v | JS_ATOM_TAG_INT; -} - -QJS_STATIC inline uint32_t __JS_AtomToUInt32(JSAtom atom) { - return atom & ~JS_ATOM_TAG_INT; -} - -QJS_HIDE JSAtomKindEnum JS_AtomGetKind(LEPUSContext *, JSAtom); -QJS_HIDE LEPUS_BOOL JS_LepusRefIsArray(LEPUSRuntime *rt, LEPUSValue v); -QJS_HIDE LEPUS_BOOL JS_LepusRefIsTable(LEPUSRuntime *rt, LEPUSValue v); -QJS_HIDE JSShape *js_dup_shape(JSShape *sh); - -typedef struct JSCFunctionDataRecord { - LEPUSCFunctionData *func; - uint8_t length; - uint8_t data_len; - uint16_t magic; - LEPUSValue data[0]; -} JSCFunctionDataRecord; - -typedef struct ValueBuffer { - LEPUSContext *ctx; - LEPUSValue *arr; - LEPUSValue def[4]; - int len; - int size; - int error_status; -} ValueBuffer; - -typedef enum JSPromiseStateEnum { - JS_PROMISE_PENDING, - JS_PROMISE_FULFILLED, - JS_PROMISE_REJECTED, -} JSPromiseStateEnum; - -typedef struct JSPromiseData { - JSPromiseStateEnum promise_state; - /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ - struct list_head promise_reactions[2]; - BOOL is_handled; /* Note: only useful to debug */ - LEPUSValue promise_result; -} JSPromiseData; - -typedef struct JSPromiseFunctionDataResolved { - int ref_count; - BOOL already_resolved; -} JSPromiseFunctionDataResolved; - -typedef struct JSPromiseFunctionData { - LEPUSValue promise; - JSPromiseFunctionDataResolved *presolved; -} JSPromiseFunctionData; - -typedef struct JSPromiseReactionData { - struct list_head link; /* not used in promise_reaction_job */ - LEPUSValue resolving_funcs[2]; - LEPUSValue handler; -} JSPromiseReactionData; - -typedef struct ReferenceRecord { - int max_size; - LEPUSValue *references; - int length; -} ReferenceRecord; - -typedef struct RegistryRecord { - struct ReferenceRecord *registra; - struct ReferenceRecord *heldvalue; - struct ReferenceRecord *target; - struct ReferenceRecord *token; - struct JSFinalizationRegistryEntry *entry; - int *idx; -} RegistryRecord; - -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; - LEPUSValue key; - LEPUSValue value; -} JSMapRecord; - -typedef struct ValueSlot { - LEPUSValue val; - JSString *str; - int64_t pos; -} ValueSlot; - -struct array_sort_context { - LEPUSContext *ctx; - int exception; - int has_method; - LEPUSValueConst method; -}; - -typedef struct FinalizerOpaque { - LEPUSContext *ctx; -} FinalizerOpaque; - -/* */ -#define BC_NEW_PREFIX 0x8 -#define VERSION_PLACEHOLDER 0xCAB00000 -#define NEW_DEBUGINFO_FLAG 0x100000000 - -bool JS_IsNewVersion(LEPUSContext *ctx); -bool JS_CheckBytecodeVersion(uint64_t v64); - -QJS_HIDE bool primjs_snapshot_enabled(); - -QJS_HIDE void DeleteCurNode(LEPUSRuntime *rt, void *node, int type); -QJS_HIDE bool CheckValidNode(LEPUSRuntime *rt, void *node, int type); - -// #sec-tointgerorinfinity -inline double DoubleToInteger(double x) { - if (isnan(x) || x == 0.0) return 0; - if (!isfinite(x)) return x; - return ((x > 0) ? floor(x) : ceil(x)) + 0.0; -} - -inline void insert_weakref_record(LEPUSObject *p, - struct WeakRefRecord *record) { - record->next_weak_ref = p->first_weak_ref; - p->first_weak_ref = record; - return; -} - -char *js_strmalloc(const char *s, size_t n); -void AddLepusRefCount(LEPUSContext *ctx); - -class LynxTraceInstance { - public: - using BeginPtr = void *(*)(const char *); - using EndPtr = void (*)(void *); - static auto &GetInstance() { - static LynxTraceInstance instance; - return instance; - } - void InitBeginPtr(BeginPtr begin) { trace_start_ = begin; } - void InitEndPtr(EndPtr end) { trace_end_ = end; } - - auto GetBeginPtr() const { return trace_start_; } - auto GetEndPtr() const { return trace_end_; } - - private: - BeginPtr trace_start_{nullptr}; - EndPtr trace_end_{nullptr}; -}; - -class TraceManager { - public: - explicit TraceManager(const char *name) { - if (auto call_begin = LynxTraceInstance::GetInstance().GetBeginPtr()) { - ptr = call_begin(name); - } - } - - ~TraceManager() { - if (auto call_end = LynxTraceInstance::GetInstance().GetEndPtr()) { - call_end(ptr); - } - } - - private: - void *ptr{nullptr}; -}; - -#define JS_OBJECT_IS_OUTER(obj) (obj->class_id >= JS_CLASS_INIT_COUNT) - -#ifdef ENABLE_QUICKJS_DEBUGGER -#define TRACE_EVENT(name) auto tracer = TraceManager{name}; - -#ifdef ENABLE_COMPATIBLE_MM -#define DEBUGGER_COMPATIBLE_CALL_RET(ctx, name, args...) \ - (ctx->rt->gc_enable) ? (name##_GC(args)) : (name(args)) -#else -#define DEBUGGER_COMPATIBLE_CALL_RET(ctx, name, args...) (name(args)) -#endif /* ENABLE_COMPATIBLE_MM */ - -#else -#define TRACE_EVENT(name) -#define DEBUGGER_COMPATIBLE_CALL_RET -#endif /* ENABLE_QUICKJS_DEBUGGER */ - -int64_t date_now(); - -inline bool json_opt_disabled() { return JSON_OPT_DISABLE & settingsFlag; } -inline bool json_opt_disabled(LEPUSRuntime *rt) { - return rt->settings_option.disable_json_opt; -} -inline bool deepclone_opt_disabled() { - return DEEPCLONE_OPT_DISABLE & settingsFlag; -} - -inline bool deepclone_opt_disabled(LEPUSRuntime *rt) { - return rt->settings_option.disable_deepclone_opt; -} - -inline bool separable_string_disabled() { - return DISABLE_SEPARABLE_STRING & settingsFlag; -} - -inline bool minify_virtual_stack_size_enabled() { - return settingsFlag & MINIFY_STACK_ENABLE; -} - -inline bool separable_string_disabled(LEPUSRuntime *rt) { - return rt->settings_option.disable_separable_string; -} - -inline bool adjust_stacksize_disabled() { - return DISABLE_ADJUST_STACKSIZE & settingsFlag; -} - -inline void js_init_settings_options(LEPUSRuntime *rt) { - rt->settings_option.disable_adjust_stacksize = adjust_stacksize_disabled(); - rt->settings_option.disable_json_opt = json_opt_disabled(); - rt->settings_option.disable_deepclone_opt = deepclone_opt_disabled(); - rt->settings_option.disable_separable_string = separable_string_disabled(); - return; -} - -inline bool js_is_bytecode_function(LEPUSValue obj) { - return (LEPUS_VALUE_IS_OBJECT(obj)) && - (LEPUS_VALUE_GET_OBJ(obj)->class_id == JS_CLASS_BYTECODE_FUNCTION); -} - -bool emit_name_str(JSParseState *s, const uint8_t *start, const uint8_t *end); -void get_caller_string(JSFunctionDef *s); - -#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_INNER_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-opcode.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-opcode.h deleted file mode 100644 index 871b32af..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs-opcode.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * 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. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -// -// clang-format off -#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(loc) -FMT(arg) -FMT(var_ref) -FMT(u32) -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) -// -FMT(u64) -// -#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(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_var, 6, 0, 0, atom_u8) -DEF(eval, 3, 1, 1, u16) -DEF(regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a - bytecode string */ -DEF(get_super_ctor, 1, 1, 1, none) -DEF(get_super, 1, 1, 1, none) -DEF(import, 1, 1, 1, none) /* dynamic module import */ - -DEF(check_var, 5, 0, 1, atom) /* check if a variable exists */ - -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(put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ - -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) - -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) - -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(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(for_await_of_next, 1, 3, 4, none) -DEF(iterator_get_value_done, 1, 1, 2, none) -DEF(iterator_close, 1, 3, 0, none) -DEF(iterator_close_return, 1, 4, 4, none) -DEF(async_iterator_close, 1, 3, 2, none) -DEF(async_iterator_next, 1, 4, 4, none) -DEF(async_iterator_get, 2, 4, 5, u8) -DEF(initial_yield, 1, 0, 0, none) -DEF(yield, 1, 1, 2, none) -DEF(yield_star, 1, 2, 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) - -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(pow, 1, 2, 1, none) -DEF(shl, 1, 2, 1, none) -DEF(sar, 1, 2, 1, none) -DEF(shr, 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(and, 1, 2, 1, none) -DEF(xor, 1, 2, 1, none) -DEF(or, 1, 2, 1, none) -#ifdef CONFIG_BIGNUM -DEF(mul_pow10, 1, 2, 1, none) -DEF(math_div, 1, 2, 1, none) -DEF(math_mod, 1, 2, 1, none) -DEF(math_pow, 1, 2, 1, none) -#endif -/* 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(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ - -def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */ -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, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ - -// -def(line_num, 9, 0, 0, u64) /* emitted in phase 1, removed in phase 3 */ - // - -#if SHORT_OPCODES -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, 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(is_function, 1, 1, 1, none) -#endif - -#undef DEF -#undef def -#endif /* DEF */ - -#ifdef COMPILE_TIME_OPCODE -COMPILE_TIME_OPCODE(caller_str, 9, 0, 0, none) -#endif diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs_queue.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs_queue.h deleted file mode 100644 index b19956fd..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs_queue.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ -#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ - -#include - -#define DEFAULT_INIT_SIZE 2048 - -#ifdef ENABLE_GC_DEBUG_TOOLS -#include -#ifdef __cplusplus -extern "C" { -#endif -bool check_valid_ptr(void *runtime, void *ptr); -#ifdef __cplusplus -} -#endif -#endif - -// rear is emptry, capacity == size - 1 -class Queue { - public: - Queue(void *runtime, int initial_size); - explicit Queue(void *runtime); - ~Queue(); - - bool IsEmpty(); - bool IsFull(); - void EnQueue(void *ptr); - void *DeQueue(); - void *Front(); - void ResizeQueue(); - // void Display(); - int GetCount() { return count; } - int GetSize() { return size; } - const void *GetQueue() { return queue; } - void Split(int cnt, Queue *q) { - for (int i = 0; i < cnt; i++) { - q->EnQueue(DeQueue()); - } - } - - private: - void **queue; - void *rt_; - int front; - int rear; - int count; - int size; -}; - -#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs_version.h b/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs_version.h deleted file mode 100644 index 8c7a3292..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/quickjs/include/quickjs_version.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ -#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ - -#include -#include -#define FEATURE_LEPUSNG_DEBUGINFO_OUTSIDE "2.5" -#define PRIMJS_ADD_VERSION_CODE "2.14" - -typedef struct Version { - int major, minor, revision, build; -} Version; - -void VersionInit(Version* v, const char* version); -uint8_t VersionLessOrEqual(Version v1, Version other); -uint8_t IsHigherOrEqual(const char* targetV, const char* baseV); - -#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/trace-gc.h b/test-app/runtime/src/main/cpp/napi/primjs/include/trace-gc.h similarity index 75% rename from test-app/runtime/src/main/cpp/napi/primjs/include/gc/trace-gc.h rename to test-app/runtime/src/main/cpp/napi/primjs/include/trace-gc.h index c45f2792..00289633 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/include/gc/trace-gc.h +++ b/test-app/runtime/src/main/cpp/napi/primjs/include/trace-gc.h @@ -4,6 +4,7 @@ #ifndef SRC_GC_TRACE_GC_H_ #define SRC_GC_TRACE_GC_H_ +#include #ifdef __cplusplus extern "C" { #endif @@ -14,10 +15,13 @@ extern "C" { struct LEPUSRuntime; +#ifdef USE_PRIMJS_NAPI +#include "primjs_napi_defines.h" +#endif typedef struct napi_env__ *napi_env; typedef struct napi_value__ *napi_value; -class napi_handle_scope__; -typedef void napi_func(napi_env env, napi_handle_scope__ *scope); +class NAPIHandleScope; +typedef void napi_func(napi_env env, NAPIHandleScope *scope); enum HandleType { HANDLE_TYPE_UNDEFINED, @@ -62,6 +66,19 @@ class PtrHandles { void ResizeHandles(); }; +class CheckTools { + public: + CheckTools(); + ~CheckTools(); + bool PushTid(int tid); + bool IsValidTid(int tid); + + private: + int tid_idx; + int tid_size; + int *tids; +}; + class HandleScope { public: HandleScope(LEPUSRuntime *rt); @@ -80,22 +97,22 @@ class HandleScope { int handle_prev_idx; }; -class napi_handle_scope__ { +class NAPIHandleScope { public: - napi_handle_scope__(napi_env env, LEPUSContext *ctx, napi_func *func = nullptr); - explicit napi_handle_scope__(LEPUSContext *ctx) + NAPIHandleScope(napi_env env, LEPUSContext *ctx, napi_func *func = nullptr); + explicit NAPIHandleScope(LEPUSContext *ctx) : env_(nullptr), ctx_(ctx), handle_tail_(nullptr), reset_napi_env(nullptr) { is_gc = ctx_ == nullptr ? false : LEPUS_IsGCMode(ctx_); if (is_gc) { - prev_ = reinterpret_cast(GetNapiScope(ctx_)); + prev_ = reinterpret_cast(GetNapiScope(ctx_)); SetNapiScope(ctx_, this); } } - inline ~napi_handle_scope__() { + inline ~NAPIHandleScope() { Handle *curr = handle_tail_; while (curr) { Handle *temp = curr; @@ -112,8 +129,8 @@ class napi_handle_scope__ { } } - napi_handle_scope__(const napi_handle_scope__ &) = delete; - void operator=(const napi_handle_scope__ &) = delete; + NAPIHandleScope(const NAPIHandleScope &) = delete; + void operator=(const NAPIHandleScope &) = delete; napi_value Escape(napi_value v); @@ -123,15 +140,23 @@ class napi_handle_scope__ { Handle *prev; }; Handle *GetHandle() { return handle_tail_; } - napi_handle_scope__ *GetPrevScope() { return prev_; } + NAPIHandleScope *GetPrevScope() { return prev_; } napi_env env_; LEPUSContext *ctx_; bool is_gc; - napi_handle_scope__ *prev_; + NAPIHandleScope *prev_; Handle *handle_tail_; napi_func *reset_napi_env; }; +class GCObserver { + public: + virtual ~GCObserver() = default; + virtual void OnGC(std::string mem_info) = 0; +}; +#ifdef USE_PRIMJS_NAPI +#include "primjs_napi_undefs.h" +#endif #endif // SRC_GC_TRACE_GC_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/js_native_api_adapter.cc b/test-app/runtime/src/main/cpp/napi/primjs/js_native_api_adapter.cc new file mode 100644 index 00000000..750814b3 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/primjs/js_native_api_adapter.cc @@ -0,0 +1,707 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2025 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +// PrimJS Node-API adapter. +// +// The prebuilt primjs `libnapi.so` does not export the standard napi_* value +// operations; instead it installs them as function pointers on the napi_env +// vtable (struct napi_env__, defined in primjs_napi_vtable.h) via the exported +// napi_attach_quickjs(). This file provides the standard-named C entry points +// the binding layer calls (declared in common/js_native_api.h) as thin +// pass-throughs into that vtable. +// +// NativeScript-specific additions (host objects, ...) live in +// js_native_api_extensions.cc, not here. Besides the pass-throughs, this file +// also holds primjs_execute_pending_jobs -- the microtask pump that drives the +// engine's job queue for the runtime. + +#include "primjs_napi_vtable.h" // struct napi_env__ (+ standard napi types) +#include "quickjs.h" // LEPUS_* for primjs_execute_pending_jobs +#include "napi_env_quickjs.h" // napi_get_env_context_quickjs + +#include // std::fprintf (napi_fatal_error) +#include // std::abort (napi_fatal_error) + +EXTERN_C_START + +napi_status napi_get_version(napi_env env, uint32_t* result) { + return env->napi_get_version(env, result); +} + +napi_status napi_get_undefined(napi_env env, napi_value* result) { + return env->napi_get_undefined(env, result); +} + +napi_status napi_get_null(napi_env env, napi_value* result) { + return env->napi_get_null(env, result); +} + +napi_status napi_get_global(napi_env env, napi_value* result) { + return env->napi_get_global(env, result); +} + +napi_status napi_get_boolean(napi_env env, bool value, + napi_value* result) { + return env->napi_get_boolean(env, value, result); +} + +napi_status napi_create_object(napi_env env, napi_value* result) { + return env->napi_create_object(env, result); +} + +napi_status napi_create_array(napi_env env, napi_value* result) { + return env->napi_create_array(env, result); +} + +napi_status napi_create_array_with_length(napi_env env, size_t length, + napi_value* result) { + return env->napi_create_array_with_length(env, length, result); +} + +napi_status napi_create_double(napi_env env, double value, + napi_value* result) { + return env->napi_create_double(env, value, result); +} + +napi_status napi_create_int32(napi_env env, int32_t value, + napi_value* result) { + return env->napi_create_int32(env, value, result); +} + +napi_status napi_create_uint32(napi_env env, uint32_t value, + napi_value* result) { + return env->napi_create_uint32(env, value, result); +} + +napi_status napi_create_int64(napi_env env, int64_t value, + napi_value* result) { + return env->napi_create_int64(env, value, result); +} + +napi_status napi_create_string_latin1(napi_env env, const char* str, + size_t length, + napi_value* result) { + return env->napi_create_string_latin1(env, str, length, result); +} + +napi_status napi_create_string_utf8(napi_env env, const char* str, + size_t length, napi_value* result) { + return env->napi_create_string_utf8(env, str, length, result); +} + +napi_status napi_create_string_utf16(napi_env env, const char16_t* str, + size_t length, napi_value* result) { + return env->napi_create_string_utf16(env, str, length, result); +} + +napi_status napi_create_symbol(napi_env env, napi_value description, + napi_value* result) { + return env->napi_create_symbol(env, description, result); +} + +napi_status napi_create_function(napi_env env, const char* utf8name, + size_t length, napi_callback cb, + void* data, napi_value* result) { + return env->napi_create_function(env, utf8name, length, cb, data, result); +} + +napi_status napi_create_error(napi_env env, napi_value code, + napi_value msg, napi_value* result) { + return env->napi_create_error(env, code, msg, result); +} + +napi_status napi_create_type_error(napi_env env, napi_value code, + napi_value msg, napi_value* result) { + return env->napi_create_type_error(env, code, msg, result); +} + +napi_status napi_create_range_error(napi_env env, napi_value code, + napi_value msg, napi_value* result) { + return env->napi_create_range_error(env, code, msg, result); +} + +napi_status napi_typeof(napi_env env, napi_value value, + napi_valuetype* result) { + return env->napi_typeof(env, value, result); +} + +napi_status napi_get_value_double(napi_env env, napi_value value, + double* result) { + return env->napi_get_value_double(env, value, result); +} + +napi_status napi_get_value_int32(napi_env env, napi_value value, + int32_t* result) { + return env->napi_get_value_int32(env, value, result); +} + +napi_status napi_get_value_uint32(napi_env env, napi_value value, + uint32_t* result) { + return env->napi_get_value_uint32(env, value, result); +} + +napi_status napi_get_value_int64(napi_env env, napi_value value, + int64_t* result) { + return env->napi_get_value_int64(env, value, result); +} + +napi_status napi_get_value_bool(napi_env env, napi_value value, + bool* result) { + return env->napi_get_value_bool(env, value, result); +} + +napi_status napi_get_value_string_latin1(napi_env env, napi_value value, + char* buf, size_t bufsize, + size_t* result) { + return env->napi_get_value_string_latin1(env, value, buf, bufsize, result); +} + +napi_status napi_get_value_string_utf8(napi_env env, napi_value value, + char* buf, size_t bufsize, + size_t* result) { + return env->napi_get_value_string_utf8(env, value, buf, bufsize, result); +} + +napi_status napi_get_value_string_utf16(napi_env env, napi_value value, + char16_t* buf, size_t bufsize, + size_t* result) { + return env->napi_get_value_string_utf16(env, value, buf, bufsize, result); +} + +napi_status napi_coerce_to_bool(napi_env env, napi_value value, + napi_value* result) { + return env->napi_coerce_to_bool(env, value, result); +} + +napi_status napi_coerce_to_number(napi_env env, napi_value value, + napi_value* result) { + return env->napi_coerce_to_number(env, value, result); +} + +napi_status napi_coerce_to_object(napi_env env, napi_value value, + napi_value* result) { + return env->napi_coerce_to_object(env, value, result); +} + +napi_status napi_coerce_to_string(napi_env env, napi_value value, + napi_value* result) { + return env->napi_coerce_to_string(env, value, result); +} + +napi_status napi_get_prototype(napi_env env, napi_value object, + napi_value* result) { + return env->napi_get_prototype(env, object, result); +} + +napi_status napi_get_property_names(napi_env env, napi_value object, + napi_value* result) { + return env->napi_get_all_property_names( + env, object, napi_key_include_prototypes, + static_cast(napi_key_enumerable | napi_key_skip_symbols), + napi_key_numbers_to_strings, result); +} + +napi_status napi_set_property(napi_env env, napi_value object, + napi_value key, napi_value value) { + return env->napi_set_property(env, object, key, value); +} + +napi_status napi_has_property(napi_env env, napi_value object, + napi_value key, bool* result) { + return env->napi_has_property(env, object, key, result); +} + +napi_status napi_get_property(napi_env env, napi_value object, + napi_value key, napi_value* result) { + return env->napi_get_property(env, object, key, result); +} + +napi_status napi_delete_property(napi_env env, napi_value object, + napi_value key, bool* result) { + return env->napi_delete_property(env, object, key, result); +} + +napi_status napi_has_own_property(napi_env env, napi_value object, + napi_value key, bool* result) { + return env->napi_has_own_property(env, object, key, result); +} + +napi_status napi_set_named_property(napi_env env, napi_value object, + const char* utf8name, + napi_value value) { + return env->napi_set_named_property(env, object, utf8name, value); +} + +napi_status napi_has_named_property(napi_env env, napi_value object, + const char* utf8name, bool* result) { + return env->napi_has_named_property(env, object, utf8name, result); +} + +napi_status napi_get_named_property(napi_env env, napi_value object, + const char* utf8name, + napi_value* result) { + return env->napi_get_named_property(env, object, utf8name, result); +} + +napi_status napi_set_element(napi_env env, napi_value object, + uint32_t index, napi_value value) { + return env->napi_set_element(env, object, index, value); +} + +napi_status napi_has_element(napi_env env, napi_value object, + uint32_t index, bool* result) { + return env->napi_has_element(env, object, index, result); +} + +napi_status napi_get_element(napi_env env, napi_value object, + uint32_t index, napi_value* result) { + return env->napi_get_element(env, object, index, result); +} + +napi_status napi_delete_element(napi_env env, napi_value object, + uint32_t index, bool* result) { + return env->napi_delete_element(env, object, index, result); +} + +napi_status napi_define_properties( + napi_env env, napi_value object, size_t property_count, + const napi_property_descriptor* properties) { + if (property_count > 0) { + } + return env->napi_define_properties_spec_compliant(env, object, property_count, + properties); +} + +napi_status napi_is_array(napi_env env, napi_value value, bool* result) { + return env->napi_is_array(env, value, result); +} + +napi_status napi_get_array_length(napi_env env, napi_value value, + uint32_t* result) { + return env->napi_get_array_length(env, value, result); +} + +napi_status napi_strict_equals(napi_env env, napi_value lhs, + napi_value rhs, bool* result) { + return env->napi_strict_equals(env, lhs, rhs, result); +} + +napi_status napi_call_function(napi_env env, napi_value recv, + napi_value func, size_t argc, + const napi_value* argv, + napi_value* result) { + if (argc > 0) { + } + return env->napi_call_function_spec_compliant(env, recv, func, argc, argv, + result); +} + +napi_status napi_new_instance(napi_env env, napi_value constructor, + size_t argc, const napi_value* argv, + napi_value* result) { + return env->napi_new_instance(env, constructor, argc, argv, result); +} + +napi_status napi_instanceof(napi_env env, napi_value object, + napi_value constructor, bool* result) { + return env->napi_instanceof(env, object, constructor, result); +} + +napi_status napi_get_cb_info(napi_env env, napi_callback_info cbinfo, + size_t* argc, napi_value* argv, + napi_value* this_arg, void** data) { + return env->napi_get_cb_info(env, cbinfo, argc, argv, this_arg, data); +} + +napi_status napi_get_new_target(napi_env env, napi_callback_info cbinfo, + napi_value* result) { + return env->napi_get_new_target(env, cbinfo, result); +} + +napi_status napi_define_class(napi_env env, const char* utf8name, + size_t length, napi_callback constructor, + void* data, size_t property_count, + const napi_property_descriptor* properties, + napi_value* result) { + napi_class class_result = nullptr; + napi_status status = env->napi_define_class_spec_compliant( + env, utf8name, length, constructor, data, property_count, properties, + nullptr, &class_result); + if (!class_result) { + return status; + } + if (status != napi_ok) { + env->napi_release_class(env, class_result); + return status; + } + status = env->napi_class_get_function(env, class_result, result); + env->napi_release_class(env, class_result); + return status; +} + +napi_status napi_wrap(napi_env env, napi_value js_object, + void* native_object, napi_finalize finalize_cb, + void* finalize_hint, napi_ref* result) { + return env->napi_wrap_spec_compliant(env, js_object, native_object, + finalize_cb, finalize_hint, result); +} + +napi_status napi_unwrap(napi_env env, napi_value js_object, + void** result) { + return env->napi_unwrap_spec_compliant(env, js_object, result); +} + +napi_status napi_remove_wrap(napi_env env, napi_value js_object, + void** result) { + return env->napi_remove_wrap_spec_compliant(env, js_object, result); +} + +napi_status napi_create_external(napi_env env, void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + return env->napi_create_external(env, data, finalize_cb, finalize_hint, + result); +} + +napi_status napi_get_value_external(napi_env env, napi_value value, + void** result) { + return env->napi_get_value_external(env, value, result); +} + +napi_status napi_create_reference(napi_env env, napi_value value, + uint32_t initial_refcount, + napi_ref* result) { + return env->napi_create_reference(env, value, initial_refcount, result); +} + +napi_status napi_delete_reference(napi_env env, napi_ref ref) { + return env->napi_delete_reference(env, ref); +} + +napi_status napi_reference_ref(napi_env env, napi_ref ref, + uint32_t* result) { + return env->napi_reference_ref(env, ref, result); +} + +napi_status napi_reference_unref(napi_env env, napi_ref ref, + uint32_t* result) { + return env->napi_reference_unref(env, ref, result); +} + +napi_status napi_get_reference_value(napi_env env, napi_ref ref, + napi_value* result) { + return env->napi_get_reference_value(env, ref, result); +} + +napi_status napi_open_handle_scope(napi_env env, + napi_handle_scope* result) { + return env->napi_open_handle_scope(env, result); +} + +napi_status napi_close_handle_scope(napi_env env, + napi_handle_scope scope) { + return env->napi_close_handle_scope(env, scope); +} + +napi_status napi_open_escapable_handle_scope( + napi_env env, napi_escapable_handle_scope* result) { + return env->napi_open_escapable_handle_scope(env, result); +} + +napi_status napi_close_escapable_handle_scope( + napi_env env, napi_escapable_handle_scope scope) { + return env->napi_close_escapable_handle_scope(env, scope); +} + +napi_status napi_escape_handle(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, napi_value* result) { + return env->napi_escape_handle(env, scope, escapee, result); +} + +napi_status napi_throw(napi_env env, napi_value error) { + return env->napi_throw_(env, error); +} + +napi_status napi_throw_error(napi_env env, const char* code, + const char* msg) { + return env->napi_throw_error(env, code, msg); +} + +napi_status napi_throw_type_error(napi_env env, const char* code, + const char* msg) { + return env->napi_throw_type_error(env, code, msg); +} + +napi_status napi_throw_range_error(napi_env env, const char* code, + const char* msg) { + return env->napi_throw_range_error(env, code, msg); +} + +napi_status napi_is_error(napi_env env, napi_value value, bool* result) { + return env->napi_is_error(env, value, result); +} + +napi_status napi_is_exception_pending(napi_env env, bool* result) { + return env->napi_is_exception_pending(env, result); +} + +napi_status napi_get_and_clear_last_exception(napi_env env, + napi_value* result) { + return env->napi_get_and_clear_last_exception(env, result); +} + +napi_status napi_is_arraybuffer(napi_env env, napi_value value, + bool* result) { + return env->napi_is_arraybuffer(env, value, result); +} +napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, + void** data, napi_value* result) { + return env->napi_create_arraybuffer(env, byte_length, data, result); +} +napi_status napi_create_external_arraybuffer( + napi_env env, void* external_data, size_t byte_length, + napi_finalize finalize_cb, void* finalize_hint, napi_value* result) { + return env->napi_create_external_arraybuffer( + env, external_data, byte_length, finalize_cb, finalize_hint, result); +} +napi_status napi_get_arraybuffer_info(napi_env env, + napi_value arraybuffer, + void** data, size_t* byte_length) { + return env->napi_get_arraybuffer_info(env, arraybuffer, data, byte_length); +} + +napi_status napi_is_typedarray(napi_env env, napi_value value, + bool* result) { + return env->napi_is_typedarray(env, value, result); +} + +napi_status napi_create_typedarray(napi_env env, + napi_typedarray_type type, + size_t length, napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + return env->napi_create_typedarray(env, type, length, arraybuffer, + byte_offset, result); +} + +napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray, + napi_typedarray_type* type, + size_t* length, void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + return env->napi_get_typedarray_info(env, typedarray, type, length, data, + arraybuffer, byte_offset); +} + +napi_status napi_create_dataview(napi_env env, size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + return env->napi_create_dataview(env, length, arraybuffer, byte_offset, + result); +} + +napi_status napi_is_dataview(napi_env env, napi_value value, + bool* result) { + return env->napi_is_dataview(env, value, result); +} + +napi_status napi_get_dataview_info(napi_env env, napi_value dataview, + size_t* bytelength, void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + return env->napi_get_dataview_info(env, dataview, bytelength, data, + arraybuffer, byte_offset); +} + +napi_status napi_create_promise(napi_env env, napi_deferred* deferred, + napi_value* promise) { + return env->napi_create_promise(env, deferred, promise); +} + +napi_status napi_is_promise(napi_env env, napi_value value, + bool* is_promise) { + return env->napi_is_promise(env, value, is_promise); +} + +napi_status napi_resolve_deferred(napi_env env, napi_deferred deferred, + napi_value resolution) { + return env->napi_release_deferred(env, deferred, resolution, + napi_deferred_resolve); +} + +napi_status napi_reject_deferred(napi_env env, napi_deferred deferred, + napi_value rejection) { + return env->napi_release_deferred(env, deferred, rejection, + napi_deferred_reject); +} + +napi_status napi_run_script(napi_env env, const char* script, size_t length, + const char* filename, napi_value* result) { + return env->napi_run_script(env, script, length, filename, result); +} + +napi_status napi_adjust_external_memory(napi_env env, + int64_t change_in_bytes, + int64_t* adjusted_value) { + return env->napi_adjust_external_memory(env, change_in_bytes, adjusted_value); +} + +napi_status napi_add_finalizer(napi_env env, napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, napi_ref* result) { + return env->napi_add_finalizer(env, js_object, native_object, finalize_cb, + finalize_hint, result); +} + +static const uint64_t kNapiAdapterInstanceDataKey = + reinterpret_cast(&kNapiAdapterInstanceDataKey); +napi_status napi_set_instance_data(napi_env env, void* data, + napi_finalize finalize_cb, + void* finalize_hint) { + return env->napi_set_instance_data_spec_compliant( + env, kNapiAdapterInstanceDataKey, data, finalize_cb, finalize_hint); +} + +napi_status napi_get_instance_data(napi_env env, void** data) { + return env->napi_get_instance_data(env, kNapiAdapterInstanceDataKey, data); +} + +napi_status napi_get_last_error_info( + napi_env env, const napi_extended_error_info** result) { + return env->napi_get_last_error_info(env, result); +} + +napi_status napi_add_env_cleanup_hook(napi_env env, + void (*fun)(void* arg), + void* arg) { + return env->napi_add_env_cleanup_hook(env, fun, arg); +} + +napi_status napi_remove_env_cleanup_hook(napi_env env, + void (*fun)(void* arg), + void* arg) { + return env->napi_remove_env_cleanup_hook(env, fun, arg); +} + +napi_status napi_create_async_work(napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, napi_async_work* result) { + return env->napi_create_async_work(env, async_resource, async_resource_name, + execute, complete, data, result); +} + +napi_status napi_delete_async_work(napi_env env, napi_async_work work) { + return env->napi_delete_async_work(env, work); +} + +napi_status napi_queue_async_work(napi_env env, napi_async_work work) { + return env->napi_queue_async_work(env, work); +} + +napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { + return env->napi_cancel_async_work(env, work); +} + +void napi_fatal_error(const char* location, size_t location_len, + const char* message, size_t message_len) { + if (location && location_len > 0) { + int print_len = + (location_len > INT_MAX) ? INT_MAX : static_cast(location_len); + std::fprintf(stderr, "Fatal error location: %.*s\n", print_len, location); + } + + if (message && message_len > 0) { + int message_print_len = + (message_len > INT_MAX) ? INT_MAX : static_cast(message_len); + std::fprintf(stderr, "Fatal error message: %.*s\n", message_print_len, + message); + } + + std::abort(); +} + +napi_status napi_create_date(napi_env env, double time, + napi_value* result) { + return env->napi_create_date(env, time, result); +} + +napi_status napi_is_date(napi_env env, napi_value value, bool* is_date) { + return env->napi_is_date(env, value, is_date); +} + +napi_status napi_get_date_value(napi_env env, napi_value value, + double* result) { + return env->napi_get_date_value(env, value, result); +} + +napi_status napi_get_all_property_names( + napi_env env, napi_value object, napi_key_collection_mode key_mode, + napi_key_filter key_filter, napi_key_conversion key_conversion, + napi_value* result) { + return env->napi_get_all_property_names(env, object, key_mode, key_filter, + key_conversion, result); +} + +napi_status napi_create_bigint_int64(napi_env env, int64_t value, + napi_value* result) { + return env->napi_create_bigint_int64(env, value, result); +} +napi_status napi_create_bigint_uint64(napi_env env, uint64_t value, + napi_value* result) { + return env->napi_create_bigint_uint64(env, value, result); +} + +napi_status napi_create_bigint_words(napi_env env, int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result) { + return env->napi_create_bigint_words(env, sign_bit, word_count, words, + result); +} +napi_status napi_get_value_bigint_int64(napi_env env, napi_value value, + int64_t* result, + bool* lossless) { + return env->napi_get_value_bigint_int64(env, value, result, lossless); +} +napi_status napi_get_value_bigint_uint64(napi_env env, napi_value value, + uint64_t* result, + bool* lossless) { + return env->napi_get_value_bigint_uint64(env, value, result, lossless); +} +napi_status napi_get_value_bigint_words(napi_env env, napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words) { + return env->napi_get_value_bigint_words(env, value, sign_bit, word_count, + words); +} + +napi_status primjs_execute_pending_jobs(napi_env env) { + int error; + do { + LEPUSContext *context; + error = LEPUS_ExecutePendingJob(LEPUS_GetRuntime(napi_get_env_context_quickjs(env)), &context); + if (error == -1) { + return napi_pending_exception; + } + } while (error != 0); + + return napi_ok; +} + +EXTERN_C_END diff --git a/test-app/runtime/src/main/cpp/napi/primjs/js_native_api_extensions.cc b/test-app/runtime/src/main/cpp/napi/primjs/js_native_api_extensions.cc new file mode 100644 index 00000000..bf6042c0 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/primjs/js_native_api_extensions.cc @@ -0,0 +1,311 @@ +// NativeScript-specific Node-API extensions for PrimJS. +// +// These are entry points NativeScript adds on top of what primjs' libnapi.so +// provides. They are ordinary exported C symbols (not vtable slots) and talk to +// the LEPUS engine directly. Keep engine extensions here so the adapter stays a +// pure pass-through to primjs' vtable. +// +// Currently: host objects (napi_create_host_object / napi_is_host_object / +// napi_get_host_object_data), guarded by USE_HOST_OBJECT.[ + +#include "primjs_napi_vtable.h" // struct napi_env__ (+ standard napi types) +#include "quickjs.h" +#include "napi_env_quickjs.h" + +#ifdef USE_HOST_OBJECT + +// --------------------------------------------------------------------------- +// PrimJS host-object implementation +// +// PrimJS is a QuickJS/LEPUS fork. We register a custom LEPUS class +// (NapiHostObject) with exotic property-trap callbacks that forward to the +// caller-supplied napi_host_object_methods. The three public NAPI functions +// live outside the vtable — they are called directly as normal C symbols by +// the binding layer. +// --------------------------------------------------------------------------- + +struct PrimJSHostObjectInfo { + napi_env env; + void *data; + napi_finalize finalize_cb; + napi_host_object_methods methods; +}; + +// Global class ID (process-lifetime; LEPUS_NewClassID is a monotonic counter). +static LEPUSClassID g_napiHostObjectClassId = 0; + +static void lepus_host_object_finalizer(LEPUSRuntime *rt, LEPUSValue val) { + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(val, g_napiHostObjectClassId)); + if (!info) return; + if (info->finalize_cb) { + info->finalize_cb(info->env, info->data, nullptr); + } + delete info; +} + +// PrimJS keeps the napi "pending exception" in a napi_env slot that is SEPARATE +// from the LEPUS context's thrown-exception slot. libnapi's own C-function +// trampoline transfers a napi exception into the ctx before unwinding, but our +// exotic host-object traps are invoked directly by the engine and bypass that +// trampoline. So when a trap's napi callback leaves an exception pending, we must +// move it into the LEPUS ctx ourselves; otherwise returning LEPUS_EXCEPTION makes +// the engine throw the ctx's (unset -> null) exception instead of the real error +// object, and JS `catch` receives null. +// +// Tracing-GC-safe: no manual LEPUS_FreeValue. napi_js_value_to_quickjs_value +// yields an owned value whose ownership is handed to LEPUS_Throw (the engine's +// exception slot); the collector reclaims it. +static bool primjs_sync_pending_exception(napi_env env, LEPUSContext *ctx) { + bool exc = false; + env->napi_is_exception_pending(env, &exc); + if (!exc) return false; + napi_value pend = nullptr; + env->napi_get_and_clear_last_exception(env, &pend); + LEPUSValue ev = pend ? napi_js_value_to_quickjs_value(env, pend) : LEPUS_UNDEFINED; + LEPUS_Throw(ctx, ev); + return true; +} + +static LEPUSValue lepus_host_object_get(LEPUSContext *ctx, + LEPUSValueConst obj, + JSAtom atom, + LEPUSValueConst /*receiver*/) { + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(obj, g_napiHostObjectClassId)); + if (!info || !info->methods.get) return LEPUS_UNDEFINED; + napi_env env = info->env; + + napi_handle_scope scope; + env->napi_open_handle_scope(env, &scope); + + napi_value host = napi_quickjs_value_to_js_value(env, LEPUS_DupValue(ctx, obj)); + napi_value prop = napi_quickjs_value_to_js_value(env, LEPUS_AtomToValue(ctx, atom)); + napi_value cb_result = info->methods.get(env, host, prop, info->data); + + LEPUSValue ret = LEPUS_UNDEFINED; + if (cb_result != nullptr) { + ret = napi_js_value_to_quickjs_value(env, cb_result); + } + + env->napi_close_handle_scope(env, scope); + + if (primjs_sync_pending_exception(env, ctx)) { + return LEPUS_EXCEPTION; + } + return ret; +} + +static int lepus_host_object_set(LEPUSContext *ctx, + LEPUSValueConst obj, + JSAtom atom, + LEPUSValueConst value, + LEPUSValueConst /*receiver*/, + int /*flags*/) { + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(obj, g_napiHostObjectClassId)); + if (!info || !info->methods.set) return 1; // true = success + napi_env env = info->env; + + napi_handle_scope scope; + env->napi_open_handle_scope(env, &scope); + + napi_value host = napi_quickjs_value_to_js_value(env, LEPUS_DupValue(ctx, obj)); + napi_value prop = napi_quickjs_value_to_js_value(env, LEPUS_AtomToValue(ctx, atom)); + napi_value val = napi_quickjs_value_to_js_value(env, LEPUS_DupValue(ctx, value)); + info->methods.set(env, host, prop, val, info->data); + + env->napi_close_handle_scope(env, scope); + + if (primjs_sync_pending_exception(env, ctx)) return -1; + return 1; +} + +static int lepus_host_object_has(LEPUSContext *ctx, + LEPUSValueConst obj, + JSAtom atom) { + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(obj, g_napiHostObjectClassId)); + if (!info || !info->methods.has) return 0; + napi_env env = info->env; + + napi_handle_scope scope; + env->napi_open_handle_scope(env, &scope); + + napi_value host = napi_quickjs_value_to_js_value(env, LEPUS_DupValue(ctx, obj)); + napi_value prop = napi_quickjs_value_to_js_value(env, LEPUS_AtomToValue(ctx, atom)); + int present = info->methods.has(env, host, prop, info->data); + + env->napi_close_handle_scope(env, scope); + + if (primjs_sync_pending_exception(env, ctx)) return -1; + return present; +} + +static int lepus_host_object_delete(LEPUSContext *ctx, + LEPUSValueConst obj, + JSAtom atom) { + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(obj, g_napiHostObjectClassId)); + if (!info || !info->methods.delete_property) return 1; + napi_env env = info->env; + + napi_handle_scope scope; + env->napi_open_handle_scope(env, &scope); + + napi_value host = napi_quickjs_value_to_js_value(env, LEPUS_DupValue(ctx, obj)); + napi_value prop = napi_quickjs_value_to_js_value(env, LEPUS_AtomToValue(ctx, atom)); + int deleted = info->methods.delete_property(env, host, prop, info->data); + + env->napi_close_handle_scope(env, scope); + + if (primjs_sync_pending_exception(env, ctx)) return -1; + return deleted; +} + +// Implements get_own_property_names so that Object.keys() / for..in enumerate +// the host object's properties via methods.own_keys. +static int lepus_host_object_own_property_names(LEPUSContext *ctx, + LEPUSPropertyEnum **ptab, + uint32_t *plen, + LEPUSValueConst obj) { + *plen = 0; + *ptab = nullptr; + + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(obj, g_napiHostObjectClassId)); + if (!info || !info->methods.own_keys) return 0; + napi_env env = info->env; + + napi_handle_scope scope; + env->napi_open_handle_scope(env, &scope); + + napi_value host = napi_quickjs_value_to_js_value(env, LEPUS_DupValue(ctx, obj)); + napi_value keys_array = info->methods.own_keys(env, host, info->data); + + int ret = 0; + if (keys_array != nullptr) { + uint32_t count = 0; + env->napi_get_array_length(env, keys_array, &count); + if (count > 0) { + *ptab = static_cast( + lepus_malloc(ctx, sizeof(LEPUSPropertyEnum) * count, + ALLOC_TAG_LEPUSPropertyEnum)); + if (*ptab) { + uint32_t added = 0; + for (uint32_t i = 0; i < count; i++) { + napi_value elem = nullptr; + env->napi_get_element(env, keys_array, i, &elem); + if (!elem) continue; + LEPUSValue lv = napi_js_value_to_quickjs_value(env, elem); + JSAtom atom = LEPUS_ValueToAtom(ctx, lv); + if (atom == 0) continue; // 0 == invalid / JS_ATOM_NULL + (*ptab)[added].atom = atom; + (*ptab)[added].is_enumerable = 1; + added++; + } + *plen = added; + } + } + } + + env->napi_close_handle_scope(env, scope); + + if (primjs_sync_pending_exception(env, ctx)) return -1; + return ret; +} + +static LEPUSClassExoticMethods kNapiHostObjectExotic = { + /* get_own_property */ nullptr, + /* get_own_property_names */ lepus_host_object_own_property_names, + /* delete_property */ lepus_host_object_delete, + /* define_own_property */ nullptr, + /* has_property */ lepus_host_object_has, + /* get_property */ lepus_host_object_get, + /* set_property */ lepus_host_object_set, +}; + +static LEPUSClassDef kNapiHostObjectClassDef = { + /* class_name */ "NapiHostObject", + /* finalizer */ lepus_host_object_finalizer, + /* gc_mark */ nullptr, + /* call */ nullptr, + /* exotic */ &kNapiHostObjectExotic, +}; + +// Register the class on the given runtime if not already registered. +static void ensure_host_object_class(LEPUSContext *ctx) { + LEPUSRuntime *rt = LEPUS_GetRuntime(ctx); + if (g_napiHostObjectClassId == 0) { + LEPUS_NewClassID(&g_napiHostObjectClassId); + } + if (!LEPUS_IsRegisteredClass(rt, g_napiHostObjectClassId)) { + LEPUS_NewClass(rt, g_napiHostObjectClassId, &kNapiHostObjectClassDef); + } + LEPUS_SetClassProto(ctx, g_napiHostObjectClassId, LEPUS_NewObject(ctx)); +} + +napi_status napi_create_host_object(napi_env env, + napi_finalize finalize_cb, + void *data, + const napi_host_object_methods *methods, + napi_value *result) { + if (!env) return napi_invalid_arg; + if (!methods || !result) return napi_invalid_arg; + if (!methods->get || !methods->set) return napi_invalid_arg; + + LEPUSContext *ctx = napi_get_env_context_quickjs(env); + ensure_host_object_class(ctx); + + LEPUSValue obj = LEPUS_NewObjectClass(ctx, (int) g_napiHostObjectClassId); + if (LEPUS_IsException(obj)) { + return napi_pending_exception; + } + + PrimJSHostObjectInfo *info = new PrimJSHostObjectInfo(); + info->env = env; + info->data = data; + info->finalize_cb = finalize_cb; + info->methods = *methods; + LEPUS_SetOpaque(obj, info); + + // napi_quickjs_value_to_js_value takes ownership of obj and puts it in the + // current handle scope. + *result = napi_quickjs_value_to_js_value(env, obj); + return napi_ok; +} + +napi_status napi_get_host_object_data(napi_env env, + napi_value object, + void **data) { + if (!env) return napi_invalid_arg; + if (!object || !data) return napi_invalid_arg; + + // Direct cast: napi_value is LEPUSValue* in a handle scope. + LEPUSValue lv = *((LEPUSValue *) object); + if (!LEPUS_IsObject(lv)) return napi_object_expected; + + PrimJSHostObjectInfo *info = static_cast( + LEPUS_GetOpaque(lv, g_napiHostObjectClassId)); + if (!info) return napi_invalid_arg; + + *data = info->data; + return napi_ok; +} + +napi_status napi_is_host_object(napi_env env, + napi_value object, + bool *result) { + if (!env) return napi_invalid_arg; + if (!object || !result) return napi_invalid_arg; + + *result = false; + LEPUSValue lv = *((LEPUSValue *) object); + if (LEPUS_IsObject(lv)) { + void *opaque = LEPUS_GetOpaque(lv, g_napiHostObjectClassId); + *result = (opaque != nullptr); + } + return napi_ok; +} + +#endif // USE_HOST_OBJECT diff --git a/test-app/runtime/src/main/cpp/napi/primjs/jsr.cpp b/test-app/runtime/src/main/cpp/napi/primjs/jsr.cpp index 47a7cc10..2005428d 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/jsr.cpp +++ b/test-app/runtime/src/main/cpp/napi/primjs/jsr.cpp @@ -1,16 +1,27 @@ #include "napi_env_quickjs.h" +#include "napi_env.h" #include "jsr.h" +#include "bytecode_container.h" +#include "File.h" +#include "NativeScriptAssert.h" +#include JSR::JSR() = default; tns::SimpleMap JSR::env_to_jsr_cache; -struct napi_runtime__ { +struct jsr_ns_runtime__ { LEPUSRuntime* runtime; LEPUSContext* context; }; -napi_status js_create_runtime(napi_runtime *runtime) { - auto _runtime = new napi_runtime__(); +static LEPUSValue gc_function(LEPUSContext *ctx, LEPUSValueConst this_val, int argc, LEPUSValueConst *argv) { + LEPUSRuntime *rt = LEPUS_GetRuntime(ctx); + LEPUS_RunGC(rt); + return LEPUS_UNDEFINED; +} + +napi_status js_create_runtime(jsr_ns_runtime *runtime) { + auto _runtime = new jsr_ns_runtime__(); LEPUSRuntime* rt = LEPUS_NewRuntimeWithMode(0); LEPUS_SetRuntimeInfo(rt, "Lynx_LepusNG"); _runtime->context = LEPUS_NewContext(rt); @@ -18,31 +29,22 @@ napi_status js_create_runtime(napi_runtime *runtime) { _runtime->runtime = rt; *runtime = _runtime; - LEPUS_Eval( _runtime->context, "var a;", strlen("var a;"), "a", LEPUS_EVAL_TYPE_GLOBAL); + DEBUG_WRITE_FORCE("[primjs] GC mode: %d, template interpreter (use_primjs): %d", + LEPUS_IsGCModeRT(rt) ? 1 : 0, + LEPUS_IsPrimjsEnabled(rt) ? 1 : 0); + LEPUSValue global_obj = LEPUS_GetGlobalObject(_runtime->context); + LEPUSValue gc_func = LEPUS_NewCFunction(_runtime->context, gc_function, "gc", 0); + LEPUS_SetPropertyStr(_runtime->context, global_obj, "gc", gc_func); +// LEPUS_FreeValue(_runtime->context, gc_func); +// LEPUS_FreeValue(_runtime->context, global_obj); return napi_ok; } -napi_status js_create_napi_env(napi_env *env, napi_runtime runtime) { - +napi_status js_create_napi_env(napi_env *env, jsr_ns_runtime runtime) { *env = napi_new_env(); napi_attach_quickjs((*env), runtime->context); - - JSR::env_to_jsr_cache.Insert((*env), new JSR()); - LEPUS_Eval( runtime->context, "var a;", strlen("var a;"), "a", LEPUS_EVAL_TYPE_GLOBAL); - - const char *requireFactoryScript = "var a;"; - - napi_value source; - napi_create_string_utf8((*env), requireFactoryScript, strlen(requireFactoryScript), &source); - -// napi_value global; -// napi_get_global(env, &global); - - napi_value result; - napi_status status = js_execute_script((*env), source, "a", &result); - return napi_ok; } @@ -71,18 +73,84 @@ napi_status js_free_napi_env(napi_env env) { return napi_ok; } -napi_status js_free_runtime(napi_runtime runtime) { +napi_status js_free_runtime(jsr_ns_runtime runtime) { LEPUS_FreeContext(runtime->context); LEPUS_FreeRuntime(runtime->runtime); return napi_ok; } +static const char *kBytecodeMagic = "NSBCPJS"; // 7 chars + NUL = 8-byte magic + +napi_status js_run_bytecode_file(napi_env env, const char *file, napi_value *result) { + std::string path; + if (!nsbc::ResolvePath(file, path)) { + DEBUG_WRITE("[bytecode] Unable to resolve file: %s", path.c_str()); + return napi_cannot_run_js; + } + if (!nsbc::HasMagic(path, kBytecodeMagic)) { + DEBUG_WRITE("[bytecode] Unable to find PrimJS header: %s", path.c_str()); + return napi_cannot_run_js; + } + + int length = 0; + auto data = tns::File::ReadBinary(path, length); + if (!data) return napi_cannot_run_js; + if (static_cast(length) <= nsbc::kHeaderLen) { + delete[] static_cast(data); + return napi_cannot_run_js; + } + + DEBUG_WRITE("[bytecode] loading PrimJS bytecode: %s (%d bytes)", file, length); + + LEPUSContext *ctx = napi_get_env_context_quickjs(env); + // LEPUS_ReadObject copies what it needs, so the buffer can be freed after. + LEPUSValue fun_obj = LEPUS_ReadObject( + ctx, static_cast(data) + nsbc::kHeaderLen, + static_cast(length) - nsbc::kHeaderLen, LEPUS_READ_OBJ_BYTECODE); + delete[] static_cast(data); + + // Errors return napi_pending_exception (not napi_cannot_run_js): the file IS + // bytecode, so surface the error instead of falling back to source. The + // exception stays pending in the context so napi_is_exception_pending picks + // it up at the call site. + if (LEPUS_IsException(fun_obj)) { + return napi_pending_exception; + } + + // LEPUS_EvalFunction consumes fun_obj; the completion value is the module + // wrapper function. + LEPUSValue eval_result = LEPUS_EvalFunction(ctx, fun_obj, LEPUS_UNDEFINED); + if (LEPUS_IsException(eval_result)) { + return napi_pending_exception; + } + + // napi_quickjs_value_to_js_value takes ownership of eval_result. + *result = napi_quickjs_value_to_js_value(env, eval_result); + return napi_ok; +} + napi_status js_execute_script(napi_env env, napi_value script, const char *file, napi_value *result) { - - return napi_run_script_source(env, script, file, result); + DEBUG_WRITE("[script] loading script: %s", file); + + // PrimJS exposes napi_run_script as a raw (source, length, filename) entry + // point, which lets us pass the script source and its filename directly + // instead of round-tripping through napi_run_script_source. + size_t length = 0; + napi_status status = napi_get_value_string_utf8(env, script, nullptr, 0, &length); + if (status != napi_ok) { + return status; + } + + std::string source(length + 1, '\0'); + status = napi_get_value_string_utf8(env, script, &source[0], length + 1, &length); + if (status != napi_ok) { + return status; + } + + return napi_run_script(env, source.c_str(), length, file, result); } napi_status js_execute_pending_jobs(napi_env env) { diff --git a/test-app/runtime/src/main/cpp/napi/primjs/jsr.h b/test-app/runtime/src/main/cpp/napi/primjs/jsr.h index 2850370d..a3345e20 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/jsr.h +++ b/test-app/runtime/src/main/cpp/napi/primjs/jsr.h @@ -14,6 +14,10 @@ class JSR { public: JSR(); std::recursive_mutex js_mutex; + // Depth of nested JS scopes entered from the host (see NapiScope). We drain + // the pending-job (microtask) queue once this returns to 0, i.e. when the + // native call stack has fully unwound back out of JS. + int jsEnterState = 0; void lock() { js_mutex.lock(); } @@ -31,6 +35,10 @@ class NapiScope { { js_lock_env(env_); // TODO: UPDATE STACK TOP HERE + jsr_ = JSR::env_to_jsr_cache.Get(env_); + if (jsr_) { + jsr_->jsEnterState++; + } if (open_handle) { napi_open_handle_scope(env_, &napiHandleScope_); } else { @@ -39,6 +47,18 @@ class NapiScope { } ~NapiScope() { + // Drain the microtask queue only when the outermost JS scope unwinds so + // that promise continuations (async/await) run — mirroring how a JS + // engine empties its job queue once control returns to the host. + // Draining at a nested depth would run continuations while JS is still + // on the stack. A throwing job must never escape a destructor. + if (jsr_ && --jsr_->jsEnterState <= 0) { + jsr_->jsEnterState = 0; + try { + js_execute_pending_jobs(env_); + } catch (...) { + } + } if (napiHandleScope_) { napi_close_handle_scope(env_, napiHandleScope_); } @@ -48,6 +68,7 @@ class NapiScope { private: napi_env env_; napi_handle_scope napiHandleScope_; + JSR* jsr_ = nullptr; }; #define JSEnterScope diff --git a/test-app/runtime/src/main/cpp/napi/primjs/napi_env.cc b/test-app/runtime/src/main/cpp/napi/primjs/napi_env.cc deleted file mode 100644 index 287de1ed..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/napi_env.cc +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. - * - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file in the root of the source tree. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#include "napi_env.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "napi_env_quickjs.h" -#include "primjs-api.h" - -struct napi_env_data__ { - void AddCleanupHook(void (*fun)(void* arg), void* arg) { - cleanup_hooks.insert(CleanupHook{fun, arg, cleanup_hooks.size()}); - } - - void RemoveCleanupHook(void (*fun)(void* arg), void* arg) { - cleanup_hooks.erase(CleanupHook{fun, arg, 0}); - } - - ~napi_env_data__() { RunCleanup(); } - - struct CleanupHook { - void (*fun)(void*); - void* arg; - uint64_t insertion_order_counter; - - struct Equal { - bool operator()(const CleanupHook& lhs, const CleanupHook& rhs) const { - return lhs.fun == rhs.fun && lhs.arg == rhs.arg; - } - }; - - struct Hash { - std::size_t operator()(CleanupHook const& s) const { - std::size_t h1 = - std::hash{}(reinterpret_cast(s.fun)); - std::size_t h2 = - std::hash{}(reinterpret_cast(s.arg)); - return h1 ^ (h2 << 1); - } - }; - }; - - void RunCleanup() { - while (!cleanup_hooks.empty()) { - // Copy into a vector, since we can't sort an unordered_set in-place. - std::vector callbacks(cleanup_hooks.begin(), - cleanup_hooks.end()); - // We can't erase the copied elements from `cleanup_hooks_` yet, because - // we need to be able to check whether they were un-scheduled by another - // hook. - - std::sort(callbacks.begin(), callbacks.end(), - [](const CleanupHook& a, const CleanupHook& b) { - // Sort in descending order so that the most recently inserted - // callbacks are run first. - return a.insertion_order_counter > b.insertion_order_counter; - }); - - for (const auto& cb : callbacks) { - if (cleanup_hooks.count(cb) == 0) { - // This hook was removed from the `cleanup_hooks_` set during another - // hook that was run earlier. Nothing to do here. - continue; - } - - cb.fun(cb.arg); - cleanup_hooks.erase(cb); - } - } - } - - std::unordered_set - cleanup_hooks; -}; - -napi_status napi_get_version(napi_env env, uint32_t* version) { - *version = NAPI_VERSION_EXPERIMENTAL; - return napi_clear_last_error(env); -} - -napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), - void* arg) { - env->state.env_data->AddCleanupHook(fun, arg); - return napi_clear_last_error(env); -} - -napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), - void* arg) { - env->state.env_data->RemoveCleanupHook(fun, arg); - return napi_clear_last_error(env); -} - -// Warning: Keep in-sync with napi_status enum -static const char* error_messages[] = { - nullptr, - "Invalid argument", - "An object was expected", - "A string was expected", - "A string or symbol was expected", - "A function was expected", - "A number was expected", - "A boolean was expected", - "An array was expected", - "Unknown failure", - "An exception is pending", - "The async work item was cancelled", - "napi_escape_handle already called on scope", - "Invalid handle scope usage", - "Invalid callback scope usage", - "Thread-safe function queue is full", - "Thread-safe function handle is closing", - "A bigint was expected", - "A date was expected", - "An arraybuffer was expected", - "A detachable arraybuffer was expected", - "Napi deadlock", - "napi_no_external_buffers_allowed", - "napi_cannot_run_js", - "napi_handle_scope_empty", - "napi_memory_error", - "napi_promise_exception"}; - -#define NAPI_ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) - -napi_status napi_get_last_error_info(napi_env env, - const napi_extended_error_info** result) { - // you must update this assert to reference the last message - // in the napi_status enum each time a new error message is added. - // We don't have a napi_status_last as this would result in an ABI - // change each time a message was added. - const int last_status = napi_promise_exception; - - static_assert(NAPI_ARRAYSIZE(error_messages) == last_status + 1, - "Count of error messages must match count of error values"); - assert(env->state.last_error.error_code <= last_status); - - // Wait until someone requests the last error information to fetch the error - // message string - env->state.last_error.error_message = - error_messages[env->state.last_error.error_code]; - - *result = &(env->state.last_error); - return napi_ok; -} - -napi_env napi_new_env() { - napi_env env = new napi_env__{}; - env->state.env_data = new napi_env_data__{}; - return env; -} - -void napi_free_env(napi_env env) { - delete env->state.env_data; - delete env; -} \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/primjs/napi_env.h b/test-app/runtime/src/main/cpp/napi/primjs/napi_env.h index 9a123453..7d9ff135 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/napi_env.h +++ b/test-app/runtime/src/main/cpp/napi/primjs/napi_env.h @@ -12,19 +12,15 @@ #ifndef SRC_NAPI_ENV_NAPI_ENV_H_ #define SRC_NAPI_ENV_NAPI_ENV_H_ -//#include "js_native_api.h" -// -//EXTERN_C_START -// -//NAPI_EXTERN napi_env napi_new_env(); -// -//NAPI_EXTERN void napi_free_env(napi_env); -// -//NAPI_EXTERN void napi_setup_loader(napi_env env, const char* name); -// -//NAPI_EXTERN napi_status primjs_execute_pending_jobs(napi_env env); -// -//EXTERN_C_END +#include "js_native_api.h" +EXTERN_C_START +NAPI_EXTERN napi_env napi_new_env(); + +NAPI_EXTERN void napi_free_env(napi_env); + +NAPI_EXTERN void napi_setup_loader(napi_env env, const char* name); + +EXTERN_C_END #endif // SRC_NAPI_ENV_NAPI_ENV_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/napi_env_quickjs.h b/test-app/runtime/src/main/cpp/napi/primjs/napi_env_quickjs.h index 88c860b9..095605a4 100644 --- a/test-app/runtime/src/main/cpp/napi/primjs/napi_env_quickjs.h +++ b/test-app/runtime/src/main/cpp/napi/primjs/napi_env_quickjs.h @@ -10,24 +10,10 @@ #ifndef SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ #define SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus -#include "quickjs/include/quickjs.h" -#ifdef __cplusplus -} -#endif // __cplusplus - #include "js_native_api.h" +#include "quickjs.h" EXTERN_C_START -NAPI_EXTERN napi_env napi_new_env(); - -NAPI_EXTERN void napi_free_env(napi_env); - -NAPI_EXTERN void napi_setup_loader(napi_env env, const char* name); - - NAPI_EXTERN void napi_attach_quickjs(napi_env env, LEPUSContext* ctx); NAPI_EXTERN void napi_detach_quickjs(napi_env env); diff --git a/test-app/runtime/src/main/cpp/napi/primjs/napi_state.h b/test-app/runtime/src/main/cpp/napi/primjs/napi_state.h deleted file mode 100644 index 20b82448..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/napi_state.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. - * - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file in the root of the source tree. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -#ifndef SRC_NAPI_COMMON_NAPI_STATE_H_ -#define SRC_NAPI_COMMON_NAPI_STATE_H_ - -//#include "primjs-api.h" - -//typedef struct napi_env_data__* napi_env_data; -// -//struct napi_state__ { -// napi_extended_error_info last_error; -// napi_env_data env_data; -//}; -// -//inline napi_status napi_clear_last_error(napi_env env) { -// env->state->last_error.error_code = napi_ok; -// -// env->state->last_error.engine_error_code = 0; -// return napi_ok; -//} -// -//inline napi_status napi_set_last_error(napi_env env, napi_status error_code) { -// env->state->last_error.error_code = error_code; -// return error_code; -//} - -#endif // SRC_NAPI_COMMON_NAPI_STATE_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/primjs-api.cc b/test-app/runtime/src/main/cpp/napi/primjs/primjs-api.cc deleted file mode 100644 index 9dc08f37..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/primjs-api.cc +++ /dev/null @@ -1,2881 +0,0 @@ -/** - * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. - * - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file in the root of the source tree. - */ - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -#include "primjs-api.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "basic/log/logging.h" -#include "napi_env_quickjs.h" -#include "quickjs/include/quickjs-inner.h" - - -std::unordered_map napi_context__::rt_to_env_cache; -struct napi_callback_info__ { - napi_value newTarget; - napi_value thisArg; - napi_value *argv; - void *data; - uint16_t argc; -}; - -static inline void js_enter(napi_env env) { - env->js_enter_state++; -} - -static inline void js_exit(napi_env env) { - if (--env->js_enter_state <= 0) { - primjs_execute_pending_jobs(env); - } -} - -namespace { - napi_status napi_set_exception(napi_env env, LEPUSValue exception) { - if (env->ctx->last_exception) { - JS_FreeValue_Comp(env->ctx->ctx, *env->ctx->last_exception); - } - env->ctx->last_exception.reset(new LEPUSValue(exception)); - - env->ctx->last_exception_pVal.Reset(nullptr, exception, nullptr, - env->ctx->ctx, true); - - return napi_set_last_error(env, napi_pending_exception); - } - - napi_status napi_set_error_msg_code(napi_env env, napi_value error, - napi_value code, napi_value msg, - const char *code_cstring) { - { - LEPUSValue msg_value = JS_DupValue_Comp(env->ctx->ctx, ToJSValue(msg)); - env->ctx->CreateHandle(msg_value, true); - - CHECK_QJS(env, LEPUS_SetProperty(env->ctx->ctx, ToJSValue(error), - env->ctx->PROP_MESSAGE, msg_value) != -1); - } - - if (code || code_cstring) { - LEPUSValue code_value; - if (code == nullptr) { - code_value = LEPUS_NewString(env->ctx->ctx, code_cstring); - env->ctx->CreateHandle(code_value, true); - } else { - code_value = ToJSValue(code); - RETURN_STATUS_IF_FALSE(env, - LEPUS_IsString(code_value) || LEPUS_IsUndefined(code_value), - napi_string_expected); - - code_value = JS_DupValue_Comp(env->ctx->ctx, code_value); - } - - if (!LEPUS_IsUndefined(code_value)) { - CHECK_QJS(env, LEPUS_SetProperty(env->ctx->ctx, ToJSValue(error), - env->ctx->PROP_CODE, code_value) != -1); - } - - } - - return napi_ok; - } - - template - class ArgsConverter { - public: - ArgsConverter(size_t argc, In *argv) { - Out *destination = inline_; - if (argc > kMaxStackArgs) { - outOfLine_ = std::make_unique(argc); - destination = outOfLine_.get(); - } - - for (size_t i = 0; i < argc; ++i) { - destination[i] = Convert(argv + i); - } - } - - operator Out *() { return outOfLine_ ? outOfLine_.get() : inline_; } - - private: - constexpr static unsigned kMaxStackArgs = 8; - Out inline_[kMaxStackArgs]; - std::unique_ptr outOfLine_; - }; -} // namespace - -namespace qjsimpl { - - enum NativeType { - External, Wrapper - }; - - class NativeInfo final { - public: - NativeInfo(napi_env env, NativeType type) - : _env(env), _type(type), _data(nullptr) {} - - std::list::const_iterator AddWeakRef(NAPIPersistent *ref) { - return _weakRefs.insert(_weakRefs.end(), ref); - } - - void RemoveWeakRef(std::list::const_iterator iter) { - _weakRefs.erase(iter); - } - - napi_env Env() const { return _env; } - - void Data(void *value) { _data = value; } - - void *Data() const { return _data; } - - NativeType Type() const { return _type; } - - static bool IsInstance(LEPUSClassID id) { return id == class_id; } - - static NativeInfo *Get(LEPUSValue val) { - return static_cast(LEPUS_GetOpaque(val, class_id)); - } - - static LEPUSClassID ClassId(napi_env env) { - static std::once_flag once_flag; - std::call_once(once_flag, [&] { LEPUS_NewClassID(&class_id); }); - auto rt = LEPUS_GetRuntime(env->ctx->ctx); - if (!LEPUS_IsRegisteredClass(rt, class_id)) { - static LEPUSClassDef def = {.class_name = "NAPIMagicNative", - .finalizer = NativeInfo::OnFinalize}; - if (LEPUS_NewClass(rt, class_id, &def) != 0) { - return 0; - } - } - - return class_id; - } - - private: - ~NativeInfo() { - // ref will remove itself when finalize, so copy is needed - for (NAPIPersistent *ref: std::vector( - std::begin(_weakRefs), std::end(_weakRefs))) { - NAPIPersistent::OnFinalize(ref); - } - } - - private: - const napi_env _env; - const NativeType _type; - - void *_data; - std::list _weakRefs; - - static LEPUSClassID class_id; - - static void OnFinalize(LEPUSRuntime *rt, LEPUSValue val) { - NativeInfo *info = static_cast(LEPUS_GetOpaque(val, class_id)); - LEPUS_SetOpaque(val, nullptr); - delete info; - } - }; - - LEPUSClassID NativeInfo::class_id = 0; - - class External { - public: - static LEPUSValue Create(napi_env env, NativeInfo **result) { - LEPUSClassID id = NativeInfo::ClassId(env); - if (!id) { - return LEPUS_ThrowInternalError(env->ctx->ctx, - "failed to create External Class"); - } - LEPUSValue object = LEPUS_NewObjectClass(env->ctx->ctx, id); - if (!LEPUS_IsException(object)) { - NativeInfo *info = new NativeInfo(env, NativeType::External); - LEPUS_SetOpaque(object, info); - *result = info; - } - return object; - } - }; - - class Wrapper { - public: - static LEPUSValue Create(napi_env env, LEPUSValue proto) { - LEPUSClassID id = NativeInfo::ClassId(env); - if (!id) { - return LEPUS_ThrowInternalError(env->ctx->ctx, - "failed to create Wrapper Class"); - } - LEPUSValue object = LEPUS_NewObjectProtoClass(env->ctx->ctx, proto, id); - if (!LEPUS_IsException(object)) { - LEPUS_SetOpaque(object, new NativeInfo(env, NativeType::Wrapper)); - } - return object; - } - }; - -// Wrapper around v8impl::Persistent that implements reference counting. - class RefBase : protected Finalizer, RefTracker { - protected: - RefBase(napi_env env, uint32_t initial_refcount, bool delete_self, - napi_finalize finalize_callback, void *finalize_data, - void *finalize_hint) - : Finalizer(env, finalize_callback, finalize_data, finalize_hint), - _refcount(initial_refcount), - _delete_self(delete_self), - _is_self_destroying(false) { - Link(finalize_callback == nullptr ? &env->ctx->reflist - : &env->ctx->finalizing_reflist); - } - - public: - static RefBase *New(napi_env env, uint32_t initial_refcount, bool delete_self, - napi_finalize finalize_callback, void *finalize_data, - void *finalize_hint) { - return new RefBase(env, initial_refcount, delete_self, finalize_callback, - finalize_data, finalize_hint); - } - - virtual ~RefBase() { Unlink(); } - - inline void *Data() { return _finalize_data; } - - // Delete is called in 2 ways. Either from the finalizer or - // from one of Unwrap or napi_delete_reference. - // - // When it is called from Unwrap or napi_delete_reference we only - // want to do the delete if the finalizer has already run or - // cannot have been queued to run (ie the reference count is > 0), - // otherwise we may crash when the finalizer does run. - // If the finalizer may have been queued and has not already run - // delay the delete until the finalizer runs by not doing the delete - // and setting _delete_self to true so that the finalizer will - // delete it when it runs. - // - // The second way this is called is from - // the finalizer and _delete_self is set. In this case we - // know we need to do the deletion so just do it. - static inline void Delete(RefBase *reference) { - if ((reference->RefCount() != 0) || (reference->_delete_self) || - (reference->_finalize_ran)) { - delete reference; - } else { - // defer until finalizer runs as - // it may alread be queued - reference->_delete_self = true; - } - } - - inline uint32_t Ref() { return ++_refcount; } - - inline uint32_t Unref() { - if (_refcount == 0) { - return 0; - } - return --_refcount; - } - - inline uint32_t RefCount() { return _refcount; } - - protected: - inline void Finalize(bool is_env_teardown = false) override { - if (is_env_teardown && RefCount() > 0) _refcount = 0; - - // There are cases where we want to avoid the reentrance of Finalize ( - // causing double-free): - // * When a wrapped object holds its own strong reference (either directly - // or indirectly) - // * (JSCore specific) when the destruction of a strong reference triggers - // garbage collection - // If we are sure this is getting deleted soon, there is no need for the - // finalizer to proceed. - if (_is_self_destroying && !is_env_teardown) { - return; - } - if (is_env_teardown) { - _is_self_destroying = true; - } - - if (_finalize_callback != nullptr) { - // This ensures that we never call the finalizer twice. - napi_finalize fini = _finalize_callback; - _finalize_callback = nullptr; - _env->ctx->CallFinalizer(fini, _finalize_data, _finalize_hint); - } - - // this is safe because if a request to delete the reference - // is made in the finalize_callback it will defer deletion - // to this block and set _delete_self to true - if (_delete_self || is_env_teardown) { - Delete(this); - } else { - _finalize_ran = true; - } - } - - private: - uint32_t _refcount; - bool _delete_self; - bool _is_self_destroying; - }; - - class Reference : public RefBase { - protected: - template - Reference(napi_env env, LEPUSValueConst value, NativeInfo *native_info, - Args &&... args) - : RefBase(env, std::forward(args)...), - _persistent(env, value, native_info, env->ctx->ctx) { - if (RefCount() == 0) { - _persistent.SetWeak(this, FinalizeCallback); - } - } - - public: - static inline Reference *New(napi_env env, LEPUSValueConst value, - NativeInfo *native_info, - uint32_t initial_refcount, bool delete_self, - napi_finalize finalize_callback = nullptr, - void *finalize_data = nullptr, - void *finalize_hint = nullptr) { - return new Reference(env, value, native_info, initial_refcount, delete_self, - finalize_callback, finalize_data, finalize_hint); - } - - void Finalize(bool is_env_teardown = false) override { - _persistent.Reset(true); - RefBase::Finalize(is_env_teardown); - } - - static inline void Delete(RefBase *reference) { - static_cast(reference)->_persistent.Reset(true); - RefBase::Delete(reference); - } - - inline uint32_t Ref() { - uint32_t refcount = RefBase::Ref(); - if (refcount == 1) { - _persistent.ClearWeak(); - } - return refcount; - } - - virtual ~Reference() { _persistent.Reset(true); } - - inline uint32_t Unref() { - uint32_t old_refcount = RefCount(); - uint32_t refcount = RefBase::Unref(); - if (old_refcount == 1 && refcount == 0) { - _persistent.SetWeak(this, FinalizeCallback); - } - return refcount; - } - - inline napi_value Get() { - return _persistent.IsEmpty() ? nullptr - : _env->ctx->CreateHandle(_persistent.Value()); - } - - private: - static void FinalizeCallback(void *data) { - Reference *r = static_cast(data); - r->_persistent.Reset(); - r->Finalize(); - } - - NAPIPersistent _persistent; - }; - - enum WrapType { - retrievable, anonymous - }; - - template - inline napi_status Wrap(napi_env env, napi_value js_object, void *native_object, - napi_finalize finalize_cb, void *finalize_hint, - napi_ref *result) { - LEPUSValueConst obj = ToJSValue(js_object); - - NativeInfo *info = NativeInfo::Get(obj); - - if (wrap_type == retrievable) { - RETURN_STATUS_IF_FALSE(env, - info != nullptr && - info->Type() == NativeType::Wrapper && - info->Data() == nullptr, - napi_invalid_arg); - } else { - // If no finalize callback is provided, we error out. - CHECK_ARG(env, finalize_cb); - } - - Reference *reference = nullptr; - if (result != nullptr) { - // The returned reference should be deleted via napi_delete_reference() - // ONLY in response to the finalize callback invocation. (If it is deleted - // before then, then the finalize callback will never be invoked.) - // Therefore a finalize callback is required when returning a reference. - CHECK_ARG(env, finalize_cb); - reference = Reference::New(env, obj, info, 0, false, finalize_cb, - native_object, finalize_hint); - *result = reinterpret_cast(reference); - } else { - // Create a self-deleting reference. - reference = - Reference::New(env, obj, info, 0, true, finalize_cb, native_object, - finalize_cb == nullptr ? nullptr : finalize_hint); - } - - if (wrap_type == retrievable) { - info->Data(reference); - } - - return napi_clear_last_error(env); - } - - enum UnwrapAction { - KeepWrap, RemoveWrap - }; - - inline static napi_status Unwrap(napi_env env, napi_value js_object, - void **result, UnwrapAction action) { - if (action == KeepWrap) { - CHECK_ARG(env, result); - } - - LEPUSValue obj = ToJSValue(js_object); - NativeInfo *info = NativeInfo::Get(obj); - - if (!info || info->Type() != qjsimpl::NativeType::Wrapper) { - if (result) { - *result = nullptr; - } - return napi_clear_last_error(env); - } - - Reference *reference = static_cast(info->Data()); - - if (result) { - *result = reference->Data(); - } - - if (action == RemoveWrap) { - info->Data(nullptr); - Reference::Delete(reference); - } - - return napi_clear_last_error(env); - } - - inline static Atom qjsAtomFromPropertyDescriptor( - napi_env env, const napi_property_descriptor &p) { - if (p.utf8name != nullptr) { - return Atom(env, env->ctx->ctx, p.utf8name); - } else { - return Atom(env, env->ctx->ctx, ToJSValue(p.name)); - } - } - - inline static uint8_t qjsFlagFromPropertyDescriptor( - napi_property_attributes attributes) { - uint8_t flags = 0; - if (attributes & napi_writable) { - flags |= LEPUS_PROP_WRITABLE; - } - if (attributes & napi_enumerable) { - flags |= LEPUS_PROP_ENUMERABLE; - } - if (attributes & napi_configurable) { - flags |= LEPUS_PROP_CONFIGURABLE; - } - return flags; - } - - inline NAPIPersistent::NAPIPersistent(napi_env env, LEPUSValueConst value, - NativeInfo *native_info, - LEPUSContext *ctx, bool is_weak) - : PersistentBase( - PersistentBase::New(LEPUS_GetRuntime(ctx), value, is_weak)), - _env(env), - _empty(false), - _value(JS_DupValue_Comp(env->ctx->ctx, value)), - _native_info(native_info), - _ctx(ctx) {} - - inline NAPIPersistent::NAPIPersistent(napi_env env, JSAtom atom, - NativeInfo *native_info, - LEPUSContext *ctx, bool is_weak) - : PersistentBase(PersistentBase::New(LEPUS_GetRuntime(ctx), - LEPUS_MKVAL(LEPUS_TAG_Atom, (int) atom), - is_weak)), - _env(env), - _empty(false), - _value(LEPUS_MKVAL(LEPUS_TAG_Atom, (int) atom)), - _native_info(native_info), - _ctx(ctx) {} - - void NAPIPersistent::Reset(bool for_gc) { - if (_empty) { - return; - } - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { - PersistentBase::Reset(_ctx); - _env = nullptr; - _empty = true; - _native_info = nullptr; - } else if (!for_gc) { - if (_weak_info) { - ResetWeakInfo(); - } else { - JS_FreeValue_Comp(_env->ctx->ctx, _value); - } - _env = nullptr; - _empty = true; - _native_info = nullptr; - } - } - - void NAPIPersistent::Reset(napi_env env, LEPUSValueConst value, - NativeInfo *native_info, LEPUSContext *ctx, - bool for_gc) { - _ctx = ctx; - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { // gc - PersistentBase::Reset(_ctx, value, false); - _empty = false; - _env = env; - _value = value; - _native_info = native_info; - } else if (!for_gc) { - Reset(); - _empty = false; - _env = env; - _value = JS_DupValue_Comp(env->ctx->ctx, value); - _native_info = native_info; - } - } - - void NAPIPersistent::Reset(napi_env env, LEPUSContext *ctx, JSAtom atom) { - _env = env; - _ctx = ctx; - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { - PersistentBase::Reset(_ctx, LEPUS_MKVAL(LEPUS_TAG_Atom, (int) atom), false); - _empty = false; - } - } - - void NAPIPersistent::SetWeak(void *data, void (*cb)(void *)) { - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { - SetGlobalWeak(LEPUS_GetRuntime(_ctx), this->val_, data, cb); - } else { - assert(!_empty); - if (_weak_info) { - _weak_info->cb_arg = data; - _weak_info->cb = cb; - } else { - _weak_info.reset( - new WeakInfo{_get_native_info()->AddWeakRef(this), cb, data}); - JS_FreeValue_Comp(_env->ctx->ctx, _value); - } - } - } - - void NAPIPersistent::ClearWeak() { - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { - ClearGlobalWeak(LEPUS_GetRuntime(_ctx), this->val_); - } else { - JS_DupValue_Comp(_env->ctx->ctx, _value); - ResetWeakInfo(); - } - } - - LEPUSValue NAPIPersistent::Value() const { - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { - return Get(); - } else { - return JS_DupValue_Comp(_env->ctx->ctx, _value); - } - } - - void NAPIPersistent::OnFinalize(NAPIPersistent *ref) { - auto cb = ref->_weak_info->cb; - auto cb_arg = ref->_weak_info->cb_arg; - ref->Reset(); - cb(cb_arg); - } - - void NAPIPersistent::ResetWeakInfo() { - assert(!_empty); - _get_native_info()->RemoveWeakRef(_weak_info->weak_iter); - _weak_info.reset(); - } - - NativeInfo *NAPIPersistent::_get_native_info() { - assert(!_empty); - if (!_native_info) { - LEPUSValue finalizer = - LEPUS_GetProperty(_env->ctx->ctx, _value, _env->ctx->PROP_FINALIZER); - assert(!LEPUS_IsException(finalizer)); - if (LEPUS_IsUndefined(finalizer)) { - NativeInfo *info; - finalizer = External::Create(_env, &info); - assert(!LEPUS_IsException(finalizer)); - int ret = LEPUS_DefinePropertyValue( - _env->ctx->ctx, _value, _env->ctx->PROP_FINALIZER, finalizer, 0); - (void) ret; - assert(ret != -1); - _native_info = info; - } else { - _native_info = qjsimpl::NativeInfo::Get(finalizer); - JS_FreeValue_Comp(_env->ctx->ctx, finalizer); - } - } - return _native_info; - } - -} // namespace qjsimpl - -namespace { - static inline LEPUSValue CallJSFunctionWithNAPI(napi_env env, napi_callback cb, - napi_callback_info cbinfo) { - napi_value result; - std::unique_ptr exception; - env->ctx->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo); }, - [&](napi_env env, LEPUSValue exc) { - exception.reset(new LEPUSValue(exc)); - }); - - if (exception) { - env->ctx->CreateHandle(*exception, true); - return LEPUS_Throw(env->ctx->ctx, *exception); - } - - return result ? JS_DupValue_Comp(env->ctx->ctx, ToJSValue(result)) - : LEPUS_UNDEFINED; - } -} // namespace - -namespace qjsimpl { - typedef struct FunctionDataWrapper { - void *data; - } FunctionDataWrapper; -} - -napi_status napi_create_function(napi_env env, const char *utf8name, - size_t length, napi_callback cb, - void *callback_data, napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - - auto dataWrapper = new qjsimpl::FunctionDataWrapper(); - dataWrapper->data = callback_data; - - LEPUSValue data[] = { - LEPUS_MKPTR(LEPUS_TAG_LEPUS_CPOINTER, env), - LEPUS_MKPTR(LEPUS_TAG_LEPUS_CPOINTER, reinterpret_cast(cb)), - LEPUS_MKPTR(LEPUS_TAG_LEPUS_CPOINTER, dataWrapper)}; - LEPUSValue fun = LEPUS_NewCFunctionData( - ctx, - [](LEPUSContext *ctx, LEPUSValueConst this_val, int argc, - LEPUSValueConst *argv, int magic, - LEPUSValue *func_data) -> LEPUSValue { - napi_env env = - reinterpret_cast(LEPUS_VALUE_GET_CPOINTER(func_data[0])); - napi_callback cb = reinterpret_cast( - LEPUS_VALUE_GET_CPOINTER(func_data[1])); - auto dataWrapper = reinterpret_cast LEPUS_VALUE_GET_CPOINTER( - func_data[2]); - - napi_clear_last_error(env); - - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - ArgsConverter args(argc, argv); - - napi_callback_info__ cbinfo{}; - cbinfo.thisArg = ToNapi(&this_val); - cbinfo.newTarget = nullptr; - cbinfo.argc = argc; - cbinfo.argv = args; - cbinfo.data = dataWrapper->data; - - return CallJSFunctionWithNAPI(env, cb, &cbinfo); - }, - 0, 0, 3, data); - - napi_value external; - napi_create_external(env, dataWrapper, [](napi_env, void *data, void *hint) { - if (data) { - auto dataWrapper = reinterpret_cast(data); - delete dataWrapper; - } - }, dataWrapper, &external); - napi_set_named_property(env, ToNapi(&fun), "__qjs::finalizer__", external); - - CHECK_QJS(env, !LEPUS_IsException(fun)); - - *result = env->ctx->CreateHandle(fun); - - if (utf8name) { - // ignore error - LEPUSValue str = LEPUS_NewString(ctx, utf8name); - env->ctx->CreateHandle(str, true); - LEPUS_DefinePropertyValue(ctx, fun, env->ctx->PROP_NAME, str, - LEPUS_PROP_CONFIGURABLE); - } - - return napi_clear_last_error(env); -} - -static __attribute__((unused)) std::string GetExceptionMessage( - LEPUSContext *ctx, LEPUSValueConst exception_val) { - LEPUSValue val; - const char *stack; - const char *message = LEPUS_ToCString(ctx, exception_val); - std::string ret = "quickjs: "; - if (message) { - ret += message; - ret += "\n"; - JS_FreeCString_Comp(ctx, message); - } - - bool is_error = LEPUS_IsError(ctx, exception_val); - if (is_error) { - val = LEPUS_GetPropertyStr(ctx, exception_val, "stack"); - if (!LEPUS_IsUndefined(val)) { - stack = LEPUS_ToCString(ctx, val); - ret += stack; - JS_FreeCString_Comp(ctx, stack); - } - JS_FreeValue_Comp(ctx, val); - } - return ret; -} - -napi_status napi_define_class(napi_env env, const char *utf8name, size_t length, - napi_callback cb, void *data, - size_t property_count, - const napi_property_descriptor *properties, napi_value *result) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - LEPUSContext *ctx = env->ctx->ctx; - - qjsimpl::Value proto(ctx, LEPUS_NewObject(ctx)); - - CHECK_QJS(env, !LEPUS_IsException(proto)); - - struct ClassData { - napi_callback cb; - void *data; - LEPUSValue proto; - qjsimpl::NAPIPersistent p_proto; - - ~ClassData() { p_proto.Reset(true); } - }; - - qjsimpl::NativeInfo *ctor_info; - qjsimpl::Value ctor_magic(ctx, qjsimpl::External::Create(env, &ctor_info)); - CHECK_QJS(env, !LEPUS_IsException(ctor_magic)); - - ClassData *ctor_magic_data = - new ClassData{.cb = cb, .data = data, .proto = proto.dup()}; - if (LEPUS_IsGCMode(env->ctx->ctx)) { - ctor_magic_data->p_proto.Reset(env, ctor_magic_data->proto, nullptr, - env->ctx->ctx, true); - } - ctor_info->Data(ctor_magic_data); - qjsimpl::Reference::New( - env, ctor_magic, ctor_info, 0, true, - [](napi_env env, void *data, void *hint) { - ClassData *ctor_magic_data = static_cast(data); - JS_FreeValue_Comp(env->ctx->ctx, ctor_magic_data->proto); - delete ctor_magic_data; - static_cast(hint)->Data(nullptr); - }, - ctor_magic_data, ctor_info); - LEPUSValue cfunction = LEPUS_NewCFunctionMagic( - ctx, - [](LEPUSContext *ctx, LEPUSValueConst new_target, int argc, - LEPUSValueConst *argv, int magic) -> LEPUSValue { - JSAtom prop_ctor_magic = LEPUS_NewAtom(ctx, "@#ctor@#"); - LEPUSValue ctor_magic = - LEPUS_GetProperty(ctx, new_target, prop_ctor_magic); - JS_FreeAtom_Comp(ctx, prop_ctor_magic); - if (LEPUS_IsException(ctor_magic) || LEPUS_IsUndefined(ctor_magic)) { - if (LEPUS_IsObject(new_target)) { - LOGI("new_target is an object"); - } - LOGI("new_target ptr is " - << LEPUS_VALUE_GET_PTR(new_target) << ", prop_ctor_magic is " - << prop_ctor_magic << ", function magic is " << magic - << ", exception message: " - << GetExceptionMessage(ctx, ctor_magic)); - return ctor_magic; - } - qjsimpl::NativeInfo *info = qjsimpl::NativeInfo::Get(ctor_magic); - JS_FreeValue_Comp(ctx, ctor_magic); - if (!(info != nullptr && - info->Type() == qjsimpl::NativeType::External && info->Data())) { - LOGI("ctor_magic native_info error return undefined, info is " - << info); - return LEPUS_UNDEFINED; - } - napi_env env = info->Env(); - ClassData *class_data = static_cast(info->Data()); - LEPUSValue this_val = qjsimpl::Wrapper::Create(env, class_data->proto); - - if (LEPUS_IsException(this_val)) { - LOGI("create Wrapper return exception"); - return this_val; - } - napi_clear_last_error(env); - - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - ArgsConverter args(argc, argv); - - napi_callback_info__ cbinfo{}; - cbinfo.thisArg = env->ctx->CreateHandle(this_val); - cbinfo.newTarget = ToNapi(&new_target); - cbinfo.argc = argc; - cbinfo.argv = args; - cbinfo.data = class_data->data; - - auto result = CallJSFunctionWithNAPI(env, class_data->cb, &cbinfo); - if (LEPUS_IsUndefined(result)) { - LOGI("napi callback return undefined"); - } - return result; - }, - utf8name, 0, LEPUS_CFUNC_constructor_magic, env->ctx->PROP_CTOR_MAGIC); - - qjsimpl::Value constructor(ctx, cfunction); - - auto ctor_handle = env->ctx->CreateHandle(cfunction, true); - - if (LEPUS_IsException(constructor)) { - napi_status status = - napi_set_exception(env, LEPUS_GetException(env->ctx->ctx)); - LOGI(GetExceptionMessage(env->ctx->ctx, *env->ctx->last_exception)); - return status; - } - - if (LEPUS_DefinePropertyValue(ctx, constructor, env->ctx->PROP_CTOR_MAGIC, - ctor_magic.move(), 0) == -1) { - napi_status status = - napi_set_exception(env, LEPUS_GetException(env->ctx->ctx)); - LOGI(GetExceptionMessage(env->ctx->ctx, *env->ctx->last_exception)); - return status; - } - - - CHECK_QJS( - env, LEPUS_DefinePropertyValue(ctx, constructor, env->ctx->PROP_PROTOTYPE, - proto.dup(), 0) != -1); - CHECK_QJS(env, LEPUS_DefinePropertyValue( - ctx, proto, env->ctx->PROP_CONSTRUCTOR, constructor.dup(), - LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE) != -1); - - int instancePropertyCount{0}; - int staticPropertyCount{0}; - for (size_t i = 0; i < property_count; i++) { - if ((properties[i].attributes & napi_static) != 0) { - staticPropertyCount++; - } else { - instancePropertyCount++; - } - } - - std::vector staticDescriptors{}; - std::vector instanceDescriptors{}; - staticDescriptors.reserve(staticPropertyCount); - instanceDescriptors.reserve(instancePropertyCount); - - for (size_t i = 0; i < property_count; i++) { - if ((properties[i].attributes & napi_static) != 0) { - staticDescriptors.push_back(properties[i]); - } else { - instanceDescriptors.push_back(properties[i]); - } - } - - if (staticPropertyCount > 0) { - LEPUSValue ctor_val = constructor; - - CHECK_NAPI(napi_define_properties(env, ToNapi(&ctor_val), - staticDescriptors.size(), - staticDescriptors.data())); - } - - if (instancePropertyCount > 0) { - LEPUSValue proto_val = proto; - - CHECK_NAPI(napi_define_properties(env, ToNapi(&proto_val), - instanceDescriptors.size(), - instanceDescriptors.data())); - } - - - *result = scope.Escape(ctor_handle); - - return napi_clear_last_error(env); -} - -napi_status napi_get_property_names_gc(napi_env env, napi_value object, - napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSPropertyEnum *props = nullptr; - HandleScope func_scope(ctx, &props, HANDLE_TYPE_HEAP_OBJ); - uint32_t props_length; - CHECK_QJS(env, LEPUS_GetOwnPropertyNames( - ctx, &props, &props_length, ToJSValue(object), - LEPUS_GPN_STRING_MASK | LEPUS_GPN_SYMBOL_MASK | - LEPUS_GPN_ENUM_ONLY | LEPUS_PROP_THROW) != -1); - - std::vector values; - values.reserve(props_length); - for (uint32_t i = 0; i < props_length; i++) { - values.emplace_back(LEPUS_AtomToValue(ctx, props[i].atom)); - func_scope.PushHandle(&values[i], HANDLE_TYPE_LEPUS_VALUE); - } - LEPUSValue arr = LEPUS_NewArrayWithValue(ctx, props_length, values.data()); - - CHECK_QJS(env, !LEPUS_IsException(arr)); - *result = env->ctx->CreateHandle(arr); - return napi_clear_last_error(env); -} - -napi_status napi_get_property_names(napi_env env, - napi_value object, - napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - if (LEPUS_IsGCMode(ctx)) { - return napi_get_property_names_gc(env, object, result); - } - LEPUSPropertyEnum *props = nullptr; - uint32_t props_length; - CHECK_QJS(env, LEPUS_GetOwnPropertyNames( - ctx, &props, &props_length, ToJSValue(object), - LEPUS_GPN_STRING_MASK | LEPUS_GPN_SYMBOL_MASK | - LEPUS_GPN_ENUM_ONLY | LEPUS_PROP_THROW) != -1); - - std::vector values; - values.reserve(props_length); - for (uint32_t i = 0; i < props_length; i++) { - values.emplace_back(LEPUS_AtomToValue(ctx, props[i].atom)); - JS_FreeAtom_Comp(ctx, props[i].atom); - } - js_free_comp(ctx, props); - LEPUSValue arr = LEPUS_NewArrayWithValue(ctx, props_length, values.data()); - for (LEPUSValue v: values) { - JS_FreeValue_Comp(ctx, v); - } - - CHECK_QJS(env, !LEPUS_IsException(arr)); - *result = env->ctx->CreateHandle(arr); - return napi_clear_last_error(env); -} - -napi_status napi_get_all_property_names(napi_env env, - napi_value object, - napi_key_collection_mode key_mode, - napi_key_filter key_filter, - napi_key_conversion key_conversion, - napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - if (LEPUS_IsGCMode(ctx)) { - return napi_get_property_names_gc(env, object, result); - } - LEPUSPropertyEnum *props = nullptr; - uint32_t props_length; - CHECK_QJS(env, LEPUS_GetOwnPropertyNames( - ctx, &props, &props_length, ToJSValue(object), - LEPUS_GPN_STRING_MASK | LEPUS_GPN_SYMBOL_MASK | - LEPUS_GPN_ENUM_ONLY | LEPUS_PROP_THROW) != -1); - - std::vector values; - values.reserve(props_length); - for (uint32_t i = 0; i < props_length; i++) { - values.emplace_back(LEPUS_AtomToValue(ctx, props[i].atom)); - JS_FreeAtom_Comp(ctx, props[i].atom); - } - js_free_comp(ctx, props); - LEPUSValue arr = LEPUS_NewArrayWithValue(ctx, props_length, values.data()); - for (LEPUSValue v: values) { - JS_FreeValue_Comp(ctx, v); - } - - CHECK_QJS(env, !LEPUS_IsException(arr)); - *result = env->ctx->CreateHandle(arr); - return napi_clear_last_error(env); -} - -napi_status napi_set_property(napi_env env, napi_value object, napi_value key, - napi_value value) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); - CHECK_QJS(env, prop_atom.IsValid()); - int result = LEPUS_SetProperty(ctx, obj, prop_atom, - JS_DupValue_Comp(ctx, ToJSValue(value))); - CHECK_QJS(env, result != -1); - return napi_clear_last_error(env); -} - -napi_status napi_has_property(napi_env env, napi_value object, napi_value key, - bool *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); - CHECK_QJS(env, prop_atom.IsValid()); - int result_has = LEPUS_HasProperty(ctx, obj, prop_atom); - CHECK_QJS(env, result_has != -1); - *result = result_has; - return napi_clear_last_error(env); -} - -napi_status napi_get_property(napi_env env, napi_value object, napi_value key, - napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); - CHECK_QJS(env, prop_atom.IsValid()); - LEPUSValue val = LEPUS_GetProperty(ctx, obj, prop_atom); - CHECK_QJS(env, !LEPUS_IsException(val)); - *result = env->ctx->CreateHandle(val); - return napi_clear_last_error(env); -} - -napi_status napi_delete_property(napi_env env, napi_value object, - napi_value key, bool *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); - CHECK_QJS(env, prop_atom.IsValid()); - int result_delete = - LEPUS_DeleteProperty(ctx, obj, prop_atom, LEPUS_PROP_THROW); - CHECK_QJS(env, result_delete != -1); - if (result) { - *result = result_delete; - } - return napi_clear_last_error(env); -} - -napi_status napi_has_own_property(napi_env env, napi_value object, - napi_value key, bool *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); - CHECK_QJS(env, prop_atom.IsValid()); - int result_has = LEPUS_GetOwnProperty(ctx, nullptr, obj, prop_atom); - CHECK_QJS(env, result_has != -1); - *result = result_has; - return napi_clear_last_error(env); -} - -napi_status napi_set_named_property(napi_env env, napi_value object, - const char *utf8name, napi_value value) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, LEPUS_NewAtom(ctx, utf8name)); - CHECK_QJS(env, prop_atom.IsValid()); - int result = LEPUS_SetProperty(ctx, obj, prop_atom, - JS_DupValue_Comp(ctx, ToJSValue(value))); - CHECK_QJS(env, result != -1); - return napi_clear_last_error(env); -} - -napi_status napi_has_named_property(napi_env env, napi_value object, - const char *utf8name, bool *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, utf8name); - CHECK_QJS(env, prop_atom.IsValid()); - int result_has = LEPUS_HasProperty(ctx, obj, prop_atom); - CHECK_QJS(env, result_has != -1); - *result = result_has; - return napi_clear_last_error(env); -} - -napi_status napi_get_named_property(napi_env env, napi_value object, - const char *utf8name, napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, utf8name); - CHECK_QJS(env, prop_atom.IsValid()); - LEPUSValue val = LEPUS_GetProperty(ctx, obj, prop_atom); - CHECK_QJS(env, !LEPUS_IsException(val)); - *result = env->ctx->CreateHandle(val); - return napi_clear_last_error(env); -} - -napi_status napi_set_element(napi_env env, napi_value object, uint32_t index, - napi_value value) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - int result = LEPUS_SetPropertyUint32(ctx, obj, index, - JS_DupValue_Comp(ctx, ToJSValue(value))); - CHECK_QJS(env, result != -1); - return napi_clear_last_error(env); -} - -napi_status napi_has_element(napi_env env, napi_value object, uint32_t index, - bool *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - LEPUSValue val = LEPUS_GetPropertyUint32(ctx, obj, index); - CHECK_QJS(env, !LEPUS_IsException(val)); - *result = !LEPUS_IsUndefined(val); - JS_FreeValue_Comp(ctx, val); - return napi_clear_last_error(env); -} - -napi_status napi_get_element(napi_env env, napi_value object, uint32_t index, - napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - LEPUSValue val = LEPUS_GetPropertyUint32(ctx, obj, index); - - CHECK_QJS(env, !LEPUS_IsException(val)); - *result = env->ctx->CreateHandle(val); - return napi_clear_last_error(env); -} - -napi_status napi_delete_element(napi_env env, napi_value object, uint32_t index, - bool *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - qjsimpl::Atom prop_atom(env, ctx, LEPUS_NewAtomUInt32(ctx, index)); - CHECK_QJS(env, prop_atom.IsValid()); - int result_delete = - LEPUS_DeleteProperty(ctx, obj, prop_atom, LEPUS_PROP_THROW); - CHECK_QJS(env, result_delete != -1); - *result = result_delete; - return napi_clear_last_error(env); -} - -napi_status napi_define_properties(napi_env env, napi_value object, - size_t property_count, - const napi_property_descriptor *properties) { - if (property_count > 0) { - CHECK_ARG(env, properties); - } - - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue obj = ToJSValue(object); - - for (size_t i = 0; i < property_count; i++) { - const napi_property_descriptor &p = properties[i]; - qjsimpl::Atom prop_atom = qjsimpl::qjsAtomFromPropertyDescriptor(env, p); - CHECK_QJS(env, prop_atom.IsValid()); - uint8_t flags = qjsimpl::qjsFlagFromPropertyDescriptor(p.attributes); - if (p.getter != nullptr || p.setter != nullptr) { - LEPUSValue getter = LEPUS_UNDEFINED; - char name_buf[128]; - memset(name_buf, 0, sizeof(name_buf)); - if (p.getter) { - napi_value napi_getter; - if (p.utf8name) { - snprintf(name_buf, sizeof(name_buf), "get %s", p.utf8name); - } - CHECK_NAPI(napi_create_function(env, name_buf, NAPI_AUTO_LENGTH, - p.getter, p.data, &napi_getter)); - getter = JS_DupValue_Comp(ctx, ToJSValue(napi_getter)); - } - LEPUSValue setter = LEPUS_UNDEFINED; - if (p.setter) { - napi_value napi_setter; - if (p.utf8name) { - snprintf(name_buf, sizeof(name_buf), "set %s", p.utf8name); - } - CHECK_NAPI(napi_create_function(env, name_buf, NAPI_AUTO_LENGTH, - p.setter, p.data, &napi_setter)); - setter = JS_DupValue_Comp(ctx, ToJSValue(napi_setter)); - } - CHECK_QJS(env, LEPUS_DefinePropertyGetSet(ctx, obj, prop_atom, getter, - setter, flags) != -1); - } else if (p.method != nullptr) { - napi_value method; - CHECK_NAPI(napi_create_function(env, p.utf8name, NAPI_AUTO_LENGTH, - p.method, p.data, &method)); - CHECK_QJS(env, - LEPUS_DefinePropertyValue( - ctx, obj, prop_atom, - JS_DupValue_Comp(ctx, ToJSValue(method)), flags) != -1); - } else { - LEPUSValue value = JS_DupValue_Comp(ctx, ToJSValue(p.value)); - CHECK_QJS(env, LEPUS_DefinePropertyValue(ctx, obj, prop_atom, value, - flags) != -1); - } - } - - return napi_clear_last_error(env); -} - -napi_status napi_is_array(napi_env env, napi_value value, bool *result) { - int result_is = LEPUS_IsArray(env->ctx->ctx, ToJSValue(value)); - CHECK_QJS(env, result_is != -1); - *result = result_is; - return napi_clear_last_error(env); -} - -napi_status napi_get_array_length(napi_env env, napi_value value, - uint32_t *result) { - LEPUSContext *ctx = env->ctx->ctx; - LEPUSValue v = - LEPUS_GetProperty(ctx, ToJSValue(value), env->ctx->PROP_LENGTH); - CHECK_QJS(env, !LEPUS_IsException(v)); - int result_toint = LEPUS_ToUint32(ctx, result, v); - JS_FreeValue_Comp(ctx, v); - CHECK_QJS(env, result_toint != -1); - return napi_clear_last_error(env); -} - -napi_status napi_equals(napi_env env, napi_value lhs, napi_value rhs, - bool *result) { - LEPUSValue a = ToJSValue(lhs); - LEPUSValue b = ToJSValue(rhs); - LEPUSContext *ctx = env->ctx->ctx; - *result = LEPUS_SameValue(ctx, a, b); - - return napi_clear_last_error(env); -} - -napi_status napi_strict_equals(napi_env env, napi_value lhs, napi_value rhs, - bool *result) { - LEPUSValue a = ToJSValue(lhs); - LEPUSValue b = ToJSValue(rhs); - LEPUSContext *ctx = env->ctx->ctx; - *result = - LEPUS_StrictEq(ctx, JS_DupValue_Comp(ctx, a), JS_DupValue_Comp(ctx, b)); - return napi_clear_last_error(env); -} - -napi_status napi_get_prototype(napi_env env, napi_value object, - napi_value *result) { - LEPUSValueConst prototype = - LEPUS_GetPrototype(env->ctx->ctx, ToJSValue(object)); - CHECK_QJS(env, !LEPUS_IsException(prototype)); - *result = env->ctx->CreateHandle(JS_DupValue_Comp(env->ctx->ctx, prototype)); - return napi_clear_last_error(env); -} - -napi_status napi_create_object(napi_env env, napi_value *result) { - LEPUSValue object = LEPUS_NewObject(env->ctx->ctx); - CHECK_QJS(env, !LEPUS_IsException(object)); - *result = env->ctx->CreateHandle(object); - return napi_clear_last_error(env); -} - -napi_status napi_create_array(napi_env env, napi_value *result) { - LEPUSValue array = LEPUS_NewArray(env->ctx->ctx); - CHECK_QJS(env, !LEPUS_IsException(array)); - *result = env->ctx->CreateHandle(array); - return napi_clear_last_error(env); -} - -napi_status napi_create_array_with_length(napi_env env, size_t length, - napi_value *result) { - LEPUSValue array = LEPUS_NewArray(env->ctx->ctx); - CHECK_QJS(env, !LEPUS_IsException(array)); - - *result = env->ctx->CreateHandle(array); - CHECK_QJS(env, - LEPUS_SetProperty(env->ctx->ctx, array, env->ctx->PROP_LENGTH, - LEPUS_NewInt64(env->ctx->ctx, length)) != -1); - - return napi_clear_last_error(env); -} - -napi_status napi_create_string_latin1(napi_env env, const char *str, - size_t length, napi_value *result) { - *result = env->ctx->CreateHandle( - length == NAPI_AUTO_LENGTH - ? LEPUS_NewString(env->ctx->ctx, str) - : LEPUS_NewStringLen(env->ctx->ctx, str, length)); - return napi_clear_last_error(env); -} - -napi_status napi_create_string_utf8(napi_env env, const char *str, - size_t length, napi_value *result) { - *result = env->ctx->CreateHandle( - length == NAPI_AUTO_LENGTH - ? LEPUS_NewString(env->ctx->ctx, str) - : LEPUS_NewStringLen(env->ctx->ctx, str, length)); - return napi_clear_last_error(env); -} - -napi_status napi_create_string_utf16(napi_env env, const char16_t *str, - size_t length, napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewWString( - env->ctx->ctx, reinterpret_cast(str), - length == NAPI_AUTO_LENGTH ? std::char_traits::length(str) - : length)); - return napi_clear_last_error(env); -} - -napi_status napi_create_double(napi_env env, double value, napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewFloat64(env->ctx->ctx, value)); - return napi_clear_last_error(env); -} - -napi_status napi_create_int32(napi_env env, int32_t value, napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewInt32(env->ctx->ctx, value)); - return napi_clear_last_error(env); -} - -napi_status napi_create_uint32(napi_env env, uint32_t value, - napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewInt64(env->ctx->ctx, value)); - return napi_clear_last_error(env); -} - -napi_status napi_create_int64(napi_env env, int64_t value, napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewInt64(env->ctx->ctx, value)); - return napi_clear_last_error(env); -} - -napi_status napi_get_boolean(napi_env env, bool value, napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewBool(env->ctx->ctx, value)); - return napi_clear_last_error(env); -} - -napi_status napi_create_symbol(napi_env env, napi_value description, - napi_value *result) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - napi_value global{}, symbol_func{}, symbol_value{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "Symbol", &symbol_func)); - CHECK_NAPI(napi_call_function(env, global, symbol_func, 1, &description, - &symbol_value)); - *result = scope.Escape(symbol_value); - return napi_clear_last_error(env); -} - -napi_status napi_create_error(napi_env env, napi_value code, napi_value msg, - napi_value *result) { - LEPUSValue error = LEPUS_NewError(env->ctx->ctx); - *result = env->ctx->CreateHandle(error); - - CHECK_NAPI(napi_set_error_msg_code(env, ToNapi(&error), code, msg, nullptr)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_type_error(napi_env env, napi_value code, - napi_value msg, napi_value *result) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - napi_value global{}, error_ctor{}, error{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "TypeError", &error_ctor)); - CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); - CHECK_NAPI(napi_set_error_msg_code(env, error, code, msg, nullptr)); - - *result = scope.Escape(error); - return napi_clear_last_error(env); -} - -napi_status napi_create_range_error(napi_env env, napi_value code, - napi_value msg, napi_value *result) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - napi_value global{}, error_ctor{}, error{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "RangeError", &error_ctor)); - CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); - CHECK_NAPI(napi_set_error_msg_code(env, error, code, msg, nullptr)); - - *result = scope.Escape(error); - return napi_clear_last_error(env); -} - -napi_status napi_typeof(napi_env env, napi_value value, - napi_valuetype *result) { - LEPUSValue v = ToJSValue(value); - int64_t tag = LEPUS_VALUE_GET_NORM_TAG(v); - - switch (tag) { - case LEPUS_TAG_INT: - case LEPUS_TAG_FLOAT64: - *result = napi_number; - break; - case LEPUS_TAG_BIG_INT: - *result = napi_bigint; - break; - case LEPUS_TAG_STRING: - *result = napi_string; - break; - case LEPUS_TAG_SEPARABLE_STRING: - *result = napi_string; - break; - case LEPUS_TAG_SYMBOL: - *result = napi_symbol; - break; - case LEPUS_TAG_NULL: - *result = napi_null; - break; - case LEPUS_TAG_UNDEFINED: - *result = napi_undefined; - break; - case LEPUS_TAG_BOOL: - *result = napi_boolean; - break; - case LEPUS_TAG_OBJECT: - if (LEPUS_IsFunction(env->ctx->ctx, v)) { - *result = napi_function; - } else { - qjsimpl::NativeInfo *info = qjsimpl::NativeInfo::Get(v); - if (info && info->Type() == qjsimpl::NativeType::External) { - *result = napi_external; - } else { - *result = napi_object; - } - } - break; - default: - // Should not get here unless QuickJS has added some new kind of value. - return napi_set_last_error(env, napi_invalid_arg); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_undefined(napi_env env, napi_value *result) { - *result = ToNapi(&(env->ctx->V_UNDEFINED)); - return napi_clear_last_error(env); -} - -napi_status napi_get_null(napi_env env, napi_value *result) { - *result = ToNapi(&(env->ctx->V_NULL)); - return napi_clear_last_error(env); -} - -napi_status napi_get_cb_info( - napi_env env, // [in] NAPI environment handle - napi_callback_info cbinfo, // [in] Opaque callback-info handle - size_t *argc, // [in-out] Specifies the size of the provided argv array - // and receives the actual count of args. - napi_value *argv, // [out] Array of values - napi_value *this_arg, // [out] Receives the JS 'this' arg for the call - void **data) { // [out] Receives the data pointer for the callback. - if (argv != nullptr) { - CHECK_ARG(env, argc); - - size_t i{0}; - size_t min{std::min(*argc, static_cast(cbinfo->argc))}; - - for (; i < min; i++) { - argv[i] = cbinfo->argv[i]; - } - - if (i < *argc) { - for (; i < *argc; i++) { - argv[i] = ToNapi(&(env->ctx->V_UNDEFINED)); - } - } - } - - if (argc != nullptr) { - *argc = cbinfo->argc; - } - - if (this_arg != nullptr) { - *this_arg = cbinfo->thisArg; - } - - if (data != nullptr) { - *data = cbinfo->data; - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_new_target(napi_env env, napi_callback_info cbinfo, - napi_value *result) { - *result = cbinfo->newTarget; - return napi_clear_last_error(env); -} - -namespace { - inline LEPUSValueConst ToJSValue(napi_value *v) { return ToJSValue(*v); } -} // namespace - -napi_status napi_call_function(napi_env env, napi_value recv, napi_value func, - size_t argc, const napi_value *argv, - napi_value *result) { - if (argc > 0) { - CHECK_ARG(env, argv); - } - - LEPUSContext *ctx = env->ctx->ctx; - - js_enter(env); - - ArgsConverter args( - argc, const_cast(argv)); - - LEPUSValue call_result = - LEPUS_Call(ctx, ToJSValue(func), recv ? ToJSValue(recv) : LEPUS_UNDEFINED, - argc, args); - - - CHECK_QJS(env, !LEPUS_IsException(call_result)); - - if (result) { - *result = env->ctx->CreateHandle(call_result); - } else { - if (!LEPUS_IsGCMode(ctx)) { - LEPUS_FreeValue(ctx, call_result); - } - } - - js_exit(env); - auto rt = LEPUS_GetRuntime(ctx); - if (rt && !rt->current_stack_frame) { - LEPUSContext *pctx = NULL; - int result = 0; - while ((result = LEPUS_ExecutePendingJob(rt, &pctx))) { - if (result < 0) { - return napi_set_exception(env, LEPUS_GetException(pctx)); - } - } - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_global(napi_env env, napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_GetGlobalObject(env->ctx->ctx)); - return napi_clear_last_error(env); -} - -napi_status napi_throw(napi_env env, napi_value error) { - if (env->ctx->last_exception) { - JS_FreeValue_Comp(env->ctx->ctx, *env->ctx->last_exception); - } - env->ctx->last_exception.reset( - new LEPUSValue(JS_DupValue_Comp(env->ctx->ctx, ToJSValue(error)))); - env->ctx->last_exception_pVal.Reset(nullptr, *env->ctx->last_exception, - nullptr, env->ctx->ctx, true); - return napi_clear_last_error(env); -} - -napi_status napi_throw_error(napi_env env, const char *code, const char *msg) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - LEPUSValue code_val = LEPUS_UNDEFINED; - if (code != NULL) { - LEPUS_NewString(env->ctx->ctx, code); - env->ctx->CreateHandle(code_val, true); - } - - LEPUSValue msg_val = LEPUS_NewString(env->ctx->ctx, msg); - env->ctx->CreateHandle(msg_val, true); - - napi_value error{}; - napi_status ret = - napi_create_error(env, ToNapi(&code_val), ToNapi(&msg_val), &error); - JS_FreeValue_Comp(env->ctx->ctx, code_val); - JS_FreeValue_Comp(env->ctx->ctx, msg_val); - - CHECK_NAPI(ret); - - return napi_throw(env, error); -} - -napi_status napi_throw_type_error(napi_env env, const char *code, - const char *msg) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - LEPUSValue code_val = LEPUS_UNDEFINED; - if (code != NULL) { - LEPUS_NewString(env->ctx->ctx, code); - env->ctx->CreateHandle(code_val, true); - } - - LEPUSValue msg_val = LEPUS_NewString(env->ctx->ctx, msg); - env->ctx->CreateHandle(msg_val, true); - - napi_value error{}; - napi_status ret = - napi_create_type_error(env, ToNapi(&code_val), ToNapi(&msg_val), &error); - JS_FreeValue_Comp(env->ctx->ctx, code_val); - JS_FreeValue_Comp(env->ctx->ctx, msg_val); - - CHECK_NAPI(ret); - - return napi_throw(env, error); -} - -napi_status napi_throw_range_error(napi_env env, const char *code, - const char *msg) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - LEPUSValue code_val = LEPUS_UNDEFINED; - if (code != NULL) { - LEPUS_NewString(env->ctx->ctx, code); - env->ctx->CreateHandle(code_val, true); - } - LEPUSValue msg_val = LEPUS_NewString(env->ctx->ctx, msg); - env->ctx->CreateHandle(msg_val, true); - - napi_value error{}; - napi_status ret = - napi_create_range_error(env, ToNapi(&code_val), ToNapi(&msg_val), &error); - - JS_FreeValue_Comp(env->ctx->ctx, code_val); - - JS_FreeValue_Comp(env->ctx->ctx, msg_val); - - CHECK_NAPI(ret); - - return napi_throw(env, error); -} - -napi_status napi_is_error(napi_env env, napi_value value, bool *result) { - *result = LEPUS_IsError(env->ctx->ctx, ToJSValue(value)); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_double(napi_env env, napi_value value, - double *result) { - int ret = LEPUS_ToFloat64(env->ctx->ctx, result, ToJSValue(value)); - - CHECK_QJS(env, ret != -1); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_int32(napi_env env, napi_value value, - int32_t *result) { - int ret = LEPUS_ToInt32(env->ctx->ctx, result, ToJSValue(value)); - - CHECK_QJS(env, ret != -1); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_uint32(napi_env env, napi_value value, - uint32_t *result) { - int ret = LEPUS_ToUint32(env->ctx->ctx, result, ToJSValue(value)); - - CHECK_QJS(env, ret != -1); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_int64(napi_env env, napi_value value, - int64_t *result) { - int ret = LEPUS_ToInt64(env->ctx->ctx, result, ToJSValue(value)); - - CHECK_QJS(env, ret != -1); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_bool(napi_env env, napi_value value, bool *result) { - *result = LEPUS_ToBool(env->ctx->ctx, ToJSValue(value)); - - return napi_clear_last_error(env); -} - -// Copies a JavaScript string into a LATIN-1 string buffer. The result is the -// number of bytes (excluding the null terminator) copied into buf. -// A sufficient buffer size should be greater than the length of string, -// reserving space for null terminator. -// If bufsize is insufficient, the string will be truncated and null terminated. -// If buf is NULL, this method returns the length of the string (in bytes) -// via the result parameter. -// The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_latin1(napi_env env, napi_value value, - char *buf, size_t bufsize, - size_t *result) { - LEPUSValue wstring = LEPUS_ToWString(env->ctx->ctx, ToJSValue(value)); - env->ctx->CreateHandle(wstring, true); - - CHECK_QJS(env, !LEPUS_IsException(wstring)); - - size_t length = LEPUS_GetStringLength(env->ctx->ctx, wstring); - - if (buf == nullptr) { - *result = length; - } else { - const char16_t *chars = reinterpret_cast( - LEPUS_GetStringChars(env->ctx->ctx, wstring)); - size_t size{std::min(length, bufsize - 1)}; - for (size_t i = 0; i < size; ++i) { - const char16_t ch{chars[i]}; - buf[i] = (ch < 256) ? ch : '?'; - } - buf[size] = '\0'; - if (result != nullptr) { - *result = size; - } - } - - JS_FreeValue_Comp(env->ctx->ctx, wstring); - - return napi_clear_last_error(env); -} - -// Copies a JavaScript string into a UTF-8 string buffer. The result is the -// number of bytes (excluding the null terminator) copied into buf. -// A sufficient buffer size should be greater than the length of string, -// reserving space for null terminator. -// If bufsize is insufficient, the string will be truncated and null terminated. -// If buf is NULL, this method returns the length of the string (in bytes) -// via the result parameter. -// The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_utf8(napi_env env, napi_value value, - char *buf, size_t bufsize, - size_t *result) { - size_t length; - const char *str = - LEPUS_ToCStringLen(env->ctx->ctx, &length, ToJSValue(value)); - - CHECK_QJS(env, str); - - if (buf == nullptr) { - *result = length; - } else { - size_t size{std::min(length, bufsize - 1)}; - std::copy(str, str + size, buf); - buf[size] = '\0'; - if (result != nullptr) { - *result = size; - } - } - - JS_FreeCString_Comp(env->ctx->ctx, str); - - return napi_clear_last_error(env); -} - -// Copies a JavaScript string into a UTF-16 string buffer. The result is the -// number of 2-byte code units (excluding the null terminator) copied into buf. -// A sufficient buffer size should be greater than the length of string, -// reserving space for null terminator. -// If bufsize is insufficient, the string will be truncated and null terminated. -// If buf is NULL, this method returns the length of the string (in 2-byte -// code units) via the result parameter. -// The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_utf16(napi_env env, napi_value value, - char16_t *buf, size_t bufsize, - size_t *result) { - LEPUSValue wstring = LEPUS_ToWString(env->ctx->ctx, ToJSValue(value)); - env->ctx->CreateHandle(wstring, true); - - CHECK_QJS(env, !LEPUS_IsException(wstring)); - - size_t length = LEPUS_GetStringLength(env->ctx->ctx, wstring); - - if (buf == nullptr) { - *result = length; - } else { - const char16_t *chars = reinterpret_cast( - LEPUS_GetStringChars(env->ctx->ctx, wstring)); - size_t size{std::min(length, bufsize - 1)}; - std::copy(chars, chars + size, buf); - buf[size] = '\0'; - if (result != nullptr) { - *result = size; - } - } - - JS_FreeValue_Comp(env->ctx->ctx, wstring); - - return napi_clear_last_error(env); -} - -napi_status napi_coerce_to_bool(napi_env env, napi_value value, - napi_value *result) { - *result = env->ctx->CreateHandle(LEPUS_NewBool( - env->ctx->ctx, LEPUS_ToBool(env->ctx->ctx, ToJSValue(value)))); - return napi_clear_last_error(env); -} - -napi_status napi_coerce_to_number(napi_env env, napi_value value, - napi_value *result) { - double number; - int ret = LEPUS_ToFloat64(env->ctx->ctx, &number, ToJSValue(value)); - - CHECK_QJS(env, ret != -1); - - *result = env->ctx->CreateHandle(LEPUS_NewFloat64(env->ctx->ctx, number)); - return napi_clear_last_error(env); -} - -napi_status napi_coerce_to_object(napi_env env, napi_value value, - napi_value *result) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - napi_value global{}, object_func{}, object_value{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_func)); - CHECK_NAPI( - napi_call_function(env, global, object_func, 1, &value, &object_value)); - *result = scope.Escape(object_value); - - return napi_clear_last_error(env); -} - -napi_status napi_coerce_to_string(napi_env env, napi_value value, - napi_value *result) { - LEPUSValue str = LEPUS_ToString(env->ctx->ctx, ToJSValue(value)); - CHECK_QJS(env, !LEPUS_IsException(str)); - *result = env->ctx->CreateHandle(str); - return napi_clear_last_error(env); -} - -napi_status napi_wrap(napi_env env, napi_value js_object, void *native_object, - napi_finalize finalize_cb, void *finalize_hint, - napi_ref *result) { - return qjsimpl::Wrap( - env, js_object, native_object, finalize_cb, finalize_hint, result); -} - -napi_status napi_unwrap(napi_env env, napi_value obj, void **result) { - return qjsimpl::Unwrap(env, obj, result, qjsimpl::KeepWrap); -} - -napi_status napi_remove_wrap(napi_env env, napi_value obj, void **result) { - return qjsimpl::Unwrap(env, obj, result, qjsimpl::RemoveWrap); -} - -napi_status napi_create_external(napi_env env, void *data, - napi_finalize finalize_cb, void *finalize_hint, - napi_value *result) { - qjsimpl::NativeInfo *info; - LEPUSValue value = qjsimpl::External::Create(env, &info); - - CHECK_QJS(env, !LEPUS_IsException(value)); - - info->Data(data); - - qjsimpl::Reference::New(env, value, info, 0, true, finalize_cb, data, - finalize_hint); - - *result = env->ctx->CreateHandle(value); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_external(napi_env env, napi_value value, - void **result) { - qjsimpl::NativeInfo *info = qjsimpl::NativeInfo::Get(ToJSValue(value)); - *result = info != nullptr && info->Type() == qjsimpl::NativeType::External - ? info->Data() - : nullptr; - return napi_clear_last_error(env); -} - -// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. -napi_status napi_create_reference(napi_env env, napi_value value, - uint32_t initial_refcount, napi_ref *result) { - LEPUSValueConst val = ToJSValue(value); - - if (LEPUS_VALUE_GET_NORM_TAG(val) != LEPUS_TAG_OBJECT) { - return napi_set_last_error(env, napi_object_expected); - } - - qjsimpl::Reference *reference = qjsimpl::Reference::New( - env, val, qjsimpl::NativeInfo::Get(val), initial_refcount, false); - - *result = reinterpret_cast(reference); - - return napi_clear_last_error(env); -} - -// Deletes a reference. The referenced value is released, and may be GC'd -// unless there are other references to it. -napi_status napi_delete_reference(napi_env env, napi_ref ref) { - qjsimpl::Reference::Delete(reinterpret_cast(ref)); - - return napi_clear_last_error(env); -} - -// Increments the reference count, optionally returning the resulting count. -// After this call the reference will be a strong reference because its refcount -// is >0, and the referenced object is effectively "pinned". Calling this when -// the refcount is 0 and the target is unavailable results in an error. -napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t *result) { - qjsimpl::Reference *reference = reinterpret_cast(ref); - uint32_t count = reference->Ref(); - - if (result != nullptr) { - *result = count; - } - - return napi_clear_last_error(env); -} - -// Decrements the reference count, optionally returning the resulting count. -// If the result is 0 the reference is now weak and the object may be GC'd at -// any time if there are no other references. Calling this when the refcount -// is already 0 results in an error. -napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t *result) { - qjsimpl::Reference *reference = reinterpret_cast(ref); - - if (reference->RefCount() == 0) { - return napi_set_last_error(env, napi_generic_failure); - } - - uint32_t count = reference->Unref(); - - if (result != nullptr) { - *result = count; - } - - return napi_clear_last_error(env); -} - -// Attempts to get a referenced value. If the reference is weak, the value -// might no longer be available, in that case the call is still successful but -// the result is NULL. -napi_status napi_get_reference_value(napi_env env, napi_ref ref, - napi_value *result) { - qjsimpl::Reference *reference = reinterpret_cast(ref); - - *result = reference->Get(); - return napi_clear_last_error(env); -} - -//// Stub implementation of handle scope apis for QuickLEPUS. -//napi_status napi_open_context_scope(napi_env env, napi_context_scope* result) { -// *result = reinterpret_cast(1); -// return napi_clear_last_error(env); -//} -// -//// Stub implementation of handle scope apis for QuickJS. -//napi_status napi_close_context_scope(napi_env env, napi_context_scope scope) { -// return napi_clear_last_error(env); -//} - -napi_status napi_open_handle_scope(napi_env env, napi_handle_scope *result) { - *result = reinterpret_cast( - new napi_handle_scope__(env, env->ctx->ctx, reset_napi_env)); - env->ctx->open_handle_scopes++; - return napi_clear_last_error(env); -} - -napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { - if (env->ctx->open_handle_scopes == 0) { - return napi_handle_scope_mismatch; - } - env->ctx->open_handle_scopes--; - - delete reinterpret_cast(scope); - return napi_clear_last_error(env); -} - -napi_status napi_open_escapable_handle_scope( - napi_env env, napi_escapable_handle_scope *result) { - *result = reinterpret_cast( - new napi_handle_scope__(env, env->ctx->ctx, reset_napi_env)); - env->ctx->open_handle_scopes++; - return napi_clear_last_error(env); -} - -napi_status napi_close_escapable_handle_scope( - napi_env env, napi_escapable_handle_scope scope) { - if (env->ctx->open_handle_scopes == 0) { - return napi_handle_scope_mismatch; - } - env->ctx->open_handle_scopes--; - - delete reinterpret_cast(scope); - return napi_clear_last_error(env); -} - -napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, - napi_value escapee, napi_value *result) { - *result = reinterpret_cast(scope)->Escape(escapee); - return napi_clear_last_error(env); -} - -napi_status napi_new_instance(napi_env env, napi_value constructor, size_t argc, - const napi_value *argv, napi_value *result) { - if (argc > 0) { - CHECK_ARG(env, argv); - } - - ArgsConverter args( - argc, const_cast(argv)); - - js_enter(env); - - LEPUSValue instance = - LEPUS_CallConstructor(env->ctx->ctx, ToJSValue(constructor), argc, args); - - js_exit(env); - - CHECK_QJS(env, !LEPUS_IsException(instance)); - - *result = env->ctx->CreateHandle(instance); - - return napi_clear_last_error(env); -} - -napi_status napi_instanceof(napi_env env, napi_value object, - napi_value constructor, bool *result) { - int ret = LEPUS_IsInstanceOf(env->ctx->ctx, ToJSValue(object), - ToJSValue(constructor)); - - CHECK_QJS(env, ret != -1); - - *result = ret; - - return napi_clear_last_error(env); -} - -napi_status napi_is_exception_pending(napi_env env, bool *result) { - *result = static_cast(env->ctx->last_exception); - return napi_clear_last_error(env); -} - -napi_status napi_get_and_clear_last_exception(napi_env env, - napi_value *result) { - if (!env->ctx->last_exception) { - return napi_get_undefined(env, result); - } else { - *result = env->ctx->CreateHandle(*env->ctx->last_exception); - env->ctx->last_exception.reset(); - env->ctx->last_exception_pVal.Reset(true); - } - - return napi_clear_last_error(env); -} - -std::string get_lepus_error_stack(LEPUSContext *ctx, LEPUSValue &value) { - std::string err; - if (LEPUS_IsError(ctx, value) || LEPUS_IsException(value)) { - LEPUSValue val = LEPUS_GetPropertyStr(ctx, value, "stack"); - if (!LEPUS_IsUndefined(val)) { - const char *stack = LEPUS_ToCString(ctx, val); - if (stack) { - err.append(stack); - } - JS_FreeCString_Comp(ctx, stack); - } - JS_FreeValue_Comp(ctx, val); - } - return err; -} - -napi_status napi_get_unhandled_rejection_exception(napi_env env, - napi_value *result) { - LEPUSContext *ctx = env->ctx->ctx; - std::string error_result; - while (LEPUS_MoveUnhandledRejectionToException(ctx)) { - LEPUSValue exception = LEPUS_GetException(ctx); - env->ctx->CreateHandle(exception, true); - const char *error_message = LEPUS_ToCString(ctx, exception); - if (error_message) { - error_result.append("message: "); - error_result.append(error_message); - } - JS_FreeCString_Comp(ctx, error_message); - - std::string error_stack = get_lepus_error_stack(ctx, exception); - error_result.append("\nstack: "); - error_result.append(error_stack); - error_result.append("\n"); - } - LEPUSValue result_lepus = LEPUS_NewString(ctx, error_result.c_str()); - *result = env->ctx->CreateHandle(result_lepus); - return napi_clear_last_error(env); -} - -napi_status napi_get_own_property_descriptor(napi_env env, napi_value obj, - napi_value prop, - napi_value *result) { - LEPUSValueConst args[2]; - args[0] = ToJSValue(obj); - args[1] = ToJSValue(prop); - LEPUSValue descriptor = lepus_object_getOwnPropertyDescriptor( - env->ctx->ctx, LEPUS_UNDEFINED, 2, args, 0); - CHECK_QJS(env, !LEPUS_IsException(descriptor)); - *result = env->ctx->CreateHandle(descriptor); - return napi_clear_last_error(env); -} - -napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool *result) { - LEPUSClassID id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); - *result = id == JS_CLASS_ARRAY_BUFFER || id == JS_CLASS_SHARED_ARRAY_BUFFER; - return napi_clear_last_error(env); -} - -napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, - void **data, napi_value *result) { - void *bytes = std::malloc(byte_length); - // v8 use zero initialized - std::memset(bytes, 0, byte_length); - LEPUSValue buffer = LEPUS_NewArrayBuffer( - env->ctx->ctx, static_cast(bytes), byte_length, - [](LEPUSRuntime *rt, void *opaque, void *ptr) { std::free(ptr); }, - nullptr, false); - - if (LEPUS_IsException(buffer)) { - std::free(bytes); - CHECK_QJS(env, false); - } - - *data = bytes; - *result = env->ctx->CreateHandle(buffer); - - return napi_clear_last_error(env); -} - -napi_status napi_create_external_arraybuffer(napi_env env, void *external_data, - size_t byte_length, - napi_finalize finalize_cb, - void *finalize_hint, - napi_value *result) { - LEPUSValue buffer = LEPUS_NewArrayBuffer( - env->ctx->ctx, static_cast(external_data), byte_length, - [](LEPUSRuntime *rt, void *opaque, void *ptr) {}, nullptr, false); - - CHECK_QJS(env, !LEPUS_IsException(buffer)); - - if (finalize_cb != nullptr) { - qjsimpl::Reference::New(env, buffer, nullptr, 0, true, finalize_cb, - external_data, finalize_hint); - } - - *result = env->ctx->CreateHandle(buffer); - - return napi_clear_last_error(env); -} - -napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, - void **data, size_t *byte_length) { - size_t size; - uint8_t *bytes = - LEPUS_GetArrayBuffer(env->ctx->ctx, &size, ToJSValue(arraybuffer)); - - CHECK_QJS(env, bytes); - - if (data) { - *data = static_cast(bytes); - } - if (byte_length) { - *byte_length = size; - } - return napi_clear_last_error(env); -} - -napi_status napi_is_typedarray(napi_env env, napi_value value, bool *result) { - LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); - *result = - class_id >= JS_CLASS_UINT8C_ARRAY && class_id <= JS_CLASS_FLOAT64_ARRAY; - return napi_clear_last_error(env); -} - -#define FOR_EACH_TYPEDARRAY(V) \ - V(napi_uint8_clamped_array, JS_CLASS_UINT8C_ARRAY) \ - V(napi_uint8_array, JS_CLASS_UINT8_ARRAY) \ - V(napi_int8_array, JS_CLASS_INT8_ARRAY) \ - V(napi_int16_array, JS_CLASS_INT16_ARRAY) \ - V(napi_uint16_array, JS_CLASS_UINT16_ARRAY) \ - V(napi_int32_array, JS_CLASS_INT32_ARRAY) \ - V(napi_uint32_array, JS_CLASS_UINT32_ARRAY) \ - V(napi_float32_array, JS_CLASS_FLOAT32_ARRAY) \ - V(napi_float64_array, JS_CLASS_FLOAT64_ARRAY) - -napi_status napi_create_typedarray(napi_env env, napi_typedarray_type type, - size_t length, napi_value arraybuffer, - size_t byte_offset, napi_value *result) { - LEPUSClassID class_id; - - switch (type) { -#define CASE_TYPE(TYPE, CLASS_ID) \ - case TYPE: \ - class_id = CLASS_ID; \ - break; - - FOR_EACH_TYPEDARRAY(CASE_TYPE) - -#undef CASE_TYPE - case napi_bigint64_array: - case napi_biguint64_array: - return napi_set_last_error(env, napi_invalid_arg); - } - - LEPUSValue array = LEPUS_NewTypedArrayWithBuffer( - env->ctx->ctx, ToJSValue(arraybuffer), byte_offset, length, class_id); - - CHECK_QJS(env, !LEPUS_IsException(array)); - - *result = env->ctx->CreateHandle(array); - - return napi_clear_last_error(env); -} - -napi_status napi_is_typedarray_of(napi_env env, napi_value typedarray, - napi_typedarray_type type, bool *result) { - LEPUSClassID class_id = - LEPUS_GetClassID(env->ctx->ctx, ToJSValue(typedarray)); - - switch (type) { -#define CASE_TYPE(TYPE, CLASS_ID) \ - case TYPE: \ - *result = class_id == CLASS_ID; \ - break; - - FOR_EACH_TYPEDARRAY(CASE_TYPE) - -#undef CASE_TYPE - case napi_bigint64_array: - case napi_biguint64_array: - return napi_set_last_error(env, napi_invalid_arg); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray, - napi_typedarray_type *type, size_t *length, - void **data, napi_value *arraybuffer, - size_t *byte_offset) { - LEPUSValueConst typedarray_val = ToJSValue(typedarray); - LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, typedarray_val); - - switch (class_id) { -#define CASE_TYPE(TYPE, CLASS_ID) \ - case CLASS_ID: \ - if (type) *type = TYPE; \ - break; - - FOR_EACH_TYPEDARRAY(CASE_TYPE) - -#undef CASE_TYPE - case napi_bigint64_array: - case napi_biguint64_array: - return napi_set_last_error(env, napi_invalid_arg); - } - - uint32_t byte_offset_num; - - { - LEPUSValue val = LEPUS_GetProperty(env->ctx->ctx, typedarray_val, - env->ctx->PROP_BYTEOFFSET); - CHECK_QJS(env, !LEPUS_IsException(val)); - CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &byte_offset_num, val) != -1); - if (byte_offset) { - *byte_offset = byte_offset_num; - } - } - - if (length) { - LEPUSValue val = - LEPUS_GetProperty(env->ctx->ctx, typedarray_val, env->ctx->PROP_LENGTH); - CHECK_QJS(env, !LEPUS_IsException(val)); - uint32_t length_num; - CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &length_num, val) != -1); - *length = length_num; - } - - if (data || arraybuffer) { - LEPUSValue val = - LEPUS_GetProperty(env->ctx->ctx, typedarray_val, env->ctx->PROP_BUFFER); - CHECK_QJS(env, !LEPUS_IsException(val)); - if (arraybuffer) { - *arraybuffer = env->ctx->CreateHandle(val); - } - if (data) { - size_t unused; - uint8_t *buffer_start = LEPUS_GetArrayBuffer(env->ctx->ctx, &unused, val); - CHECK_QJS(env, buffer_start); - *data = buffer_start + byte_offset_num; - } - } - - return napi_clear_last_error(env); -} - -napi_status napi_create_dataview(napi_env env, size_t byte_length, - napi_value arraybuffer, size_t byte_offset, - napi_value *result) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - napi_value global{}, dataview_ctor{}, data_view{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "DataView", &dataview_ctor)); - - napi_value byte_offset_value{}, byte_length_value{}; - napi_create_double(env, static_cast(byte_offset), &byte_offset_value); - napi_create_double(env, static_cast(byte_length), &byte_length_value); - napi_value args[] = {arraybuffer, byte_offset_value, byte_length_value}; - CHECK_NAPI(napi_new_instance(env, dataview_ctor, 3, args, &data_view)); - - *result = scope.Escape(data_view); - - return napi_clear_last_error(env); -} - -napi_status napi_is_dataview(napi_env env, napi_value value, bool *result) { - LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); - *result = class_id == JS_CLASS_DATAVIEW; - return napi_clear_last_error(env); -} - -napi_status napi_is_date(napi_env env, napi_value value, bool *result) { - LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); - *result = class_id == JS_CLASS_DATE; - return napi_clear_last_error(env); -} - -napi_status napi_get_dataview_info(napi_env env, napi_value dataview, - size_t *byte_length, void **data, - napi_value *arraybuffer, - size_t *byte_offset) { - LEPUSValueConst dataview_val = ToJSValue(dataview); - LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, dataview_val); - - if (class_id != JS_CLASS_DATAVIEW) { - return napi_set_last_error(env, napi_invalid_arg); - } - - uint32_t byte_offset_num; - - { - LEPUSValue val = LEPUS_GetProperty(env->ctx->ctx, dataview_val, - env->ctx->PROP_BYTEOFFSET); - CHECK_QJS(env, !LEPUS_IsException(val)); - CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &byte_offset_num, val) != -1); - if (byte_offset) { - *byte_offset = byte_offset_num; - } - } - - if (byte_length) { - LEPUSValue val = LEPUS_GetProperty(env->ctx->ctx, dataview_val, - env->ctx->PROP_BYTELENGTH); - CHECK_QJS(env, !LEPUS_IsException(val)); - uint32_t byte_length_num; - CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &byte_length_num, val) != -1); - *byte_length = byte_length_num; - } - - if (data || arraybuffer) { - LEPUSValue val = - LEPUS_GetProperty(env->ctx->ctx, dataview_val, env->ctx->PROP_BUFFER); - CHECK_QJS(env, !LEPUS_IsException(val)); - if (arraybuffer) { - *arraybuffer = env->ctx->CreateHandle(val); - } - if (data) { - size_t unused; - uint8_t *buffer_start = LEPUS_GetArrayBuffer(env->ctx->ctx, &unused, val); - CHECK_QJS(env, buffer_start); - *data = buffer_start + byte_offset_num; - } - } - - return napi_clear_last_error(env); -} - -struct napi_deferred__ { - qjsimpl::NAPIPersistent resolve; - qjsimpl::NAPIPersistent reject; - - bool has_init = false; - - static napi_value Callback(napi_env env, napi_callback_info cbinfo) { - napi_deferred__ *deferred = - static_cast(cbinfo->data); - deferred->has_init = true; - deferred->resolve.Reset(env, ToJSValue(cbinfo->argv[0]), nullptr, - env->ctx->ctx, false); - deferred->reject.Reset(env, ToJSValue(cbinfo->argv[1]), nullptr, - env->ctx->ctx, false); - return nullptr; - } - - ~napi_deferred__() { - resolve.Reset(true); - reject.Reset(true); - } -}; - -napi_status napi_create_promise(napi_env env, napi_deferred *deferred, - napi_value *promise) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - napi_value global{}, promise_ctor{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); - - std::unique_ptr deferred_val = - std::make_unique(); - napi_value executor{}, promise_val{}; - CHECK_NAPI(napi_create_function(env, "executor", NAPI_AUTO_LENGTH, - napi_deferred__::Callback, - deferred_val.get(), &executor)); - CHECK_NAPI(napi_new_instance(env, promise_ctor, 1, &executor, &promise_val)); - - if (!deferred_val->has_init) { - return napi_set_last_error(env, napi_generic_failure); - } - - *promise = scope.Escape(promise_val); - *deferred = deferred_val.release(); - - return napi_clear_last_error(env); -} - -napi_status napi_is_promise(napi_env env, napi_value promise, - bool *is_promise) { - napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); - - napi_value global{}, promise_ctor{}; - CHECK_NAPI(napi_get_global(env, &global)); - CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); - CHECK_NAPI(napi_instanceof(env, promise, promise_ctor, is_promise)); - - return napi_clear_last_error(env); -} - -napi_status napi_run_script(napi_env env, napi_value script, - napi_value *result) { - return napi_run_script_source(env, script, "ctx->ctx, &size, ToJSValue(script)); - js_enter(env); - result_val = LEPUS_Eval(env->ctx->ctx, src, size, - source_url, LEPUS_EVAL_TYPE_GLOBAL); - JS_FreeCString_Comp(env->ctx->ctx, src); - js_exit(env); - - CHECK_QJS(env, !LEPUS_IsException(result_val)); - - *result = env->ctx->CreateHandle(result_val); - return napi_clear_last_error(env); -} - -#ifdef ENABLE_CODECACHE - -napi_status napi_run_code_cache(napi_env env, const uint8_t* data, int length, - napi_value* result) { - LEPUSValue result_val = LEPUS_UNDEFINED; - LEPUSValue top_func = - LEPUS_EvalBinary(env->ctx->ctx, data, static_cast(length), - LEPUS_EVAL_BINARY_LOAD_ONLY); - if (!LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)) { - LEPUSValue global = LEPUS_GetGlobalObject(env->ctx->ctx); - env->ctx->CreateHandle(top_func, true); - result_val = LEPUS_EvalFunction(env->ctx->ctx, top_func, global); - } - CHECK_QJS(env, !LEPUS_IsException(result_val)); - - *result = env->ctx->CreateHandle(result_val); - return napi_clear_last_error(env); -} - -napi_status napi_run_script_cache(napi_env env, const char* script, - size_t length, const char* filename, - napi_value* result) { - if (length == NAPI_AUTO_LENGTH) { - length = std::strlen(script); - } - - LEPUSValue result_val = LEPUS_UNINITIALIZED; - { - int len = -1; - const uint8_t* data = nullptr; - env->napi_get_code_cache(env, filename, &data, &len); - if (data) { - // TODO(yang): check whether the script is obsolate - LOG_TIME_START(); - LEPUSValue top_func = - LEPUS_EvalBinary(env->ctx->ctx, data, static_cast(len), - LEPUS_EVAL_BINARY_LOAD_ONLY); - if (!LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)) { - LEPUSValue global = LEPUS_GetGlobalObject(env->ctx->ctx); - env->ctx->CreateHandle(top_func, true); - result_val = LEPUS_EvalFunction(env->ctx->ctx, top_func, global); - } - LOG_TIME_END("----- script eval with cache -----"); - } else if (len == 0) { - // if len is 0, we need to make cache. - // if len is -1, someone is modifying the cache, we do not make cache. - LOG_TIME_START(); - LEPUSValue top_func = - LEPUS_Eval(env->ctx->ctx, script, length, filename, - LEPUS_EVAL_FLAG_COMPILE_ONLY | LEPUS_EVAL_TYPE_GLOBAL); - CHECK_QJS(env, - !LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)); - LEPUSValue global = LEPUS_GetGlobalObject(env->ctx->ctx); - env->ctx->CreateHandle(top_func, true); - - size_t obj_len; - data = LEPUS_WriteObject(env->ctx->ctx, &obj_len, top_func, - LEPUS_WRITE_OBJ_BYTECODE); - env->napi_store_code_cache(env, filename, data, - static_cast(obj_len)); - js_free_comp(env->ctx->ctx, - reinterpret_cast(const_cast(data))); - result_val = LEPUS_EvalFunction(env->ctx->ctx, top_func, global); - LOG_TIME_END( - "---- evaluating %s and making code cache for it lengthed %d -----", - filename, (int)obj_len); - } - } - if (LEPUS_IsUninitialized(result_val)) { - LOG_TIME_START(); - result_val = LEPUS_Eval(env->ctx->ctx, script, length, - filename ? filename : "", LEPUS_EVAL_TYPE_GLOBAL); - LOG_TIME_END("----- script eval without cache -----"); - } - CHECK_QJS(env, !LEPUS_IsException(result_val)); - - *result = env->ctx->CreateHandle(result_val); - return napi_clear_last_error(env); -} - -// `data` memory need to be freed from outside -napi_status napi_gen_code_cache(napi_env env, const char* script, - size_t script_len, const uint8_t** data, - int* length) { - if (script_len == NAPI_AUTO_LENGTH) { - script_len = std::strlen(script); - } - LEPUSValue top_func = - LEPUS_Eval(env->ctx->ctx, script, script_len, "", - LEPUS_EVAL_FLAG_COMPILE_ONLY | LEPUS_EVAL_TYPE_GLOBAL); - CHECK_QJS(env, !LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)); - env->ctx->CreateHandle(top_func, true); - - size_t obj_len; - uint8_t* cache = LEPUS_WriteObject(env->ctx->ctx, &obj_len, top_func, - LEPUS_WRITE_OBJ_BYTECODE); - *data = static_cast(std::malloc(obj_len)); - *length = static_cast(obj_len); - memcpy(static_cast(const_cast(*data)), - static_cast(cache), obj_len); - js_free_comp(env->ctx->ctx, reinterpret_cast(cache)); - - return napi_clear_last_error(env); -} - -#endif // ENABLE_CODECACHE - -napi_status napi_add_finalizer(napi_env env, napi_value js_object, - void *native_object, napi_finalize finalize_cb, - void *finalize_hint, napi_ref *result) { - return qjsimpl::Wrap(env, js_object, native_object, - finalize_cb, finalize_hint, result); -} - -napi_status napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, - int64_t *adjusted_value) { - // TODO: Determine if QJS needs or is able to do anything here - // For now, we can lie and say that we always adjusted more memory - *adjusted_value = change_in_bytes; - - return napi_clear_last_error(env); -} - -napi_status napi_set_instance_data(napi_env env, uint64_t key, void *data, - napi_finalize finalize_cb, - void *finalize_hint) { - auto it = env->ctx->instance_data_registry.find(key); - if (it != env->ctx->instance_data_registry.end()) { - return napi_invalid_arg; - } - - env->ctx->instance_data_registry[key] = - qjsimpl::RefBase::New(env, 0, true, finalize_cb, data, finalize_hint); - - return napi_clear_last_error(env); -} - -napi_status napi_get_instance_data(napi_env env, uint64_t key, void **data) { - auto it = env->ctx->instance_data_registry.find(key); - if (it == env->ctx->instance_data_registry.end()) { - *data = nullptr; - } else { - qjsimpl::RefBase *idata = static_cast(it->second); - - *data = idata->Data(); - } - - return napi_clear_last_error(env); -} - -void napi_attach_quickjs(napi_env env, LEPUSContext *context) { - env->ctx = new napi_context__(env, context); - - InitNapiScope(context); -} - -void napi_detach_quickjs(napi_env env) { - LEPUSContext *ctx = env->ctx->ctx; - delete env->ctx; - env->ctx = nullptr; - FreeNapiScope(ctx); -} - -LEPUSContext *napi_get_env_context_quickjs(napi_env env) { - return env->ctx->ctx; -} - -LEPUSValue napi_js_value_to_quickjs_value(napi_env env, napi_value value) { - return JS_DupValue_Comp(env->ctx->ctx, ToJSValue(value)); -} - -napi_value napi_quickjs_value_to_js_value(napi_env env, LEPUSValue value) { - return env->ctx->CreateHandle(value); -} - -inline napi_handle_scope__::napi_handle_scope__(napi_env env, LEPUSContext *ctx, - napi_func *func) - : env_(env), ctx_(ctx), handle_tail_(nullptr), reset_napi_env(func) { - is_gc = env_->ctx->gc_enable; - if (is_gc) { - prev_ = reinterpret_cast(GetNapiScope(ctx_)); - SetNapiScope(ctx_, this); - } else { - prev_ = env_->ctx->handle_scope; - env_->ctx->handle_scope = this; - } -} - -inline napi_value napi_handle_scope__::CreateHandle(LEPUSValue v) { - Handle *h = new Handle{.value = v, .prev = handle_tail_}; - handle_tail_ = h; - return ToNapi(&(h->value)); -} - -inline napi_value napi_handle_scope__::Escape(napi_value v) { - return prev_->CreateHandle(JS_DupValue_Comp(env_->ctx->ctx, ToJSValue(v))); -} - -napi_status primjs_execute_pending_jobs(napi_env env) { - int error; - do { - LEPUSContext *context; - error = LEPUS_ExecutePendingJob(LEPUS_GetRuntime(env->ctx->ctx), &context); - if (error == -1) { - return napi_set_last_error(env, napi_pending_exception); - } - } while (error != 0); - - return napi_clear_last_error(env); -} - - -typedef struct NapiHostObjectInfo { - void *data; - napi_ref ref; - napi_finalize finalize_cb; - bool is_array; - napi_ref getter; - napi_ref setter; -} NapiHostObjectInfo; - -void host_object_finalizer(LEPUSRuntime *rt, LEPUSValue value) { - napi_env env = (napi_env) napi_context__::GetEnv(rt); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(value, - env->ctx->napiHostObjectClassId); - if (info->finalize_cb) { - info->finalize_cb(env, info->data, NULL); - } - if (info->is_array) { - napi_delete_reference(env, info->getter); - napi_delete_reference(env, info->setter); - } - - napi_delete_reference(env, info->ref); - delete info; -} - -int host_object_set(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom, - LEPUSValue value, LEPUSValue receiver, int flags) { - napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, - env->ctx->napiHostObjectClassId); - if (info != NULL) { - auto *target = reinterpret_cast(info->ref); - if (info->is_array) { - LEPUSValue atom_val = LEPUS_AtomToValue(ctx, atom); - - auto *setter = reinterpret_cast(info->setter); - - LEPUSValue argv[4] = { - ToJSValue(target->Get()), - atom_val, - value, - obj - }; - - LEPUSValue result = LEPUS_Call(ctx, ToJSValue(setter->Get()), LEPUS_UNDEFINED, 4, argv); - - JS_FreeValue_Comp(ctx, atom_val); - - if (LEPUS_IsException(result)) return -1; - - return true; - } - return LEPUS_SetProperty(ctx, ToJSValue(target->Get()), atom, JS_DupValue_Comp(ctx, value)); - } - return true; -} - -LEPUSValue host_object_get(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom, LEPUSValue receiver) { - napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, - env->ctx->napiHostObjectClassId); - if (info != NULL) { - auto *target = reinterpret_cast(info->ref); - if (info->is_array) { - LEPUSValue atom_val = LEPUS_AtomToValue(ctx, atom); - auto *getter = reinterpret_cast(info->getter); - LEPUSValue argv[3] = { - ToJSValue(target->Get()), - atom_val, - obj - }; - LEPUSValue value = LEPUS_Call(ctx, ToJSValue(getter->Get()), LEPUS_UNDEFINED, 3, argv); - JS_FreeValue_Comp(ctx, atom_val); - return value; - } - return LEPUS_GetProperty(ctx, ToJSValue(target->Get()), atom); - } - return LEPUS_UNDEFINED; -} - -int host_object_has(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom) { - napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, - env->ctx->napiHostObjectClassId); - if (info != NULL) { - auto *target = reinterpret_cast(info->ref); - return LEPUS_HasProperty(ctx, ToJSValue(target->Get()), atom); - } - return false; -} - -static int host_object_delete(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom) { - napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, - env->ctx->napiHostObjectClassId); - if (info != NULL) { - auto *target = reinterpret_cast(info->ref); - return LEPUS_DeleteProperty(ctx, ToJSValue(target->Get()), atom, 0); - } - return true; -} - -LEPUSClassExoticMethods NapiHostObjectExoticMethods = { - .get_own_property = nullptr, - .get_own_property_names = nullptr, - .delete_property = host_object_delete, - .define_own_property = nullptr, - .has_property = host_object_has, - .get_property = host_object_get, - .set_property = host_object_set, -}; - -napi_status -napi_create_host_object(napi_env env, napi_value value, napi_finalize finalize, void *data, - bool is_array, napi_value getter, napi_value setter, napi_value *result) { - CHECK_ARG(env, result); - - if (env->ctx->napi_host_object_class_init == 0) { - LEPUSClassDef NapiHostObjectClassDef = {"NapiHostObject", host_object_finalizer, NULL, NULL, - &NapiHostObjectExoticMethods}; - LEPUS_NewClass(env->ctx->rt, env->ctx->napiHostObjectClassId, &NapiHostObjectClassDef); - env->ctx->napi_host_object_class_init = 1; - } - - napi_value constructor; - napi_get_named_property(env, value, "constructor", &constructor); - - napi_value prototype; - napi_get_named_property(env, constructor, "prototype", &prototype); - - LEPUSValue jsValue = LEPUS_NewObjectClass(env->ctx->ctx, env->ctx->napiHostObjectClassId); - LEPUS_SetPrototype(env->ctx->ctx, jsValue, ToJSValue(prototype)); - - NapiHostObjectInfo *info = new NapiHostObjectInfo; - info->data = data; - if (finalize) { - info->finalize_cb = finalize; - } else { - info->finalize_cb = NULL; - } - info->is_array = is_array; - - if (is_array) { - if (getter) napi_create_reference(env, getter, 1, &info->getter); - if (setter) napi_create_reference(env, setter, 1, &info->setter); - } - - napi_create_reference(env, value, 1, &info->ref); - - LEPUS_SetOpaque(jsValue, info); - - *result = env->ctx->CreateHandle(jsValue); - return napi_ok; -} - -napi_status napi_get_host_object_data(napi_env env, napi_value object, void **data) { - CHECK_ARG(env, object); - CHECK_ARG(env, data); - - LEPUSValue jsValue = ToJSValue(object); - - - if (!LEPUS_IsObject(jsValue)) { - return napi_set_last_error(env, napi_object_expected); - } - - NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(jsValue, - env->ctx->napiHostObjectClassId); - if (info) { - *data = info->data; - } else { - *data = NULL; - } - - return napi_clear_last_error(env); -} - -napi_status napi_is_host_object(napi_env env, napi_value object, bool *result) { - CHECK_ARG(env, object); - - LEPUSValue jsValue = ToJSValue(object); - - if (!LEPUS_IsObject(jsValue)) { - return napi_set_last_error(env, napi_object_expected); - } - - void *data = LEPUS_GetOpaque(jsValue, - env->ctx->napiHostObjectClassId); - if (data != NULL) { - *result = true; - } else { - *result = false; - } - - return napi_clear_last_error(env); -} \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/primjs/primjs-api.h b/test-app/runtime/src/main/cpp/napi/primjs/primjs-api.h deleted file mode 100644 index c7953eef..00000000 --- a/test-app/runtime/src/main/cpp/napi/primjs/primjs-api.h +++ /dev/null @@ -1,572 +0,0 @@ -/** - * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. - * - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file in the root of the source tree. - */ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -#ifndef SRC_NAPI_QUICKJS_JS_NATIVE_API_QUICKJS_H_ -#define SRC_NAPI_QUICKJS_JS_NATIVE_API_QUICKJS_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus -#include "quickjs/include/quickjs.h" -#ifdef __cplusplus -} -#endif // __cplusplus - -#include "gc/persistent-handle.h" -#include "gc/trace-gc.h" -#include "js_native_api.h" -#include "js_native_api_types.h" -#include "napi_env_quickjs.h" - - -typedef struct napi_env_data__ *napi_env_data; -typedef struct napi_state__ { - napi_extended_error_info last_error; - napi_env_data env_data; -} napi_state; - -typedef struct napi_context__ *napi_context; - -struct napi_env__ { - napi_state state; - napi_runtime rt; - napi_context ctx; - int js_enter_state; -}; - -inline napi_status napi_clear_last_error(napi_env env) { - env->state.last_error.error_code = napi_ok; - env->state.last_error.engine_error_code = 0; - return napi_ok; -} - -inline napi_status napi_set_last_error(napi_env env, napi_status error_code) { - env->state.last_error.error_code = error_code; - return error_code; -} - -inline napi_value ToNapi(LEPUSValueConst *v) { - return reinterpret_cast(v); -} - -inline LEPUSValueConst ToJSValue(napi_value v) { - return *reinterpret_cast(v); -} - -inline LEPUSValue JS_DupValue_Comp(LEPUSContext *ctx, LEPUSValueConst v) { - if (!LEPUS_IsGCMode(ctx)) { - return LEPUS_DupValue(ctx, v); - } - return v; -} - -inline void JS_FreeValue_Comp(LEPUSContext *ctx, LEPUSValue v) { - if (!LEPUS_IsGCMode(ctx)) { - LEPUS_FreeValue(ctx, v); - } -} - -inline void JS_FreeAtom_Comp(LEPUSContext *ctx, JSAtom v) { - if (!LEPUS_IsGCMode(ctx)) { - LEPUS_FreeAtom(ctx, v); - } -} - -inline void JS_FreeCString_Comp(LEPUSContext *ctx, const char *ptr) { - if (!LEPUS_IsGCMode(ctx)) { - LEPUS_FreeCString(ctx, ptr); - } -} - -inline void js_free_comp(LEPUSContext *ctx, void *ptr) { - if (!LEPUS_IsGCMode(ctx)) { - lepus_free(ctx, ptr); - } -} - -namespace qjsimpl { - - class NAPIPersistent; - - class NativeInfo; - - struct WeakInfo { - std::list::const_iterator weak_iter; - std::function cb; - void *cb_arg; - }; - - class NAPIPersistent : public PersistentBase { - public: - /** - * A Persistent with no storage cell. - */ - inline NAPIPersistent() - : PersistentBase(nullptr), - _env(nullptr), - _empty(true), - _native_info(nullptr), - _ctx(nullptr) {} - - /** - * Construct a NAPIPersistent from a Local. - * When the Local is non-empty, a new storage cell is created - * pointing to the same object, and no flags are set. - */ - inline NAPIPersistent(napi_env env, LEPUSValueConst value, - NativeInfo *native_info, LEPUSContext *ctx, - bool is_weak = false); - - inline NAPIPersistent(napi_env env, JSAtom atom, NativeInfo *native_info, - LEPUSContext *ctx, bool is_weak = false); - - NAPIPersistent(const NAPIPersistent &that) = delete; - - NAPIPersistent &operator=(const NAPIPersistent &that) = delete; - - void Reset(bool for_gc = false); - - void Reset(napi_env env, LEPUSValueConst value, NativeInfo *native_info, - LEPUSContext *ctx, bool for_gc = false); - - void Reset(napi_env env, LEPUSContext *ctx, JSAtom atom); - - void SetWeak(void *data, void (*cb)(void *)); - - void ClearWeak(); - - inline ~NAPIPersistent() { Reset(); } - - LEPUSValue Value() const; - - bool IsEmpty() { - if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { - return val_ == nullptr; - } else { - return _empty; - } - } - - static void OnFinalize(NAPIPersistent *ref); - - private: - inline LEPUSValue *operator*() const { return this->val_; } - - void ResetWeakInfo(); - - NativeInfo *_get_native_info(); - - napi_env _env; - bool _empty; - LEPUSValue _value; - NativeInfo *_native_info; - std::unique_ptr _weak_info; - LEPUSContext *_ctx; - }; - - class Atom { - public: - Atom() : _env(nullptr), _ctx(nullptr), _atom(0) {} - - Atom(napi_env env, LEPUSContext *ctx, LEPUSValueConst value) - : _env(env), _ctx(ctx), _atom(LEPUS_ValueToAtom(ctx, value)) { - _atom_persist.Reset(_env, _ctx, _atom); - } - - Atom(napi_env env, LEPUSContext *ctx, JSAtom atom) - : _env(env), _ctx(ctx), _atom(atom) { - _atom_persist.Reset(_env, _ctx, _atom); - } - - Atom(napi_env env, LEPUSContext *ctx, const char *str, - size_t length = NAPI_AUTO_LENGTH) - : _env(env), - _ctx(ctx), - _atom(length == NAPI_AUTO_LENGTH ? LEPUS_NewAtom(ctx, str) - : LEPUS_NewAtomLen(ctx, str, length)) { - _atom_persist.Reset(_env, _ctx, _atom); - } - - Atom(Atom &other) - : _env(other._env), - _ctx(other._ctx), - _atom(LEPUS_DupAtom(_ctx, other._atom)) { - _atom_persist.Reset(_env, _ctx, _atom); - } - - Atom(Atom &&other) : _env(other._env), _ctx(other._ctx), _atom(other._atom) { - other._env = nullptr; - other._ctx = nullptr; - other._atom = 0; - other._atom_persist.Reset(true); - _atom_persist.Reset(_env, _ctx, _atom); - } - - ~Atom() { - if (_atom) { - JS_FreeAtom_Comp(_ctx, _atom); - } - _atom_persist.Reset(true); - } - - bool IsValid() { return _atom > 0; } - - operator JSAtom() const { return _atom; } - - private: - napi_env _env; - LEPUSContext *_ctx; - JSAtom _atom; - NAPIPersistent _atom_persist; - }; - - class Value { - public: - Value() : _ctx(nullptr), _val(), pVal(), is_gc(false) {} - - Value(LEPUSContext *ctx, LEPUSValue val) : _ctx(ctx), _val(val), pVal() { - if (ctx) { - is_gc = LEPUS_IsGCMode(ctx); - } - if (is_gc) { - pVal.Reset(nullptr, val, nullptr, ctx, true); - } - } - - ~Value() { - if (is_gc) { - pVal.Reset(true); - } else if (_ctx) { - JS_FreeValue_Comp(_ctx, _val); - } - } - - operator LEPUSValueConst() { - if (is_gc) { - return pVal.Value(); - } else { - return _val; - } - } - - LEPUSValue dup() { - if (is_gc) { - return pVal.Value(); - } else { - return JS_DupValue_Comp(_ctx, _val); - } - } - - LEPUSValue move() { - if (!is_gc) { - _ctx = nullptr; - return _val; - } else { - return pVal.Value(); - } - } - - private: - LEPUSContext *_ctx; - LEPUSValue _val; - NAPIPersistent pVal; - bool is_gc; - }; - - class RefTracker { - public: - RefTracker() {} - - virtual ~RefTracker() {} - - virtual void Finalize(bool isEnvTeardown) {} - - typedef RefTracker RefList; - - inline void Link(RefList *list) { - prev_ = list; - next_ = list->next_; - if (next_ != nullptr) { - next_->prev_ = this; - } - list->next_ = this; - } - - inline void Unlink() { - if (prev_ != nullptr) { - prev_->next_ = next_; - } - if (next_ != nullptr) { - next_->prev_ = prev_; - } - prev_ = nullptr; - next_ = nullptr; - } - - static void FinalizeAll(RefList *list) { - while (list->next_ != nullptr) { - list->next_->Finalize(true); - } - } - - private: - RefList *next_ = nullptr; - RefList *prev_ = nullptr; - }; - - class Reference; - -} // namespace qjsimpl - -inline void reset_napi_env(napi_env env, napi_handle_scope__ *scope); - - - -struct napi_context__ { - napi_env env; - LEPUSRuntime *rt{}; - LEPUSContext *ctx{}; - - LEPUSValue V_NULL{LEPUS_NULL}; - LEPUSValue V_UNDEFINED{LEPUS_UNDEFINED}; - - napi_context__(napi_env env, LEPUSContext *ctx) - : env(env), - rt{LEPUS_GetRuntime(ctx)}, - ctx{ctx}, - PROP_NAME(env, ctx, "name"), - PROP_LENGTH(env, ctx, "length"), - PROP_PROTOTYPE(env, ctx, "prototype"), - PROP_CONSTRUCTOR(env, ctx, "constructor"), - PROP_FINALIZER(env, ctx, "@#fin@#"), - PROP_MESSAGE(env, ctx, "message"), - PROP_CODE(env, ctx, "code"), - PROP_BUFFER(env, ctx, "buffer"), - PROP_BYTELENGTH(env, ctx, "byteLength"), - PROP_BYTEOFFSET(env, ctx, "byteOffset"), - PROP_CTOR_MAGIC(env, ctx, "@#ctor@#"), - gc_enable(LEPUS_IsGCModeRT(rt)) { - env->ctx = this; - napi_context__::rt_to_env_cache.emplace(rt, env); - handle_scope = new napi_handle_scope__(env, ctx, reset_napi_env); - - LEPUSValue gc = LEPUS_NewCFunction(ctx, [](LEPUSContext *ctx, LEPUSValueConst this_val, - int argc, LEPUSValueConst *argv) -> LEPUSValue { - LEPUS_RunGC(LEPUS_GetRuntime(ctx)); - return LEPUS_UNDEFINED; - }, "gc", 0); - auto globalValue = LEPUS_GetGlobalObject(ctx); - LEPUS_SetPropertyStr(ctx, globalValue, "gc", gc); - JS_FreeValue_Comp(ctx, globalValue); - LEPUS_NewClassID(&napiHostObjectClassId); - - // TODO primjs doesn't expose DupContext - } - - ~napi_context__() { - qjsimpl::RefTracker::FinalizeAll(&finalizing_reflist); - qjsimpl::RefTracker::FinalizeAll(&reflist); - - // root handle scope may be used during FinalizeAll - // must delete at last - delete handle_scope; - rt_to_env_cache.erase(rt); - } - - inline void Ref() { refs++; } - - inline void Unref() { - if (--refs == 0) delete this; - } - - template> - inline void CallIntoModule(T &&call, U &&handle_exception) { - int open_handle_scopes_before = open_handle_scopes; - (void) open_handle_scopes_before; - napi_clear_last_error(this->env); - call(this->env); - assert(open_handle_scopes == open_handle_scopes_before); - if (last_exception) { - handle_exception(this->env, *last_exception); - last_exception.reset(); - last_exception_pVal.Reset(true); - } - } - - void CallFinalizer(napi_finalize cb, void *data, void *hint) { - cb(this->env, data, hint); - } - - qjsimpl::RefTracker::RefList reflist; - qjsimpl::RefTracker::RefList finalizing_reflist; - - std::unique_ptr last_exception; - qjsimpl::NAPIPersistent last_exception_pVal; - - std::unordered_map instance_data_registry; - - int napi_host_object_class_init = 0; - LEPUSClassID napiHostObjectClassId = 0; - static std::unordered_map rt_to_env_cache; - - static napi_env GetEnv(LEPUSRuntime* rt) { - auto it = rt_to_env_cache.find(rt); - if (it == rt_to_env_cache.end()) return nullptr; - return it->second; - } - - int open_handle_scopes = 0; - - const qjsimpl::Atom PROP_NAME; - const qjsimpl::Atom PROP_LENGTH; - const qjsimpl::Atom PROP_PROTOTYPE; - const qjsimpl::Atom PROP_CONSTRUCTOR; - const qjsimpl::Atom PROP_FINALIZER; - const qjsimpl::Atom PROP_MESSAGE; - const qjsimpl::Atom PROP_CODE; - const qjsimpl::Atom PROP_BUFFER; - const qjsimpl::Atom PROP_BYTELENGTH; - const qjsimpl::Atom PROP_BYTEOFFSET; - const qjsimpl::Atom PROP_CTOR_MAGIC; - - napi_value CreateHandle(LEPUSValue v, bool only_gc = false) { - if (LEPUS_IsGCMode(ctx)) { - return static_cast(GetNapiScope(ctx))->CreateHandle(v); - } else if (!only_gc) { - return handle_scope->CreateHandle(v); - } - return {}; - } - - void set_handle_scope(napi_handle_scope__ *scope) { handle_scope = scope; } - -private: - int refs = 1; - napi_handle_scope__ *handle_scope{}; - bool gc_enable; - - friend class napi_handle_scope__; -}; - -inline void reset_napi_env(napi_env env, napi_handle_scope__ *scope) { - env->ctx->set_handle_scope(scope); -} - -struct napi_class__qjs { - napi_class__qjs(LEPUSContext *ctx, LEPUSValue proto, LEPUSValue constructor) - : ctx(ctx), proto(proto), constructor(constructor) { - if (LEPUS_IsGCMode(ctx)) { - proto_persist.Reset(nullptr, proto, nullptr, ctx, true); - constructor_persist.Reset(nullptr, constructor, nullptr, ctx, true); - } - } - - ~napi_class__qjs() { - if (LEPUS_IsGCMode(ctx)) { - proto_persist.Reset(true); - constructor_persist.Reset(true); - } else { - LEPUS_FreeValue(ctx, proto); - LEPUS_FreeValue(ctx, constructor); - } - } - - LEPUSValue GetFunction() { return JS_DupValue_Comp(ctx, constructor); } - - LEPUSContext *ctx; - - LEPUSValue proto; - qjsimpl::NAPIPersistent proto_persist; - LEPUSValue constructor; - qjsimpl::NAPIPersistent constructor_persist; -}; - -#define RETURN_STATUS_IF_FALSE(env, condition, status) \ - do { \ - if (!(condition)) { \ - return napi_set_last_error((env), (status)); \ - } \ - } while (0) - -#define CHECK_ARG(env, arg) \ - RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) - -#define CHECK_QJS(env, condition) \ - do { \ - if (!(condition)) { \ - return napi_set_exception(env, LEPUS_GetException(env->ctx->ctx)); \ - } \ - } while (0) - -// This does not call napi_set_last_error because the expression -// is assumed to be a NAPI function call that already did. -#define CHECK_NAPI(expr) \ - do { \ - napi_status status = (expr); \ - if (status != napi_ok) return status; \ - } while (0) - -namespace qjsimpl { -// Adapter for napi_finalize callbacks. - class Finalizer { - public: - // Some Finalizers are run during shutdown when the napi_env is destroyed, - // and some need to keep an explicit reference to the napi_env because they - // are run independently. - enum EnvReferenceMode { - kNoEnvReference, kKeepEnvReference - }; - - protected: - Finalizer(napi_env env, napi_finalize finalize_callback, void *finalize_data, - void *finalize_hint, EnvReferenceMode refmode = kNoEnvReference) - : _env(env), - _finalize_callback(finalize_callback), - _finalize_data(finalize_data), - _finalize_hint(finalize_hint), - _has_env_reference(refmode == kKeepEnvReference) { - if (_has_env_reference) _env->ctx->Ref(); - } - - ~Finalizer() { - if (_has_env_reference) _env->ctx->Unref(); - } - - public: - static Finalizer *New(napi_env env, napi_finalize finalize_callback = nullptr, - void *finalize_data = nullptr, - void *finalize_hint = nullptr, - EnvReferenceMode refmode = kNoEnvReference) { - return new Finalizer(env, finalize_callback, finalize_data, finalize_hint, - refmode); - } - - static void Delete(Finalizer *finalizer) { delete finalizer; } - - protected: - napi_env _env; - napi_finalize _finalize_callback; - void *_finalize_data; - void *_finalize_hint; - bool _finalize_ran = false; - bool _has_env_reference = false; - }; - -} // namespace qjsimpl - -#endif // SRC_NAPI_QUICKJS_JS_NATIVE_API_QUICKJS_H_ diff --git a/test-app/runtime/src/main/cpp/napi/primjs/primjs_napi_vtable.h b/test-app/runtime/src/main/cpp/napi/primjs/primjs_napi_vtable.h new file mode 100644 index 00000000..f032b224 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/primjs/primjs_napi_vtable.h @@ -0,0 +1,520 @@ +// PrimJS Node-API vtable. +// +// `struct napi_env__` below is the function-pointer table that the prebuilt +// primjs `libnapi.so` installs on every napi_env via the exported +// napi_attach_quickjs(). The adapter (js_native_api_adapter.cc) forwards the +// standard-named napi_* C entry points into these slots. +// +// This is a verbatim mirror of `struct napi_env__` in primjs' +// src/napi/js_native_api.h, built with ENABLE_CODECACHE=ON. KEEP IT IN SYNC: +// append new members at the END only -- never reorder or remove -- to preserve +// the ABI the shipped libnapi.so expects. + +#ifndef TEST_APP_PRIMJS_NAPI_VTABLE_H +#define TEST_APP_PRIMJS_NAPI_VTABLE_H + +#include "js_native_api.h" // standard napi types (napi_env, napi_value, ...) + +#include +#include +#include +#include + +// --- primjs-internal handle/callback types. Only their pointer/enum size is +// --- relevant here: every use below is a vtable *slot* (function pointer) or a +// --- leading env pointer, so opaque placeholders keep the layout ABI-exact. +typedef void* napi_state; +typedef void* napi_runtime; +typedef void* napi_context; +typedef void* napi_context_scope; +typedef void* napi_error_scope; +typedef void* napi_class; +typedef void* napi_async_work; +typedef void* napi_threadsafe_function; +typedef void (*napi_async_execute_callback)(napi_env env, void* data); +typedef void (*napi_async_complete_callback)(napi_env env, napi_status status, + void* data); +typedef void (*napi_threadsafe_function_call_js)(napi_env env, napi_value cb, + void* ctx, void* data); +typedef int napi_threadsafe_function_call_mode; +typedef int napi_status_legacy; + +typedef enum { + napi_deferred_resolve, + napi_deferred_reject, + napi_deferred_delete +} napi_deferred_release_mode; + +struct napi_env__ { + napi_state state; + napi_runtime rt; + napi_context ctx; + + // Warning: Keep in-sync with macros in napi_macro.h! + // Always append function at the end to keep ABI compatible! + + napi_status (*napi_get_version)(napi_env env, uint32_t* result); + + // ENGINE CALL + // Getters for defined singletons + napi_status (*napi_get_undefined)(napi_env env, napi_value* result); + napi_status (*napi_get_null)(napi_env env, napi_value* result); + napi_status (*napi_get_global)(napi_env env, napi_value* result); + napi_status (*napi_get_boolean)(napi_env env, bool value, napi_value* result); + + // Methods to create Primitive types/Objects + napi_status (*napi_create_object)(napi_env env, napi_value* result); + napi_status (*napi_create_array)(napi_env env, napi_value* result); + napi_status (*napi_create_array_with_length)(napi_env env, size_t length, + napi_value* result); + napi_status (*napi_create_double)(napi_env env, double value, + napi_value* result); + napi_status (*napi_create_int32)(napi_env env, int32_t value, + napi_value* result); + napi_status (*napi_create_uint32)(napi_env env, uint32_t value, + napi_value* result); + napi_status (*napi_create_int64)(napi_env env, int64_t value, + napi_value* result); + napi_status (*napi_create_string_latin1)(napi_env env, const char* str, + size_t length, napi_value* result); + napi_status (*napi_create_string_utf8)(napi_env env, const char* str, + size_t length, napi_value* result); + napi_status (*napi_create_string_utf16)(napi_env env, const char16_t* str, + size_t length, napi_value* result); + napi_status (*napi_create_symbol)(napi_env env, napi_value description, + napi_value* result); + napi_status (*napi_create_function)(napi_env env, const char* utf8name, + size_t length, napi_callback cb, + void* data, napi_value* result); + napi_status (*napi_create_error)(napi_env env, napi_value code, + napi_value msg, napi_value* result); + napi_status (*napi_create_type_error)(napi_env env, napi_value code, + napi_value msg, napi_value* result); + napi_status (*napi_create_range_error)(napi_env env, napi_value code, + napi_value msg, napi_value* result); + + // Methods to get the native napi_value from Primitive type + napi_status (*napi_typeof)(napi_env env, napi_value value, + napi_valuetype* result); + napi_status (*napi_get_value_double)(napi_env env, napi_value value, + double* result); + napi_status (*napi_get_value_int32)(napi_env env, napi_value value, + int32_t* result); + napi_status (*napi_get_value_uint32)(napi_env env, napi_value value, + uint32_t* result); + napi_status (*napi_get_value_int64)(napi_env env, napi_value value, + int64_t* result); + napi_status (*napi_get_value_bool)(napi_env env, napi_value value, + bool* result); + + // Copies LATIN-1 encoded bytes from a string into a buffer. + napi_status (*napi_get_value_string_latin1)(napi_env env, napi_value value, + char* buf, size_t bufsize, + size_t* result); + + // Copies UTF-8 encoded bytes from a string into a buffer. + napi_status (*napi_get_value_string_utf8)(napi_env env, napi_value value, + char* buf, size_t bufsize, + size_t* result); + + // Copies UTF-16 encoded bytes from a string into a buffer. + napi_status (*napi_get_value_string_utf16)(napi_env env, napi_value value, + char16_t* buf, size_t bufsize, + size_t* result); + + // Methods to coerce values + // These APIs may execute user scripts + napi_status (*napi_coerce_to_bool)(napi_env env, napi_value value, + napi_value* result); + napi_status (*napi_coerce_to_number)(napi_env env, napi_value value, + napi_value* result); + napi_status (*napi_coerce_to_object)(napi_env env, napi_value value, + napi_value* result); + napi_status (*napi_coerce_to_string)(napi_env env, napi_value value, + napi_value* result); + + // Methods to work with Objects + napi_status (*napi_get_prototype)(napi_env env, napi_value object, + napi_value* result); + napi_status (*napi_get_property_names)(napi_env env, napi_value object, + napi_value* result); + napi_status (*napi_set_property)(napi_env env, napi_value object, + napi_value key, napi_value value); + napi_status (*napi_has_property)(napi_env env, napi_value object, + napi_value key, bool* result); + napi_status (*napi_get_property)(napi_env env, napi_value object, + napi_value key, napi_value* result); + napi_status (*napi_delete_property)(napi_env env, napi_value object, + napi_value key, bool* result); + napi_status (*napi_has_own_property)(napi_env env, napi_value object, + napi_value key, bool* result); + napi_status (*napi_set_named_property)(napi_env env, napi_value object, + const char* utf8name, + napi_value value); + napi_status (*napi_has_named_property)(napi_env env, napi_value object, + const char* utf8name, bool* result); + napi_status (*napi_get_named_property)(napi_env env, napi_value object, + const char* utf8name, + napi_value* result); + napi_status (*napi_set_element)(napi_env env, napi_value object, + uint32_t index, napi_value value); + napi_status (*napi_has_element)(napi_env env, napi_value object, + uint32_t index, bool* result); + napi_status (*napi_get_element)(napi_env env, napi_value object, + uint32_t index, napi_value* result); + napi_status (*napi_delete_element)(napi_env env, napi_value object, + uint32_t index, bool* result); + + napi_status (*napi_define_properties)( + napi_env env, napi_value object, size_t property_count, + const napi_property_descriptor* properties); + + // Methods to work with Arrays + napi_status (*napi_is_array)(napi_env env, napi_value value, bool* result); + napi_status (*napi_get_array_length)(napi_env env, napi_value value, + uint32_t* result); + + // Methods to compare values + napi_status (*napi_strict_equals)(napi_env env, napi_value lhs, + napi_value rhs, bool* result); + + // Methods to work with Functions + napi_status (*napi_call_function)(napi_env env, napi_value recv, + napi_value func, size_t argc, + const napi_value* argv, napi_value* result); + napi_status (*napi_new_instance)(napi_env env, napi_value constructor, + size_t argc, const napi_value* argv, + napi_value* result); + napi_status (*napi_instanceof)(napi_env env, napi_value object, + napi_value constructor, bool* result); + + // Methods to work with napi_callbacks + + // Gets all callback info in a single call. (Ugly, but faster.) + napi_status (*napi_get_cb_info)( + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + void** data); // [out] Receives the data pointer for the callback. + + napi_status (*napi_get_new_target)(napi_env env, napi_callback_info cbinfo, + napi_value* result); + + napi_status (*napi_define_class)(napi_env env, const char* utf8name, + size_t length, napi_callback constructor, + void* data, size_t property_count, + const napi_property_descriptor* properties, + napi_class super_class, napi_class* result); + + napi_status (*napi_release_class)(napi_env env, napi_class clazz); + + napi_status (*napi_class_get_function)(napi_env env, napi_class clazz, + napi_value* result); + + // Methods to work with external data objects + napi_status (*napi_wrap)(napi_env env, napi_value js_object, + void* native_object, napi_finalize finalize_cb, + void* finalize_hint, napi_ref* result); + napi_status (*napi_unwrap)(napi_env env, napi_value js_object, void** result); + napi_status (*napi_remove_wrap)(napi_env env, napi_value js_object, + void** result); + napi_status (*napi_create_external)(napi_env env, void* data, + napi_finalize finalize_cb, + void* finalize_hint, napi_value* result); + napi_status (*napi_get_value_external)(napi_env env, napi_value value, + void** result); + + // Methods to control object lifespan + + // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. + napi_status (*napi_create_reference)(napi_env env, napi_value value, + uint32_t initial_refcount, + napi_ref* result); + + // Deletes a reference. The referenced value is released, and may + // be GC'd unless there are other references to it. + napi_status (*napi_delete_reference)(napi_env env, napi_ref ref); + + // Increments the reference count, optionally returning the resulting count. + // After this call the reference will be a strong reference because its + // refcount is >0, and the referenced object is effectively "pinned". + // Calling this when the refcount is 0 and the object is unavailable + // results in an error. + napi_status (*napi_reference_ref)(napi_env env, napi_ref ref, + uint32_t* result); + + // Decrements the reference count, optionally returning the resulting count. + // If the result is 0 the reference is now weak and the object may be GC'd + // at any time if there are no other references. Calling this when the + // refcount is already 0 results in an error. + napi_status (*napi_reference_unref)(napi_env env, napi_ref ref, + uint32_t* result); + + // Attempts to get a referenced value. If the reference is weak, + // the value might no longer be available, in that case the call + // is still successful but the result is NULL. + napi_status (*napi_get_reference_value)(napi_env env, napi_ref ref, + napi_value* result); + + napi_status (*napi_open_handle_scope)(napi_env env, + napi_handle_scope* result); + napi_status (*napi_close_handle_scope)(napi_env env, napi_handle_scope scope); + napi_status (*napi_open_escapable_handle_scope)( + napi_env env, napi_escapable_handle_scope* result); + napi_status (*napi_close_escapable_handle_scope)( + napi_env env, napi_escapable_handle_scope scope); + + napi_status (*napi_escape_handle)(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, napi_value* result); + + // Methods to support error handling + napi_status (*napi_throw_)(napi_env env, napi_value error); + napi_status (*napi_throw_error)(napi_env env, const char* code, + const char* msg); + napi_status (*napi_throw_type_error)(napi_env env, const char* code, + const char* msg); + napi_status (*napi_throw_range_error)(napi_env env, const char* code, + const char* msg); + napi_status (*napi_is_error)(napi_env env, napi_value value, bool* result); + + // Methods to support catching exceptions + napi_status (*napi_is_exception_pending)(napi_env env, bool* result); + napi_status (*napi_get_and_clear_last_exception)(napi_env env, + napi_value* result); + + // Methods to work with array buffers and typed arrays + napi_status (*napi_is_arraybuffer)(napi_env env, napi_value value, + bool* result); + napi_status (*napi_create_arraybuffer)(napi_env env, size_t byte_length, + void** data, napi_value* result); + napi_status (*napi_create_external_arraybuffer)( + napi_env env, void* external_data, size_t byte_length, + napi_finalize finalize_cb, void* finalize_hint, napi_value* result); + napi_status (*napi_get_arraybuffer_info)(napi_env env, napi_value arraybuffer, + void** data, size_t* byte_length); + napi_status (*napi_is_typedarray)(napi_env env, napi_value value, + bool* result); + napi_status (*napi_create_typedarray)(napi_env env, napi_typedarray_type type, + size_t length, napi_value arraybuffer, + size_t byte_offset, napi_value* result); + + napi_status (*napi_is_typedarray_of)(napi_env env, napi_value typedarray, + napi_typedarray_type type, bool* result); + + napi_status (*napi_get_typedarray_info)(napi_env env, napi_value typedarray, + napi_typedarray_type* type, + size_t* length, void** data, + napi_value* arraybuffer, + size_t* byte_offset); + + napi_status (*napi_create_dataview)(napi_env env, size_t length, + napi_value arraybuffer, + size_t byte_offset, napi_value* result); + napi_status (*napi_is_dataview)(napi_env env, napi_value value, bool* result); + napi_status (*napi_get_dataview_info)(napi_env env, napi_value dataview, + size_t* bytelength, void** data, + napi_value* arraybuffer, + size_t* byte_offset); + + // Promises + napi_status (*napi_create_promise)(napi_env env, napi_deferred* deferred, + napi_value* promise); + napi_status (*napi_release_deferred)(napi_env env, napi_deferred deferred, + napi_value resolution, + napi_deferred_release_mode mode); + napi_status (*napi_is_promise)(napi_env env, napi_value value, + bool* is_promise); + + // Running a script + napi_status (*napi_run_script)(napi_env env, const char* script, + size_t length, const char* filename, + napi_value* result); + + // Memory management + napi_status (*napi_adjust_external_memory)(napi_env env, + int64_t change_in_bytes, + int64_t* adjusted_value); + + // Add finalizer for pointer + napi_status (*napi_add_finalizer)(napi_env env, napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, napi_ref* result); + + // Instance data + napi_status_legacy (*napi_set_instance_data)(napi_env env, uint64_t key, + void* data, + napi_finalize finalize_cb, + void* finalize_hint); + + napi_status (*napi_get_instance_data)(napi_env env, uint64_t key, + void** data); + + // ENGINE CALL END + + // Universal CALL + napi_status (*napi_get_last_error_info)( + napi_env env, const napi_extended_error_info** result); + + napi_status (*napi_add_env_cleanup_hook)(napi_env env, void (*fun)(void* arg), + void* arg); + napi_status (*napi_remove_env_cleanup_hook)(napi_env env, + void (*fun)(void* arg), + void* arg); + + napi_status (*napi_create_async_work)(napi_env env, napi_value async_resource, + napi_value async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, napi_async_work* result); + + napi_status (*napi_delete_async_work)(napi_env env, napi_async_work work); + napi_status (*napi_queue_async_work)(napi_env env, napi_async_work work); + napi_status (*napi_cancel_async_work)(napi_env env, napi_async_work work); + + // Calling into JS from other threads + napi_status (*napi_create_threadsafe_function)( + napi_env env, void* thread_finalize_data, + napi_finalize thread_finalize_cb, void* context, + napi_threadsafe_function_call_js call_js_cb, + napi_threadsafe_function* result); + + napi_status (*napi_get_threadsafe_function_context)( + napi_threadsafe_function func, void** result); + + napi_status (*napi_call_threadsafe_function)( + napi_threadsafe_function func, void* data, + napi_threadsafe_function_call_mode is_blocking); + + // no need to acquire tsfn, just hold the pointer and release in any thread + napi_status (*napi_acquire_threadsafe_function)( + napi_threadsafe_function func); + + napi_status (*napi_delete_threadsafe_function)(napi_threadsafe_function func); + + // no need to ref thread + napi_status (*napi_unref_threadsafe_function)( + napi_env env, napi_threadsafe_function func); + + // no need to ref thread + napi_status (*napi_ref_threadsafe_function)( + napi_env env, napi_threadsafe_function func); + + napi_status (*napi_get_loader)(napi_env env, napi_value* result); + + // UNIVERSAL CALL END + + napi_status (*napi_open_context_scope)(napi_env env, + napi_context_scope* result); + napi_status_legacy (*napi_close_context_scope)(napi_env env, + napi_context_scope scope); + + napi_status (*napi_open_error_scope)(napi_env env, napi_error_scope* result); + napi_status (*napi_close_error_scope)(napi_env env, napi_error_scope scope); + + // loose equals + napi_status (*napi_equals)(napi_env env, napi_value lhs, napi_value rhs, + bool* result); + + napi_status (*napi_get_unhandled_rejection_exception)(napi_env env, + napi_value* result); + napi_status (*napi_get_own_property_descriptor)(napi_env env, napi_value obj, + napi_value prop, + napi_value* result); + napi_status (*napi_post_worker_task)(napi_env env, + std::function task); + napi_status (*napi_store_code_cache)(napi_env env, + const std::string& filename, + const uint8_t* data, int length); + napi_status (*napi_get_code_cache)(napi_env env, const std::string& filename, + const uint8_t** data, int* length); + napi_status (*napi_output_code_cache)(napi_env env, + unsigned int place_holder); + // this interface may be optimized by + // discarding the parameter of std::function type + napi_status (*napi_init_code_cache)(napi_env env, int capacity, + const std::string& cache_file, + std::function callback); + napi_status (*napi_dump_code_cache_status)(napi_env env, void* dump_vec); + napi_status (*napi_run_script_cache)(napi_env env, const char* script, + size_t length, const char* filename, + napi_value* result); + napi_status (*napi_run_code_cache)(napi_env env, const uint8_t* data, + int length, napi_value* result); + napi_status (*napi_gen_code_cache)(napi_env env, const char* script, + size_t script_len, const uint8_t** data, + int* length); + + napi_status (*napi_set_instance_data_spec_compliant)( + napi_env env, uint64_t key, void* data, napi_finalize finalize_cb, + void* finalize_hint); + + napi_status (*napi_define_properties_spec_compliant)( + napi_env env, napi_value object, size_t property_count, + const napi_property_descriptor* properties); + + napi_status (*napi_define_class_spec_compliant)( + napi_env env, const char* utf8name, size_t length, + napi_callback constructor, void* data, size_t property_count, + const napi_property_descriptor* properties, napi_class super_class, + napi_class* result); + + napi_status (*napi_call_function_spec_compliant)(napi_env env, + napi_value recv, + napi_value func, size_t argc, + const napi_value* argv, + napi_value* result); + + napi_status (*napi_wrap_spec_compliant)(napi_env env, napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); + napi_status (*napi_unwrap_spec_compliant)(napi_env env, napi_value js_object, + void** result); + napi_status (*napi_remove_wrap_spec_compliant)(napi_env env, + napi_value js_object, + void** result); + + napi_status (*napi_create_date)(napi_env env, double time, + napi_value* result); + + napi_status (*napi_is_date)(napi_env env, napi_value value, bool* is_date); + + napi_status (*napi_get_date_value)(napi_env env, napi_value value, + double* result); + + napi_status (*napi_get_all_property_names)(napi_env env, napi_value object, + napi_key_collection_mode key_mode, + napi_key_filter key_filter, + napi_key_conversion key_conversion, + napi_value* result); + + napi_status (*napi_create_threadsafe_function_spec_compliant)( + napi_env env, void* thread_finalize_data, + napi_finalize thread_finalize_cb, void* context, + napi_threadsafe_function_call_js call_js_cb, size_t max_queue_size, + size_t thread_count, napi_threadsafe_function* result); + + napi_status (*napi_create_bigint_int64)(napi_env env, int64_t value, + napi_value* result); + napi_status (*napi_create_bigint_uint64)(napi_env env, uint64_t value, + napi_value* result); + napi_status (*napi_get_value_bigint_int64)(napi_env env, napi_value value, + int64_t* result, bool* lossless); + napi_status (*napi_get_value_bigint_uint64)(napi_env env, napi_value value, + uint64_t* result, bool* lossless); + napi_status (*napi_create_bigint_words)(napi_env env, int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result); + napi_status (*napi_get_value_bigint_words)(napi_env env, napi_value value, + int* sign_bit, size_t* word_count, + uint64_t* words); +}; + +#endif // TEST_APP_PRIMJS_NAPI_VTABLE_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/jsr.cpp b/test-app/runtime/src/main/cpp/napi/quickjs/jsr.cpp index b9ec43e7..eaf680dd 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/jsr.cpp +++ b/test-app/runtime/src/main/cpp/napi/quickjs/jsr.cpp @@ -1,14 +1,33 @@ #include "jsr.h" #include "quicks-runtime.h" +#include "bytecode_container.h" +#include "File.h" +#include "NativeScriptAssert.h" JSR::JSR() = default; tns::SimpleMap JSR::env_to_jsr_cache; -napi_status js_create_runtime(napi_runtime *runtime) { - return qjs_create_runtime(runtime); +// Engine-agnostic runtime handle for the jsr layer. QuickJS keeps its own +// napi_runtime (the real engine runtime defined in quickjs-api.c); this wrapper +// just points at it so the jsr API can speak jsr_ns_runtime while +// quickjs-api.c / quicks-runtime.h stay unchanged. +struct jsr_ns_runtime__ { + napi_runtime rt; +}; + +napi_status js_create_runtime(jsr_ns_runtime *runtime) { + if (!runtime) return napi_invalid_arg; + auto *wrapper = new jsr_ns_runtime__(); + napi_status status = qjs_create_runtime(&wrapper->rt); + if (status != napi_ok) { + delete wrapper; + return status; + } + *runtime = wrapper; + return napi_ok; } -napi_status js_create_napi_env(napi_env *env, napi_runtime runtime) { - napi_status status = qjs_create_napi_env(env, runtime); +napi_status js_create_napi_env(napi_env *env, jsr_ns_runtime runtime) { + napi_status status = qjs_create_napi_env(env, runtime->rt); JSR::env_to_jsr_cache.Insert((*env), new JSR()); return status; } @@ -37,14 +56,54 @@ napi_status js_free_napi_env(napi_env env) { return qjs_free_napi_env(env); } -napi_status js_free_runtime(napi_runtime runtime) { - return qjs_free_runtime(runtime); +napi_status js_free_runtime(jsr_ns_runtime runtime) { + napi_status status = qjs_free_runtime(runtime->rt); + delete runtime; + return status; +} + +#ifdef __QUICKJS_NG__ +static const char *kBytecodeMagic = "NSBCNGS"; // 7 chars + NUL = 8-byte magic +static const char *kEngineName = "QuickJS-NG"; +#else +static const char *kBytecodeMagic = "NSBCQJS"; +static const char *kEngineName = "QuickJS"; +#endif + +napi_status js_run_bytecode_file(napi_env env, const char *file, napi_value *result) { + std::string path; + if (!nsbc::ResolvePath(file, path)) { + DEBUG_WRITE("[bytecode] Unable to resolve file: %s", path.c_str()); + return napi_cannot_run_js; + } + if (!nsbc::HasMagic(path, kBytecodeMagic)) { + DEBUG_WRITE("[bytecode] Unable to find %s header: %s", kEngineName, path.c_str()); + return napi_cannot_run_js; + } + + int length = 0; + auto data = tns::File::ReadBinary(path, length); + if (!data) return napi_cannot_run_js; + if (static_cast(length) <= nsbc::kHeaderLen) { + delete[] static_cast(data); + return napi_cannot_run_js; + } + + DEBUG_WRITE("[bytecode] loading %s bytecode: %s (%d bytes)", kEngineName, file, length); + + // JS_ReadObject copies what it needs, so the buffer can be freed after. + napi_status status = qjs_run_bytecode( + env, static_cast(data) + nsbc::kHeaderLen, + static_cast(length) - nsbc::kHeaderLen, file, result); + delete[] static_cast(data); + return status; } napi_status js_execute_script(napi_env env, napi_value script, const char *file, napi_value *result) { + DEBUG_WRITE("[script] loading script: %s", file); return qjs_execute_script(env, script, file, result); } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/jsr.h b/test-app/runtime/src/main/cpp/napi/quickjs/jsr.h index e6d62c9f..bc83d2da 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/jsr.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/jsr.h @@ -15,6 +15,10 @@ class JSR { public: JSR(); std::recursive_mutex js_mutex; + // Depth of nested JS scopes entered from the host (see NapiScope). We drain + // the pending-job (microtask) queue once this returns to 0, i.e. when the + // native call stack has fully unwound back out of JS. + int jsEnterState = 0; void lock() { js_mutex.lock(); } @@ -32,6 +36,10 @@ class NapiScope { { js_lock_env(env_); qjs_update_stack_top(env); + jsr_ = JSR::env_to_jsr_cache.Get(env_); + if (jsr_) { + jsr_->jsEnterState++; + } if (open_handle) { napi_open_handle_scope(env_, &napiHandleScope_); } else { @@ -40,6 +48,18 @@ class NapiScope { } ~NapiScope() { + // Drain the microtask queue only when the outermost JS scope unwinds so + // that promise continuations (async/await) run — mirroring how a JS + // engine empties its job queue once control returns to the host. + // Draining at a nested depth would run continuations while JS is still + // on the stack. A throwing job must never escape a destructor. + if (jsr_ && --jsr_->jsEnterState <= 0) { + jsr_->jsEnterState = 0; + try { + js_execute_pending_jobs(env_); + } catch (...) { + } + } if (napiHandleScope_) { napi_close_handle_scope(env_, napiHandleScope_); } @@ -49,6 +69,7 @@ class NapiScope { private: napi_env env_; napi_handle_scope napiHandleScope_; + JSR* jsr_ = nullptr; }; #define JSEnterScope diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/CMakeLists.txt b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/CMakeLists.txt index bcfe91d8..b380ce03 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/CMakeLists.txt +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/CMakeLists.txt @@ -1,37 +1,67 @@ cmake_minimum_required(VERSION 3.18) -project(libmimalloc C CXX) +project(libmimalloc C) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) -option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF) -option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF) -option(MI_PADDING "Enable padding to detect heap block overflow (always on in DEBUG or SECURE mode, or with Valgrind/ASAN)" OFF) -option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON) +option(MI_SECURE "Use security mitigations (like meta data guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF) +option(MI_SECURE_FULL "Use full security mitigations including guard pages at the end of each mimalloc page (may be expensive)" OFF) +option(MI_OVERRIDE "Override the standard malloc interface (i.e. define entry points for 'malloc', 'free', etc)" ON) option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF) option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF) +option(MI_GUARDED "Build with guard pages behind certain object allocations (enabled by default in a debug build)" OFF) +option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF) +option(MI_OPT_ARCH "Only for optimized builds: turn on architecture specific optimizations (for x64: '-march=haswell;-mavx2' (2013), for arm64: '-march=armv8.1-a' (2016))" OFF) +option(MI_OPT_SIMD "Use SIMD instructions (requires MI_OPT_ARCH to be enabled)" OFF) +option(MI_FREE_IS_CHECKED "Always check for invalid pointers in 'free' (always enabled in debug and secure builds, or when MI_OVERRIDE is ON)" OFF) + +option(MI_DEBUG "Enable assertion checks (enabled by default in a debug build)" OFF) +option(MI_DEBUG_INTERNAL "Enable assertion and internal invariant checks (enabled by default in a debug build)" OFF) +option(MI_DEBUG_FULL "Enable assertion checks and expensive internal heap invariant checking" OFF) + +option(MI_DEBUG_TSAN "Build with thread sanitizer (needs clang)" OFF) +option(MI_DEBUG_UBSAN "Build with undefined-behavior sanitizer (needs clang++)" OFF) option(MI_TRACK_VALGRIND "Compile with Valgrind support (adds a small overhead)" OFF) option(MI_TRACK_ASAN "Compile with address sanitizer support (adds a small overhead)" OFF) option(MI_TRACK_ETW "Compile with Windows event tracing (ETW) support (adds a small overhead)" OFF) -option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF) -option(MI_SEE_ASM "Generate assembly files" OFF) -option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) -option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON) -option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON) -option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF) -option(MI_LIBC_MUSL "Set this when linking with musl libc" OFF) + option(MI_BUILD_SHARED "Build shared library" ON) option(MI_BUILD_STATIC "Build static library" ON) option(MI_BUILD_OBJECT "Build object library" ON) option(MI_BUILD_TESTS "Build test executables" ON) -option(MI_DEBUG_TSAN "Build with thread sanitizer (needs clang)" OFF) -option(MI_DEBUG_UBSAN "Build with undefined-behavior sanitizer (needs clang++)" OFF) + +# OS specific +option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) +option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON) +option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON) +option(MI_WIN_DIRECT_TLS "Use only direct TLS slots on Windows to avoid extra tests in the malloc fast path (only works if the program uses less than 64 TlsAlloc'd slots in total)" OFF) +option(MI_LOCAL_DYNAMIC_TLS "Use local-dynamic-tls, a slightly slower but dlopen-compatible thread local storage mechanism (Unix)" OFF) +option(MI_LIBC_MUSL "Enable this when linking with musl libc" OFF) + +# advanced +option(MI_PADDING "Enable padding to detect heap block overflow (always on in DEBUG or SECURE mode, or with Valgrind/ASAN)" OFF) option(MI_SKIP_COLLECT_ON_EXIT "Skip collecting memory on program exit" OFF) option(MI_NO_PADDING "Force no use of padding even in DEBUG mode etc." OFF) option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead of PREFIX/lib/mimalloc-version" OFF) option(MI_NO_THP "Disable transparent huge pages support on Linux/Android for the mimalloc process only" OFF) +option(MI_EXTRA_CPPDEFS "Extra pre-processor definitions (use as `-DMI_EXTRA_CPPDEFS=\"opt1=val1;opt2=val2\"`)" "") +option(MI_SEE_ASM "Generate assembly files" OFF) + +# negated options for vcpkg features +option(MI_NO_USE_CXX "Use plain C compilation (has priority over MI_USE_CXX)" OFF) +option(MI_NO_OPT_ARCH "Do not use architecture specific optimizations (like '-march=armv8.1-a' for example) (has priority over MI_OPT_ARCH)" OFF) +option(MI_NO_DEBUG "Disable all assertion checks even in debug builds" OFF) + +# experimental +option(MI_WIN_INIT_USE_RAW_DLLMAIN "Use the raw DLL main entry point for mimalloc initialization; can be more robust but can also lead to link errors with other libraries" OFF) +option(MI_WIN_INIT_USE_TLS_DLLMAIN "Use legacy TLS entries with DllMain for mimalloc initialization" OFF) + +option(MI_TLS_MODEL_LOCAL "Force TLS model based on thread local variables" OFF) +option(MI_TLS_MODEL_PTHREADS "Force TLS model based on pthreads" OFF) +option(MI_TLS_RECURSE_GUARD "Force a recursion check on thread local access (MI_TLS_MODEL_LOCAL only)" OFF) # deprecated options +option(MI_WIN_USE_FLS "Use Fiber local storage on Windows to detect thread termination (deprecated)" OFF) option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF) option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF) @@ -45,6 +75,7 @@ set(mi_sources src/alloc-aligned.c src/alloc-posix.c src/arena.c + src/arena-meta.c src/bitmap.c src/heap.c src/init.c @@ -52,46 +83,133 @@ set(mi_sources src/options.c src/os.c src/page.c + src/page-map.c src/random.c - src/segment.c - src/segment-map.c src/stats.c + src/theap.c + src/threadlocal.c src/prim/prim.c) set(mi_cflags "") set(mi_cflags_static "") # extra flags for a static library build set(mi_cflags_dynamic "") # extra flags for a shared-object library build -set(mi_defines "") set(mi_libraries "") +if(MI_EXTRA_CPPDEFS) + set(mi_defines ${MI_EXTRA_CPPDEFS}) +else() + set(mi_defines "") +endif() + +# pass git revision as a define +if(EXISTS "${CMAKE_SOURCE_DIR}/.git/index") + find_package(Git) + if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} "describe" OUTPUT_VARIABLE mi_git_describe RESULT_VARIABLE mi_git_res ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(mi_git_res EQUAL "0") + list(APPEND mi_defines "MI_GIT_DESCRIBE=${mi_git_describe}") + # add to dependencies so we rebuild if the git head commit changes + set_property(GLOBAL APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/.git/index") + endif() + endif() +endif() + # ----------------------------------------------------------------------------- -# Convenience: set default build type depending on the build directory +# Convenience: set default build type and compiler depending on the build directory # ----------------------------------------------------------------------------- message(STATUS "") -if (NOT CMAKE_BUILD_TYPE) - if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$" OR MI_DEBUG_FULL) - message(STATUS "No build type selected, default to: Debug") +if(NOT CMAKE_BUILD_TYPE) + if ("${CMAKE_BINARY_DIR}" MATCHES ".*((D|d)ebug|asan|tsan|ubsan|valgrind)$") + message(STATUS "No build type selected, default to 'Debug'") set(CMAKE_BUILD_TYPE "Debug") else() - message(STATUS "No build type selected, default to: Release") + message(STATUS "No build type selected, default to 'Release'") set(CMAKE_BUILD_TYPE "Release") endif() endif() -if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$") +if(CMAKE_GENERATOR MATCHES "^Visual Studio.*$") + message(STATUS "Note: when building with Visual Studio the build type is specified when building.") + message(STATUS "For example: 'cmake --build . --config=Release") +endif() + +if(NOT MI_SECURE AND NOT MI_SECURE_FULL AND "${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$") message(STATUS "Default to secure build") set(MI_SECURE "ON") endif() +# Determine architecture +set(MI_OPT_ARCH_FLAGS "") +set(MI_ARCH "unknown") +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|i[3456]86)$" OR CMAKE_GENERATOR_PLATFORM MATCHES "^(x86|Win32)$") + set(MI_ARCH "x86") +elseif((CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|x64|amd64|AMD64)$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "x64" OR "x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES) AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") # must be before arm64 + set(MI_ARCH "x64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|armv[89].?|ARM64)$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64" OR "arm64" IN_LIST CMAKE_OSX_ARCHITECTURES) + set(MI_ARCH "arm64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|armv[34567].?|ARM)$") + set(MI_ARCH "arm32") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv|riscv32|riscv64)$") + if(CMAKE_SIZEOF_VOID_P==4) + set(MI_ARCH "riscv32") + else() + set(MI_ARCH "riscv64") + endif() +else() + set(MI_ARCH ${CMAKE_SYSTEM_PROCESSOR}) +endif() +message(STATUS "Architecture: ${MI_ARCH}") # (${CMAKE_SYSTEM_PROCESSOR}, ${CMAKE_GENERATOR_PLATFORM}, ${CMAKE_GENERATOR})") + +# negative overrides (mainly to support vcpkg features) +if(MI_NO_USE_CXX) + set(MI_USE_CXX "OFF") +endif() + +if(MI_NO_OPT_ARCH) + set(MI_OPT_ARCH "OFF") +elseif(MI_ARCH STREQUAL "arm64") + set(MI_OPT_ARCH "ON") # enable armv8.1-a by default on arm64 unless MI_NO_OPT_ARCH is set +endif() + +# ----------------------------------------------------------------------------- +# Enable the C++ compiler early on if needed +# ----------------------------------------------------------------------------- + +# clang-cl detection on windows +if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + set(MI_CLANG_CL "ON") +endif() + +# force C++ compilation with msvc or clang-cl to use modern C++ atomics +if(CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel" OR MI_CLANG_CL) + set(MI_USE_CXX "ON") +elseif(MI_DEBUG_UBSAN AND CMAKE_BUILD_TYPE MATCHES "Debug") # ubsan needs C++ + set(MI_USE_CXX "ON") +endif() + +# enable C++ ? +if(MI_USE_CXX) + enable_language(CXX) + message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") + if(CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") + list(APPEND mi_cflags -Kc++) + endif() +endif() + + # ----------------------------------------------------------------------------- # Process options # ----------------------------------------------------------------------------- # put -Wall early so other warnings can be disabled selectively if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") - list(APPEND mi_cflags -Wall -Wextra -Wpedantic) + if (MI_CLANG_CL) + list(APPEND mi_cflags -W) + else() + list(APPEND mi_cflags -Wall -Wextra -Wpedantic) + endif() endif() if(CMAKE_C_COMPILER_ID MATCHES "GNU") list(APPEND mi_cflags -Wall -Wextra) @@ -100,8 +218,8 @@ if(CMAKE_C_COMPILER_ID MATCHES "Intel") list(APPEND mi_cflags -Wall) endif() -if(CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel") - set(MI_USE_CXX "ON") +if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang") + list(APPEND mi_cflags -Wno-deprecated) endif() if(MI_OVERRIDE) @@ -121,7 +239,7 @@ if(MI_OVERRIDE) message(STATUS " Use interpose to override malloc (MI_OSX_INTERPOSE=ON)") list(APPEND mi_defines MI_OSX_INTERPOSE=1) if (NOT MI_OSX_ZONE) - message(STATUS " WARNING: interpose usually also needs zone overriding (use -DMI_OSX_INTERPOSE=ON)") + message(STATUS " WARNING: interpose usually also needs zone overriding (use -DMI_OSX_ZONE=ON)") endif() endif() if(MI_USE_CXX AND MI_OSX_INTERPOSE) @@ -131,20 +249,18 @@ if(MI_OVERRIDE) endif() if(WIN32) - if (MI_WIN_REDIRECT) - if (MSVC_C_ARCHITECTURE_ID MATCHES "ARM") - message(STATUS "Cannot use redirection on Windows ARM (MI_WIN_REDIRECT=OFF)") - set(MI_WIN_REDIRECT OFF) - endif() - endif() if (NOT MI_WIN_REDIRECT) # use a negative define for backward compatibility list(APPEND mi_defines MI_WIN_NOREDIRECT=1) endif() endif() -if(MI_SECURE) - message(STATUS "Set full secure build (MI_SECURE=ON)") +if(MI_SECURE_FULL) + set(MI_SECURE ON) + message(STATUS "Set full secure build (MI_SECURE_FULL=ON)") + list(APPEND mi_defines MI_SECURE=5) +elseif(MI_SECURE) + message(STATUS "Set secure build (MI_SECURE=ON)") list(APPEND mi_defines MI_SECURE=4) endif() @@ -152,8 +268,8 @@ if(MI_TRACK_VALGRIND) CHECK_INCLUDE_FILES("valgrind/valgrind.h;valgrind/memcheck.h" MI_HAS_VALGRINDH) if (NOT MI_HAS_VALGRINDH) set(MI_TRACK_VALGRIND OFF) - message(WARNING "Cannot find the 'valgrind/valgrind.h' and 'valgrind/memcheck.h' -- install valgrind first") - message(STATUS "Compile **without** Valgrind support (MI_TRACK_VALGRIND=OFF)") + message(WARNING "Cannot find the 'valgrind/valgrind.h' and 'valgrind/memcheck.h' -- install valgrind first?") + message(STATUS "Disabling Valgrind support (MI_TRACK_VALGRIND=OFF)") else() message(STATUS "Compile with Valgrind support (MI_TRACK_VALGRIND=ON)") list(APPEND mi_defines MI_TRACK_VALGRIND=1) @@ -201,10 +317,14 @@ endif() if(MI_SEE_ASM) message(STATUS "Generate assembly listings (MI_SEE_ASM=ON)") - list(APPEND mi_cflags -save-temps) - if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") - message(STATUS "No GNU Line marker") - list(APPEND mi_cflags -Wno-gnu-line-marker) + if(CMAKE_C_COMPILER_ID MATCHES "MSVC" OR MI_CLANG_CL) + list(APPEND mi_cflags -FA) + else() + list(APPEND mi_cflags -save-temps) + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 14) + message(STATUS "No GNU Line marker") + list(APPEND mi_cflags -Wno-gnu-line-marker) + endif() endif() endif() @@ -218,9 +338,35 @@ if (MI_SKIP_COLLECT_ON_EXIT) list(APPEND mi_defines MI_SKIP_COLLECT_ON_EXIT=1) endif() -if(MI_DEBUG_FULL) - message(STATUS "Set debug level to full internal invariant checking (MI_DEBUG_FULL=ON)") - list(APPEND mi_defines MI_DEBUG=3) # full invariant checking +if(MI_NO_DEBUG) + message(STATUS "Set debug level zero, all assertions are disabled (MI_NO_DEBUG=ON)") + set(MI_DEBUG OFF) + list(APPEND mi_defines MI_DEBUG=0) +elseif(MI_DEBUG_FULL) + message(STATUS "Set debug level to full assertion and internal invariant checking (MI_DEBUG_FULL=ON, expensive)") + set(MI_DEBUG ON) + list(APPEND mi_defines MI_DEBUG=3) # full invariant checking (mi_assert, mi_assert_internal, and mi_assert_expensive) +elseif(MI_DEBUG_INTERNAL) + message(STATUS "Set debug level to internal assertion and invariant checking (MI_DEBUG_INTERNAL=ON)") + set(MI_DEBUG ON) + list(APPEND mi_defines MI_DEBUG=2) # invariant checking (mi_assert and mi_assert_internal) +elseif(MI_DEBUG) + message(STATUS "Set debug level to assertion checking (MI_DEBUG=ON)") + list(APPEND mi_defines MI_DEBUG=1) # assertion checking (mi_assert) +elseif(CMAKE_BUILD_TYPE MATCHES "Debug") + message(STATUS "Set debug level to internal assertion and invariant checking (CMAKE_BUILD_TYPE=Debug)") + set(MI_DEBUG_INTERNAL ON) + set(MI_DEBUG ON) + list(APPEND mi_defines MI_DEBUG=2) # invariant checking (mi_assert and mi_assert_internal) +endif() + +if(MI_DEBUG AND !MI_GUARDED) + message(STATUS "Enable MI_GUARDED (since MI_DEBUG=ON)") + set(MI_GUARDED=ON) +endif() +if(MI_GUARDED) + message(STATUS "Compile guard pages behind certain object allocations (MI_GUARDED=ON)") + list(APPEND mi_defines MI_GUARDED=1) endif() if(MI_NO_PADDING) @@ -233,6 +379,17 @@ else() endif() endif() +if(MI_DEBUG OR MI_SECURE) + set(MI_FREE_IS_CHECKED ON) +endif() +if(MI_FREE_IS_CHECKED) + message(STATUS "Check validity of pointers passed to 'mi_free' (MI_FREE_IS_CHECKED=ON)") + list(APPEND mi_defines MI_FREE_IS_CHECKED=1) +elseif(MI_OVERRIDE) + message(STATUS "Check validity of pointers passed to 'mi_free' only in the dynamic link library (MI_OVERRIDE=ON, MI_FREE_IS_CHECKED=OFF)") + list(APPEND mi_cflags_dynamic -DMI_FREE_IS_CHECKED=1) +endif() + if(MI_XMALLOC) message(STATUS "Enable abort() calls on memory allocation failure (MI_XMALLOC=ON)") list(APPEND mi_defines MI_XMALLOC=1) @@ -256,38 +413,27 @@ endif() if(MI_DEBUG_UBSAN) if(CMAKE_BUILD_TYPE MATCHES "Debug") - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - message(STATUS "Build with undefined-behavior sanitizer (MI_DEBUG_UBSAN=ON)") - list(APPEND mi_cflags -fsanitize=undefined -g -fno-sanitize-recover=undefined) - list(APPEND mi_libraries -fsanitize=undefined) - if (NOT MI_USE_CXX) - message(STATUS "(switch to use C++ due to MI_DEBUG_UBSAN)") - set(MI_USE_CXX "ON") + if(MI_USE_CXX) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(STATUS "Build with undefined-behavior sanitizer (MI_DEBUG_UBSAN=ON)") + list(APPEND mi_defines MI_UBSAN=1) + list(APPEND mi_cflags -fsanitize=undefined -g -fno-sanitize-recover=undefined) + list(APPEND mi_libraries -fsanitize=undefined) + else() + message(WARNING "Can only use undefined-behavior sanitizer with clang++ (MI_DEBUG_UBSAN=ON but ignored)") endif() else() - message(WARNING "Can only use undefined-behavior sanitizer with clang++ (MI_DEBUG_UBSAN=ON but ignored)") + message(WARNING "Can only use undefined-behavior sanitizer with a C++ build (MI_USE_CXX=ON)") endif() else() message(WARNING "Can only use undefined-behavior sanitizer with a debug build (CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})") endif() endif() -if(MI_USE_CXX) - message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") - set_source_files_properties(${mi_sources} PROPERTIES LANGUAGE CXX ) - set_source_files_properties(src/static.c test/test-api.c test/test-api-fill test/test-stress PROPERTIES LANGUAGE CXX ) - if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang") - list(APPEND mi_cflags -Wno-deprecated) - endif() - if(CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") - list(APPEND mi_cflags -Kc++) - endif() -endif() - if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") if(MI_NO_THP) message(STATUS "Disable transparent huge pages support (MI_NO_THP=ON)") - list(APPEND mi_defines MI_NO_THP=1) + list(APPEND mi_defines MI_DEFAULT_ALLOW_THP=0) endif() endif() @@ -296,6 +442,50 @@ if(MI_LIBC_MUSL) list(APPEND mi_defines MI_LIBC_MUSL=1) endif() +if(MI_TLS_MODEL_LOCAL) + message(STATUS "Use TLS model based on thread local (MI_TLS_MODEL_LOCAL=ON)") + list(APPEND mi_defines MI_TLS_MODEL_THREAD_LOCAL=1) +elseif(MI_TLS_MODEL_PTHREADS) + message(STATUS "Use TLS model based on pthreads (MI_TLS_MODEL_PTHREADS=ON)") + list(APPEND mi_defines MI_TLS_MODEL_DYNAMIC_PTHREADS=1) +endif() + +if(MI_TLS_RECURSE_GUARD) + message(STATUS "Use TLS recursion guard (for MI_TLS_MODEL_LOCAL only)") + list(APPEND mi_defines MI_TLS_RECURSE_GUARD=1) +endif() + +if(MI_WIN_DIRECT_TLS) + message(STATUS "Use only direct TLS slots on Windows to avoid extra tests in the malloc fast path -- this only works if the program uses less than 64 TlsAlloc'd slots in total! (MI_WIN_USE_ONLY_DIRECT_TLS=ON)") + list(APPEND mi_defines MI_WIN_DIRECT_TLS=1) +endif() + +if(MI_WIN_INIT_USE_RAW_DLLMAIN) + message(STATUS "Use raw Dll main entry on Windows to initialize and finalize mimalloc (MI_WIN_INIT_USE_RAW_DLLMAIN=ON)") + message(STATUS "Note: this can lead to link errors if other libraries also try to define a raw Dll main entry point") + list(APPEND mi_defines MI_WIN_INIT_USE_RAW_DLLMAIN=1) +elseif(MI_WIN_INIT_USE_TLS_DLLMAIN) + message(STATUS "Use Dll main entry with TLS entries on Windows to initialize and finalize mimalloc (MI_WIN_INIT_USE_TLS_DLLMAIN=ON)") + list(APPEND mi_defines MI_WIN_INIT_USE_TLS_DLLMAIN=1) +elseif(MI_WIN_USE_FLS) + message(STATUS "Deprecated: Use the Fiber API to detect thread termination (deprecated) (MI_WIN_USE_FLS=ON)") + list(APPEND mi_defines MI_WIN_INIT_USE_FLS=1) +else() + # default uses CRT with TLS entries for initialization and finalization + # list(APPEND mi_defines MI_WIN_INIT_USE_CRT_TLS=1) +endif() + +if(MI_ARCH MATCHES "riscv") + CHECK_INCLUDE_FILES("asm/hwprobe.h" MI_HAS_ASM_HWPROBEH) + if (MI_HAS_ASM_HWPROBEH) + list(APPEND mi_defines MI_HAS_ASM_HWPROBEH=1) + endif() + CHECK_INCLUDE_FILES("sys/hwprobe.h" MI_HAS_SYS_HWPROBEH) + if (MI_HAS_SYS_HWPROBEH) + list(APPEND mi_defines MI_HAS_SYS_HWPROBEH=1) + endif() +endif() + # On Haiku use `-DCMAKE_INSTALL_PREFIX` instead, issue #788 # if(CMAKE_SYSTEM_NAME MATCHES "Haiku") # SET(CMAKE_INSTALL_LIBDIR ~/config/non-packaged/lib) @@ -303,7 +493,7 @@ endif() # endif() # Compiler flags -if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU") +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU" AND NOT MI_CLANG_CL) list(APPEND mi_cflags -Wno-unknown-pragmas -fvisibility=hidden) if(NOT MI_USE_CXX) list(APPEND mi_cflags -Wstrict-prototypes) @@ -317,7 +507,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "Intel") list(APPEND mi_cflags -fvisibility=hidden) endif() -if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku") +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku" AND NOT MI_CLANG_CL) if(MI_LOCAL_DYNAMIC_TLS) list(APPEND mi_cflags -ftls-model=local-dynamic) else() @@ -327,36 +517,75 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM list(APPEND mi_cflags_dynamic -ftls-model=initial-exec) message(STATUS "Use local dynamic TLS for the static build (since MI_LIBC_MUSL=ON)") else() - list(APPEND mi_cflags -ftls-model=initial-exec) + list(APPEND mi_cflags -ftls-model=initial-exec) endif() endif() +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel") if(MI_OVERRIDE) list(APPEND mi_cflags -fno-builtin-malloc) endif() endif() -if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914) +# Compiler and architecture specific flags +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku") + if(MI_OPT_ARCH) + if(APPLE AND CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang" AND CMAKE_OSX_ARCHITECTURES) # to support multi-arch binaries (#999) + if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES) + list(APPEND MI_OPT_ARCH_FLAGS "-Xarch_arm64;-march=armv8.1-a") + endif() + if("x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES) + list(APPEND MI_OPT_ARCH_FLAGS "-Xarch_x86_64;-march=haswell;-Xarch_x86_64;-mavx2") + endif() + elseif(MI_ARCH STREQUAL "x64") + set(MI_OPT_ARCH_FLAGS "-march=haswell;-mavx2") # fast bit scan (since 2013) + elseif(MI_ARCH STREQUAL "arm64") + set(MI_OPT_ARCH_FLAGS "-march=armv8.1-a") # fast atomics (since 2016) + endif() + endif() +endif() + +if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914) # vs2017+ list(APPEND mi_cflags /Zc:__cplusplus) + if(MI_OPT_ARCH AND NOT MI_CLANG_CL) + if(MI_ARCH STREQUAL "x64") + set(MI_OPT_ARCH_FLAGS "/arch:AVX2") + elseif(MI_ARCH STREQUAL "arm64") + set(MI_OPT_ARCH_FLAGS "/arch:armv8.1") # fast atomics (LSE) + endif() + endif() endif() if(MINGW) - add_definitions(-D_WIN32_WINNT=0x600) + add_definitions(-D_WIN32_WINNT=0x600) # issue #976 +endif() + +if(MI_OPT_ARCH_FLAGS) + list(APPEND mi_cflags ${MI_OPT_ARCH_FLAGS}) + message(STATUS "Architecture specific optimization is enabled (with ${MI_OPT_ARCH_FLAGS}) (MI_OPT_ARCH=ON)") + if (MI_OPT_SIMD) + list(APPEND mi_defines "MI_OPT_SIMD=1") + message(STATUS "SIMD instructions are enabled (MI_OPT_SIMD=ON)") + endif() +elseif(MI_OPT_SIMD) + message(STATUS "SIMD instructions are not enabled (either MI_OPT_ARCH=OFF or this architecture has no SIMD support)") endif() # extra needed libraries -# we prefer -l test over `find_library` as sometimes core libraries +# we prefer -l test over `find_library` as sometimes core libraries # like `libatomic` are not on the system path (see issue #898) -function(find_link_library libname outlibname) - check_linker_flag(C "-l${libname}" mi_has_lib${libname}) +function(find_link_library libname outlibname) + check_linker_flag(C "-l${libname}" mi_has_lib${libname}) if (mi_has_lib${libname}) message(VERBOSE "link library: -l${libname}") - set(${outlibname} ${libname} PARENT_SCOPE) + set(${outlibname} ${libname} PARENT_SCOPE) else() - find_library(MI_LIBPATH libname) - if (MI_LIBPATH) - message(VERBOSE "link library ${libname} at ${MI_LIBPATH}") - set(${outlibname} ${MI_LIBPATH} PARENT_SCOPE) + find_library(MI_LIBPATH_${libname} ${libname}) + if (MI_LIBPATH_${libname}) + message(VERBOSE "link library ${libname} at ${MI_LIBPATH_${libname}}") + set(${outlibname} ${MI_LIBPATH_${libname}} PARENT_SCOPE) else() message(VERBOSE "link library not found: ${libname}") set(${outlibname} "" PARENT_SCOPE) @@ -365,29 +594,35 @@ function(find_link_library libname outlibname) endfunction() if(WIN32) - list(APPEND mi_libraries psapi shell32 user32 advapi32 bcrypt) + list(APPEND mi_libraries psapi shell32 user32 advapi32 bcrypt) else() find_link_library("pthread" MI_LIB_PTHREAD) - if(MI_LIB_PTHREAD) + if(MI_LIB_PTHREAD) list(APPEND mi_libraries "${MI_LIB_PTHREAD}") endif() find_link_library("rt" MI_LIB_RT) - if(MI_LIB_RT) + if(MI_LIB_RT) list(APPEND mi_libraries "${MI_LIB_RT}") endif() find_link_library("atomic" MI_LIB_ATOMIC) - if(MI_LIB_ATOMIC) - list(APPEND mi_libraries "${MI_LIB_ATOMIC}") + if(MI_LIB_ATOMIC) + list(APPEND mi_libraries "${MI_LIB_ATOMIC}") endif() endif() +# set language for source files now +if(MI_USE_CXX) + set_source_files_properties(${mi_sources} PROPERTIES LANGUAGE CXX ) + set_source_files_properties(src/static.c test/test-api.c test/test-api-fill.c test/test-stress.c PROPERTIES LANGUAGE CXX ) +endif() + + # ----------------------------------------------------------------------------- # Install and output names # ----------------------------------------------------------------------------- # dynamic/shared library and symlinks always go to /usr/local/lib equivalent -set(mi_install_libdir "${CMAKE_INSTALL_LIBDIR}") -set(mi_install_bindir "${CMAKE_INSTALL_BINDIR}") +# we use ${CMAKE_INSTALL_BINDIR} and ${CMAKE_INSTALL_LIBDIR}. # static libraries and object files, includes, and cmake config files # are either installed at top level, or use versioned directories for side-by-side installation (default) @@ -401,19 +636,22 @@ else() set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info endif() -set(mi_basename "mimalloc") +set(mi_libname "mimalloc") if(MI_SECURE) - set(mi_basename "${mi_basename}-secure") + set(mi_libname "${mi_libname}-secure") endif() if(MI_TRACK_VALGRIND) - set(mi_basename "${mi_basename}-valgrind") + set(mi_libname "${mi_libname}-valgrind") endif() if(MI_TRACK_ASAN) - set(mi_basename "${mi_basename}-asan") + set(mi_libname "${mi_libname}-asan") endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) -if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$")) - set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version +list(APPEND mi_defines "MI_CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LC}") #todo: multi-config project needs $ ? +if(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$") + list(APPEND mi_defines MI_BUILD_RELEASE) +else() + set(mi_libname "${mi_libname}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version endif() if(MI_BUILD_SHARED) @@ -430,8 +668,8 @@ if(MI_BUILD_TESTS) endif() message(STATUS "") -message(STATUS "Library base name: ${mi_basename}") -message(STATUS "Version : ${mi_version}") +message(STATUS "Library name : ${mi_libname}") +message(STATUS "Version : ${mi_version}.${mi_version_patch}") message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}") if(MI_USE_CXX) message(STATUS "C++ Compiler : ${CMAKE_CXX_COMPILER}") @@ -451,7 +689,7 @@ message(STATUS "") # shared library if(MI_BUILD_SHARED) add_library(mimalloc SHARED ${mi_sources}) - set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} SOVERSION ${mi_version_major} OUTPUT_NAME ${mi_basename} ) + set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} SOVERSION ${mi_version_major} OUTPUT_NAME ${mi_libname} ) target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT) target_compile_options(mimalloc PRIVATE ${mi_cflags} ${mi_cflags_dynamic}) target_link_libraries(mimalloc PRIVATE ${mi_libraries}) @@ -459,28 +697,50 @@ if(MI_BUILD_SHARED) $ $ ) + install(TARGETS mimalloc EXPORT mimalloc ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) + + if(WIN32 AND NOT MINGW) + # On windows, the import library name for the dll would clash with the static mimalloc.lib library + # so we postfix the dll import library with `.dll.lib` (and also the .pdb debug file) + set_property(TARGET mimalloc PROPERTY ARCHIVE_OUTPUT_NAME "${mi_libname}.dll" ) + install(FILES "$/${mi_libname}.dll.lib" DESTINATION ${CMAKE_INSTALL_LIBDIR}/mimalloc-${mi_version}) + set_property(TARGET mimalloc PROPERTY PDB_NAME "${mi_libname}.dll") + # don't try to install the pdb since it may not be generated depending on the configuration + # install(FILES "$/${mi_libname}.dll.pdb" DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() if(WIN32 AND MI_WIN_REDIRECT) + if(MINGW) + set_property(TARGET mimalloc PROPERTY PREFIX "") + endif() # On windows, link and copy the mimalloc redirection dll too. - if(CMAKE_SIZEOF_VOID_P EQUAL 4) + if(CMAKE_GENERATOR_PLATFORM STREQUAL "arm64ec") + set(MIMALLOC_REDIRECT_SUFFIX "-arm64ec") + elseif(MI_ARCH STREQUAL "x64") + set(MIMALLOC_REDIRECT_SUFFIX "") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64") + message(STATUS "Note: x64 code emulated on Windows for arm64 should use an arm64ec build of 'mimalloc.dll'") + message(STATUS " together with 'mimalloc-redirect-arm64ec.dll'. See the 'bin\\readme.md' for more information.") + endif() + elseif(MI_ARCH STREQUAL "x86") set(MIMALLOC_REDIRECT_SUFFIX "32") else() - set(MIMALLOC_REDIRECT_SUFFIX "") + set(MIMALLOC_REDIRECT_SUFFIX "-${MI_ARCH}") # -arm64 etc. endif() - target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib) + target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib) # the DLL import library add_custom_command(TARGET mimalloc POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" $ COMMENT "Copy mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll to output directory") - install(FILES "$/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" DESTINATION ${mi_install_bindir}) + install(FILES "$/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() - - install(TARGETS mimalloc EXPORT mimalloc ARCHIVE DESTINATION ${mi_install_libdir} RUNTIME DESTINATION ${mi_install_bindir} LIBRARY DESTINATION ${mi_install_libdir}) - install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) endif() + # static library if (MI_BUILD_STATIC) add_library(mimalloc-static STATIC ${mi_sources}) + set_property(TARGET mimalloc-static PROPERTY OUTPUT_NAME ${mi_libname}) set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON) target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB) target_compile_options(mimalloc-static PRIVATE ${mi_cflags} ${mi_cflags_static}) @@ -489,15 +749,6 @@ if (MI_BUILD_STATIC) $ $ ) - if(WIN32) - # When building both static and shared libraries on Windows, a static library should use a - # different output name to avoid the conflict with the import library of a shared one. - string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename}) - set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name}) - else() - set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename}) - endif() - install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_objdir} LIBRARY) install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) endif() @@ -506,6 +757,7 @@ endif() install(FILES include/mimalloc.h DESTINATION ${mi_install_incdir}) install(FILES include/mimalloc-override.h DESTINATION ${mi_install_incdir}) install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_incdir}) +install(FILES include/mimalloc-stats.h DESTINATION ${mi_install_incdir}) install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_cmakedir}) install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_cmakedir}) @@ -522,12 +774,15 @@ if (MI_BUILD_OBJECT) ) # Copy the generated object file (`static.o`) to the output directory (as `mimalloc.o`) - if(NOT WIN32) + if(CMAKE_GENERATOR MATCHES "^Visual Studio.*$") + set(mimalloc-obj-static "${CMAKE_CURRENT_BINARY_DIR}/mimalloc-obj.dir/$/static${CMAKE_C_OUTPUT_EXTENSION}") + else() set(mimalloc-obj-static "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}") - set(mimalloc-obj-out "${CMAKE_CURRENT_BINARY_DIR}/${mi_basename}${CMAKE_C_OUTPUT_EXTENSION}") - add_custom_command(OUTPUT ${mimalloc-obj-out} DEPENDS mimalloc-obj COMMAND "${CMAKE_COMMAND}" -E copy "${mimalloc-obj-static}" "${mimalloc-obj-out}") - add_custom_target(mimalloc-obj-target ALL DEPENDS ${mimalloc-obj-out}) endif() + set(mimalloc-obj-out "${CMAKE_CURRENT_BINARY_DIR}/${mi_libname}${CMAKE_C_OUTPUT_EXTENSION}") + add_custom_command(OUTPUT ${mimalloc-obj-out} DEPENDS mimalloc-obj mimalloc-static COMMAND "${CMAKE_COMMAND}" -E copy "${mimalloc-obj-static}" "${mimalloc-obj-out}") + add_custom_target(mimalloc-obj-target ALL DEPENDS ${mimalloc-obj-out}) + # the following seems to lead to cmake warnings/errors on some systems, disable for now :-( # install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_objdir}) @@ -536,22 +791,23 @@ if (MI_BUILD_OBJECT) # but that fails cmake versions less than 3.10 so we leave it as is for now install(FILES ${mimalloc-obj-static} DESTINATION ${mi_install_objdir} - RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} ) + RENAME ${mi_libname}${CMAKE_C_OUTPUT_EXTENSION} ) endif() + # pkg-config file support -set(pc_libraries "") +set(mi_pc_libraries "") foreach(item IN LISTS mi_libraries) if(item MATCHES " *[-].*") - set(pc_libraries "${pc_libraries} ${item}") + set(mi_pc_libraries "${mi_pc_libraries} ${item}") else() - set(pc_libraries "${pc_libraries} -l${item}") + set(mi_pc_libraries "${mi_pc_libraries} -l${item}") endif() endforeach() include("cmake/JoinPaths.cmake") -join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") -join_paths(libdir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_LIBDIR}") +join_paths(mi_pc_includedir "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") +join_paths(mi_pc_libdir "\${prefix}" "${CMAKE_INSTALL_LIBDIR}") configure_file(mimalloc.pc.in mimalloc.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc" @@ -566,15 +822,42 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc" if (MI_BUILD_TESTS) enable_testing() + # static link tests foreach(TEST_NAME api api-fill stress) add_executable(mimalloc-test-${TEST_NAME} test/test-${TEST_NAME}.c) target_compile_definitions(mimalloc-test-${TEST_NAME} PRIVATE ${mi_defines}) target_compile_options(mimalloc-test-${TEST_NAME} PRIVATE ${mi_cflags}) target_include_directories(mimalloc-test-${TEST_NAME} PRIVATE include) - target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc ${mi_libraries}) - + if(MI_BUILD_STATIC AND NOT MI_DEBUG_TSAN) + target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc-static ${mi_libraries}) + elseif(MI_BUILD_SHARED) + target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc ${mi_libraries}) + else() + message(STATUS "cannot build TSAN tests without MI_BUILD_SHARED being enabled") + endif() add_test(NAME test-${TEST_NAME} COMMAND mimalloc-test-${TEST_NAME}) endforeach() + + # dynamic override test + if(MI_BUILD_SHARED AND NOT (MI_TRACK_ASAN OR MI_DEBUG_TSAN OR MI_DEBUG_UBSAN)) # AND NOT (APPLE AND MI_USE_CXX)) + add_executable(mimalloc-test-stress-dynamic test/test-stress.c) + target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE ${mi_defines} "USE_STD_MALLOC=1") + target_compile_options(mimalloc-test-stress-dynamic PRIVATE ${mi_cflags}) + target_include_directories(mimalloc-test-stress-dynamic PRIVATE include) + if(WIN32) + target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE "MI_LINK_VERSION=1") # link mi_version + target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # link mi_version + add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_VERBOSE=1 $) + else() + target_link_libraries(mimalloc-test-stress-dynamic PRIVATE ${mi_libraries}) # pthreads, issue 1158 + if(APPLE) + set(LD_PRELOAD "DYLD_INSERT_LIBRARIES") + else() + set(LD_PRELOAD "LD_PRELOAD") + endif() + add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_VERBOSE=1 ${LD_PRELOAD}=$ $) + endif() + endif() endif() # ----------------------------------------------------------------------------- @@ -584,13 +867,10 @@ if (MI_OVERRIDE) if (MI_BUILD_SHARED) target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE) endif() - if(NOT WIN32) - # It is only possible to override malloc on Windows when building as a DLL. - if (MI_BUILD_STATIC) - target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) - endif() - if (MI_BUILD_OBJECT) - target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) - endif() + if (MI_BUILD_STATIC) + target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) + endif() + if (MI_BUILD_OBJECT) + target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) endif() endif() diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/LICENSE b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/LICENSE index 670b668a..53315ebe 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/LICENSE +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2021 Microsoft Corporation, Daan Leijen +Copyright (c) 2018-2025 Microsoft Corporation, Daan Leijen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/azure-pipelines.yml b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/azure-pipelines.yml index 0247c76f..5094950e 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/azure-pipelines.yml +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/azure-pipelines.yml @@ -4,18 +4,18 @@ # https://aka.ms/yaml trigger: + batch: true branches: include: - - master - - dev - - dev-slice + - main + - dev* tags: include: - v* jobs: - job: - displayName: Windows + displayName: Windows 2022 pool: vmImage: windows-2022 @@ -29,10 +29,30 @@ jobs: BuildType: release cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release MSBuildConfiguration: Release + Release SIMD: + BuildType: release-simd + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON -DMI_OPT_SIMD=ON -DMI_WIN_USE_FIXED_TLS=ON + MSBuildConfiguration: Release Secure: BuildType: secure cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON MSBuildConfiguration: Release + Debug x86: + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -A Win32 + MSBuildConfiguration: Debug + Release x86: + BuildType: release + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -A Win32 + MSBuildConfiguration: Release + Debug Fixed TLS: + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_WIN_USE_FIXED_TLS=ON + MSBuildConfiguration: Debug + Release Fixed TLS: + BuildType: release + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_WIN_USE_FIXED_TLS=ON + MSBuildConfiguration: Release steps: - task: CMake@1 inputs: @@ -43,7 +63,7 @@ jobs: solution: $(BuildType)/libmimalloc.sln configuration: '$(MSBuildConfiguration)' msbuildArguments: -m - - script: ctest --verbose --timeout 120 -C $(MSBuildConfiguration) + - script: ctest --verbose --timeout 240 -C $(MSBuildConfiguration) workingDirectory: $(BuildType) displayName: CTest #- script: $(BuildType)\$(BuildType)\mimalloc-test-stress @@ -52,7 +72,7 @@ jobs: # artifact: mimalloc-windows-$(BuildType) - job: - displayName: Linux + displayName: Ubuntu 22.04 pool: vmImage: ubuntu-22.04 @@ -88,6 +108,11 @@ jobs: CXX: clang++ BuildType: release-clang cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + Release SIMD Clang: + CC: clang + CXX: clang++ + BuildType: release-simd-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON -DMI_OPT_SIMD=ON Secure Clang: CC: clang CXX: clang++ @@ -112,8 +137,13 @@ jobs: CC: clang CXX: clang++ BuildType: debug-tsan-clang-cxx - cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_USE_CXX=ON -DMI_DEBUG_TSAN=ON - + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMI_USE_CXX=ON -DMI_DEBUG_TSAN=ON + Debug Guarded Clang: + CC: clang + CXX: clang + BuildType: debug-guarded-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMI_DEBUG_FULL=ON -DMI_GUARDED=ON + steps: - task: CMake@1 inputs: @@ -121,17 +151,19 @@ jobs: cmakeArgs: .. $(cmakeExtraArgs) - script: make -j$(nproc) -C $(BuildType) displayName: Make - - script: ctest --verbose --timeout 180 + - script: ctest --verbose --timeout 240 workingDirectory: $(BuildType) displayName: CTest + env: + MIMALLOC_GUARDED_SAMPLE_RATE: 1000 # - upload: $(Build.SourcesDirectory)/$(BuildType) # artifact: mimalloc-ubuntu-$(BuildType) - job: - displayName: macOS + displayName: macOS 14 (Sonoma) pool: vmImage: - macOS-latest + macOS-14 strategy: matrix: Debug: @@ -140,6 +172,9 @@ jobs: Release: BuildType: release cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + Release SIMD: + BuildType: release-simd + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON -DMI_OPT_SIMD=ON Secure: BuildType: secure cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON @@ -150,48 +185,79 @@ jobs: cmakeArgs: .. $(cmakeExtraArgs) - script: make -j$(sysctl -n hw.ncpu) -C $(BuildType) displayName: Make - # - script: MIMALLOC_VERBOSE=1 ./mimalloc-test-api - # workingDirectory: $(BuildType) - # displayName: TestAPI - # - script: MIMALLOC_VERBOSE=1 ./mimalloc-test-stress - # workingDirectory: $(BuildType) - # displayName: TestStress - - script: ctest --verbose --timeout 120 + - script: ctest --verbose --timeout 240 workingDirectory: $(BuildType) displayName: CTest - # - upload: $(Build.SourcesDirectory)/$(BuildType) # artifact: mimalloc-macos-$(BuildType) -# - job: -# displayName: Windows-2017 -# pool: -# vmImage: -# vs2017-win2016 -# strategy: -# matrix: -# Debug: -# BuildType: debug -# cmakeExtraArgs: -A x64 -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -# MSBuildConfiguration: Debug -# Release: -# BuildType: release -# cmakeExtraArgs: -A x64 -DCMAKE_BUILD_TYPE=Release -# MSBuildConfiguration: Release -# Secure: -# BuildType: secure -# cmakeExtraArgs: -A x64 -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON -# MSBuildConfiguration: Release -# steps: -# - task: CMake@1 -# inputs: -# workingDirectory: $(BuildType) -# cmakeArgs: .. $(cmakeExtraArgs) -# - task: MSBuild@1 -# inputs: -# solution: $(BuildType)/libmimalloc.sln -# configuration: '$(MSBuildConfiguration)' -# - script: | -# cd $(BuildType) -# ctest --verbose --timeout 120 -# displayName: CTest +# ---------------------------------------------------------- +# Other OS versions (just debug mode) +# ---------------------------------------------------------- + +- job: + displayName: Ubuntu 24.04 + pool: + vmImage: + ubuntu-24.04 + strategy: + matrix: + Debug: + CC: gcc + CXX: g++ + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + Debug++: + CC: gcc + CXX: g++ + BuildType: debug-cxx + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON + Debug Clang: + CC: clang + CXX: clang++ + BuildType: debug-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + Debug++ Clang: + CC: clang + CXX: clang++ + BuildType: debug-clang-cxx + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON + Release Clang: + CC: clang + CXX: clang++ + BuildType: release-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + steps: + - task: CMake@1 + inputs: + workingDirectory: $(BuildType) + cmakeArgs: .. $(cmakeExtraArgs) + - script: make -j$(nproc) -C $(BuildType) + displayName: Make + - script: ctest --verbose --timeout 240 + workingDirectory: $(BuildType) + displayName: CTest + +- job: + displayName: macOS 15 (Sequoia) + pool: + vmImage: + macOS-15 + strategy: + matrix: + Debug: + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + # Release: + # BuildType: release + # cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + steps: + - task: CMake@1 + inputs: + workingDirectory: $(BuildType) + cmakeArgs: .. $(cmakeExtraArgs) + - script: make -j$(sysctl -n hw.ncpu) -C $(BuildType) + displayName: Make + - script: ctest --verbose --timeout 240 + workingDirectory: $(BuildType) + displayName: CTest diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/bundle.bat b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/bundle.bat new file mode 100644 index 00000000..a924f9e0 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/bundle.bat @@ -0,0 +1,74 @@ +@echo off +rem ------------------------------------------------------------------ +rem Build a binary release +rem ------------------------------------------------------------------ +setlocal + +mkdir "out/bundle" + +set MI_TAG= +for /F "tokens=*" %%x in ('git describe --tag 2^> nul') do (set MI_TAG=%%x) +set MI_COMMIT= +for /F "tokens=*" %%x in ('git rev-parse HEAD 2^> nul') do (set MI_COMMIT=%%x) + +echo. +echo Tag : %MI_TAG% +echo Commit: %MI_COMMIT% + +rem (not needed as it is already done on other platforms) +rem curl --proto =https --tlsv1.2 -f -L -o "https://github.com/microsoft/mimalloc/archive/%MI_COMMIT%.tar.gz" "out/bundle/mimalloc-%MI_TAG%-source.tar.gz" + +call:bundle x64 debug Debug "-A x64 -T ClangCl" || goto :err +call:bundle x64 release Release "-A x64 -T ClangCl -DMI_OPT_ARCH=ON" || goto :err +call:bundle x64 secure Release "-A x64 -T ClangCl -DMI_OPT_ARCH=ON -DMI_SECURE=ON" || goto:err +call:archive x64 || goto:err + +call:bundle x86 debug Debug "-A Win32 -T ClangCl" || goto :err +call:bundle x86 release Release "-A Win32 -T ClangCl -DMI_OPT_ARCH=ON" || goto :err +call:bundle x86 secure Release "-A Win32 -T ClangCl -DMI_OPT_ARCH=ON -DMI_SECURE=ON" || goto:err +call:archive x86 || goto:err + +call:bundle arm64 debug Debug "-A ARM64 -T ClangCl" || goto :err +call:bundle arm64 release Release "-A ARM64 -T ClangCl -DMI_OPT_ARCH=ON" || goto :err +call:bundle arm64 secure Release "-A ARM64 -T ClangCl -DMI_OPT_ARCH=ON -DMI_SECURE=ON" || goto:err +call:archive arm64 || goto:err + +call:bundle arm64ec debug Debug "-A Arm64EC" || goto :err +call:bundle arm64ec release Release "-A Arm64EC -DMI_OPT_ARCH=ON" || goto :err +call:bundle arm64ec secure Release "-A Arm64EC -DMI_OPT_ARCH=ON -DMI_SECURE=ON" || goto:err +call:archive arm64ec || goto:err + +echo. +echo Done +exit /b 0 + +:err +echo Failed with error: %errorlevel% +exit /b %errorlevel% + + +:bundle +rem bundle: 1:arch 2:build-variant 3:build-type 4:cmake options +echo. +echo Build: %~1 %~2: %~3 +mkdir "out/bundle/prefix-%~1" +cmake . -B "out/bundle/%~2-%~1" -DCMAKE_BUILD_TYPE=%~3 %~4 || goto :err +cmake --build "out/bundle/%~2-%~1" --config %~3 || goto :err +if "%~1" == "x64" (ctest --test-dir "out/bundle/%~2-%~1" -C %~3 || goto :err) +if "%~1" == "x86" (ctest --test-dir "out/bundle/%~2-%~1" -C %~3 || goto :err) +cmake --install "out/bundle/%~2-%~1" --prefix "out/bundle/prefix-%~1" --config %~3 || goto :err +exit /b %errorlevel% + + +:archive +rem archive: 1:arch +echo. +echo Archive: %~1 +if "%~1" == "x64" (xcopy /Y /F "bin/minject.exe" "out/bundle/prefix-%~1/bin") +if "%~1" == "x86" (xcopy /Y /F "bin/minject32.exe" "out/bundle/prefix-%~1/bin") +if "%~1" == "arm64" (xcopy /Y /F "bin/minject-arm64.exe" "out/bundle/prefix-%~1/bin") +if "%~1" == "arm64ec" (xcopy /Y /F "bin/minject-arm64.exe" "out/bundle/prefix-%~1/bin") +cd "out/bundle/prefix-%~1" +tar -czvf "../mimalloc-%MI_TAG%-windows-%~1.tar.gz" . +cd ../../.. +exit /b %errorlevel% diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/bundle.sh b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/bundle.sh new file mode 100755 index 00000000..bc864b35 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/bundle.sh @@ -0,0 +1,236 @@ +#!/bin/sh -e + +#----------------------------------------------------------------------------- +# Bundle release +#----------------------------------------------------------------------------- + +OSARCH="" +OSNAME="" +MI_TAG="" +MI_COMMIT="" + +#--------------------------------------------------------- +# Helper functions +#--------------------------------------------------------- + +info() { + if [ -z "$QUIET" ] ; then + echo "$@" + fi +} + +warn() { + echo "$@" >&2 +} + +stop() { + warn "$@" + exit 1 +} + +has_cmd() { + command -v "$1" > /dev/null 2>&1 +} + + +#--------------------------------------------------------- +# Detect git tag and commit +#--------------------------------------------------------- + +detect_git_tag() { + if [ -z "$MI_COMMIT" ] ; then + MI_COMMIT=`git rev-parse HEAD` + fi + if [ -z "$MI_TAG" ] ; then + MI_TAG=`git describe --tag` + fi + info "Tag : $MI_TAG" + info "Commit : $MI_COMMIT" +} + + +#--------------------------------------------------------- +# Detect OS and cpu architecture +#--------------------------------------------------------- + +detect_osarch() { + arch="$(uname -m)" + case "$arch" in + x86_64*|amd64*) + arch="x64";; + x86*|i[35678]86*) + arch="x86";; + arm64*|aarch64*|armv8*) + arch="arm64";; + arm*) + arch="arm";; + parisc*) + arch="hppa";; + esac + + OSNAME="linux" + case "$(uname)" in + [Ll]inux) + OSNAME="linux";; + [Dd]arwin) + OSNAME="macos";; + [Ff]ree[Bb][Ss][Dd]) + OSNAME="unix-freebsd";; + [Oo]pen[Bb][Ss][Dd]) + OSNAME="unix-openbsd";; + *) + info "Warning: assuming generic Linux" + esac + OSARCH="$OSNAME-$arch" +} + +#--------------------------------------------------------- +# Command line options +#--------------------------------------------------------- + +process_options() { + while : ; do + flag="$1" + case "$flag" in + *=*) flag_arg="${flag#*=}";; + *) flag_arg="yes" ;; + esac + # echo "option: $flag, arg: $flag_arg" + case "$flag" in + "") break;; + -q|--quiet) + QUIET="yes";; + -p) shift + PREFIX="$1";; + -p=*|--prefix=*) + case "$flag_arg" in + "~") PREFIX="$HOME" ;; + "~/"*) PREFIX="$HOME/${flag_arg#~/}" ;; + *) PREFIX="$flag_arg" ;; + esac;; + -h|--help|-\?|help|\?) + MODE="help";; + *) case "$flag" in + *) warn "warning: unknown option \"$1\".";; + esac;; + esac + shift + done +} + +#--------------------------------------------------------- +# Download +#--------------------------------------------------------- + +download_failed() { # + stop "unable to download: $2" +} + +download_file() { # + case "$1" in + ftp://*|http://*|https://*) + info "Downloading: $1" + if has_cmd curl ; then + if ! curl ${QUIET:+-sS} --proto =https --tlsv1.2 -f -L -o "$2" "$1"; then + download_failed "curl" $1 + fi + elif has_cmd wget ; then + if ! wget ${QUIET:+-q} --https-only "-O$2" "$1"; then + download_failed "wget" $1 + fi + else + stop "Neither 'curl' nor 'wget' is available; install one to continue." + fi;; + *) + info "Copying: $1" + if ! cp "$1" "$2" ; then + stop "Unable to copy from $1" + fi;; + esac +} + +#--------------------------------------------------------- +# Bundle +#--------------------------------------------------------- + +download_source_at_commit() { # + download_file "https://github.com/microsoft/mimalloc/archive/$1.tar.gz" "$2" +} + +build_test_install() { # + info "Build, test, install: $1 to $3" + build_dir="$2/$1" + mkdir -p "$build_dir" + cmake . -B "$build_dir" $4 + cmake --build "$build_dir" --parallel 4 + ctest --test-dir "$build_dir" + cmake --install "$build_dir" --prefix "$3" +} + +main_bundle() { + # config + bundle_dir="out/bundle" + mkdir -p "$bundle_dir" + if [ -z "$PREFIX" ] ; then + prefix_dir="$bundle_dir/prefix" + else + prefix_dir="$PREFIX" + fi + + # build + build_test_install "debug" "$bundle_dir" "$prefix_dir" "-DCMAKE_BUILD_TYPE=Debug" + build_test_install "release" "$bundle_dir" "$prefix_dir" "-DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON" + build_test_install "secure" "$bundle_dir" "$prefix_dir" "-DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON -DMI_SECURE=ON" + + # archive binaries + binary_archive_name="mimalloc-$MI_TAG-$OSARCH.tar.gz" + binary_archive="$bundle_dir/$binary_archive_name" + info "Create binary archive: $binary_archive_name" + (cd "$prefix_dir" && tar -czvf "../$binary_archive_name" .) + + # source archive + if [ "$OSNAME" = "linux" ] ; then + info "Download source archive for $MI_TAG" + source_archive="$bundle_dir/mimalloc-$MI_TAG-source.tar.gz" + download_source_at_commit "$MI_COMMIT" "$source_archive" + fi + + # done + info "" + info "Created:" + info " - $binary_archive" + if [ -n "$source_archive" ] ; then + info " - $source_archive" + fi + info "" + info "Done." +} + +#--------------------------------------------------------- +# Main +#--------------------------------------------------------- + +main_help() { + info "command:" + info " ./bin/bundle.sh [options]" + info "" + info "options:" + info " -q, --quiet suppress output" + info " -p, --prefix= prefix directory ($PREFIX)" + info " -h, --help show command line options" + info "" +} + +main_start() { + detect_osarch + detect_git_tag + process_options "$@" + if [ "$MODE" = "help" ] ; then + main_help + else + main_bundle + fi +} + +# note: only start executing commands now to guard against partial downloads +main_start "$@" diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64.dll b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64.dll new file mode 100755 index 00000000..27172d2c Binary files /dev/null and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64.dll differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64.lib b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64.lib new file mode 100755 index 00000000..dca80b9b Binary files /dev/null and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64.lib differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64ec.dll b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64ec.dll new file mode 100755 index 00000000..a228af39 Binary files /dev/null and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64ec.dll differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64ec.lib b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64ec.lib new file mode 100755 index 00000000..0ce77436 Binary files /dev/null and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect-arm64ec.lib differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll old mode 100644 new mode 100755 index a3a3591f..ec035f1b Binary files a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib old mode 100644 new mode 100755 index de128bb9..785fa475 Binary files a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll old mode 100644 new mode 100755 index 522723e5..92578f24 Binary files a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib old mode 100644 new mode 100755 index 87f19b8e..bf649787 Binary files a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject-arm64.exe b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject-arm64.exe new file mode 100644 index 00000000..4293b0ff Binary files /dev/null and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject-arm64.exe differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject.exe b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject.exe index dba8f80f..91c79512 100644 Binary files a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject.exe and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject.exe differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject32.exe b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject32.exe index f837383b..077f235b 100644 Binary files a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject32.exe and b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/minject32.exe differ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/readme.md b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/readme.md index 9b121bda..69eed411 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/readme.md +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/bin/readme.md @@ -1,27 +1,46 @@ # Windows Override -Dynamically overriding on mimalloc on Windows -is robust and has the particular advantage to be able to redirect all malloc/free calls that go through -the (dynamic) C runtime allocator, including those from other DLL's or libraries. -As it intercepts all allocation calls on a low level, it can be used reliably -on large programs that include other 3rd party components. -There are four requirements to make the overriding work robustly: +We use a separate redirection DLL to override mimalloc on Windows +such that we redirect all malloc/free calls that go through the (dynamic) C runtime allocator, +including those from other DLL's or libraries. As it intercepts all allocation calls on a low level, +it can be used on large programs that include other 3rd party components. +There are four requirements to make the overriding work well: 1. Use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch). -2. Link your program explicitly with `mimalloc-override.dll` library. - To ensure the `mimalloc-override.dll` is loaded at run-time it is easiest to insert some - call to the mimalloc API in the `main` function, like `mi_version()` - (or use the `/INCLUDE:mi_version` switch on the linker). See the `mimalloc-override-test` project - for an example on how to use this. +2. Link your program explicitly with the `mimalloc.dll.lib` export library for + the `mimalloc.dll` -- which contains all mimalloc functionality. + To ensure the `mimalloc.dll` is actually loaded at run-time it is easiest + to insert some call to the mimalloc API in the `main` function, like `mi_version()` + (or use the `/include:mi_version` switch on the linker, or + similarly, `#pragma comment(linker, "/include:mi_version")` in some source file). + See the `mimalloc-test-override` project for an example on how to use this. -3. The `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be put - in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency of that DLL). - The redirection DLL ensures that all calls to the C runtime malloc API get redirected to - mimalloc functions (which reside in `mimalloc-override.dll`). +3. The `mimalloc-redirect.dll` must be put in the same folder as the main + `mimalloc.dll` at runtime (as it is a dependency of that DLL). + The redirection DLL ensures that all calls to the C runtime malloc API get + redirected to mimalloc functions (which reside in `mimalloc.dll`). -4. Ensure the `mimalloc-override.dll` comes as early as possible in the import +4. Ensure the `mimalloc.dll` comes as early as possible in the import list of the final executable (so it can intercept all potential allocations). + You can use `minject -l ` to check this if needed. + +```csharp +┌──────────────┐ +│ Your Program │ +└────┬─────────┘ + │ + │ mi_version() ┌───────────────┐ ┌───────────────────────┐ + ├──────────────►│ mimalloc.dll ├────►│ mimalloc-redirect.dll │ + │ └──────┬────────┘ └───────────────────────┘ + │ ▼ + │ malloc() etc. ┌──────────────┐ + ├──────────────►│ ucrtbase.dll │ + │ └──────────────┘ + │ + │ + └──────────────► ... +``` For best performance on Windows with C++, it is also recommended to also override the `new`/`delete` operations (by including @@ -29,18 +48,43 @@ is also recommended to also override the `new`/`delete` operations (by including a single(!) source file in your project). The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic -overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully redirected. +overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully +redirected. + +### Other Platforms + +You always link with `mimalloc.dll` but for different platforms you may +need a specific redirection DLL: + +- __x64__: `mimalloc-redirect.dll`. +- __x86__: `mimalloc-redirect32.dll`. Use for older 32-bit Windows programs. +- __arm64__: `mimalloc-redirect-arm64.dll`. Use for native Windows arm64 programs. +- __arm64ec__: `mimalloc-redirect-arm64ec.dll`. The [arm64ec] ABI is "emulation compatible" + mode on Windows arm64. Unfortunately we cannot run x64 code emulated on Windows arm64 with + the x64 mimalloc override directly (since the C runtime always uses `arm64ec`). Instead: + 1. Build the program as normal for x64 and link as normal with the x64 + `mimalloc.dll.lib` export library. + 2. Now separately build `mimalloc.dll` in `arm64ec` mode and _overwrite_ your + previous (x64) `mimalloc.dll` -- the loader can handle the mix of arm64ec + and x64 code. Now use `mimalloc-redirect-arm64ec.dll` to match your new + arm64ec `mimalloc.dll`. The main program stays as is and can be fully x64 + or contain more arm64ec modules. At runtime, the arm64ec `mimalloc.dll` will + run with native arm64 instructions while the rest of the program runs emulated x64. -## Minject +[arm64ec]: https://learn.microsoft.com/en-us/windows/arm/arm64ec -We cannot always re-link an executable with `mimalloc-override.dll`, and similarly, we cannot always -ensure the the DLL comes first in the import table of the final executable. + +### Minject + +We cannot always re-link an executable with `mimalloc.dll`, and similarly, we +cannot always ensure that the DLL comes first in the import table of the final executable. In many cases though we can patch existing executables without any recompilation -if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the `mimalloc-override.dll` -into the import table (and put `mimalloc-redirect.dll` in the same folder) -Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388). +if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the +`mimalloc.dll` into the import table (and put `mimalloc-redirect.dll` in the same +directory) Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388). -The `minject` program can also do this from the command line, use `minject --help` for options: +The `minject` program can also do this from the command line +Use `minject --help` for options: ``` > minject --help @@ -58,8 +102,8 @@ options: -l --list only list imported modules -i --inplace update the exe in-place (make sure there is a backup!) -f --force always overwrite without prompting - --postfix=

use

as a postfix to the mimalloc dll (default is 'override') - e.g. use --postfix=override-debug to link with mimalloc-override-debug.dll + --postfix=

use

as a postfix to the mimalloc dll. + e.g. use --postfix=debug to link with mimalloc-debug.dll notes: Without '--inplace' an injected is generated with the same name ending in '-mi'. @@ -69,3 +113,6 @@ examples: > minject --list myprogram.exe > minject --force --inplace myprogram.exe ``` + +For x86 32-bit binaries, use `minject32`, and for arm64 binaries use `minject-arm64`. + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake index 81fd3c9d..30ca6305 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake @@ -1,6 +1,6 @@ -set(mi_version_major 2) -set(mi_version_minor 1) -set(mi_version_patch 7) +set(mi_version_major 3) +set(mi_version_minor 4) +set(mi_version_patch 1) set(mi_version ${mi_version_major}.${mi_version_minor}) set(PACKAGE_VERSION ${mi_version}) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/alpine-arm32v7/Dockerfile b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine-arm32v7/Dockerfile similarity index 62% rename from test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/alpine-arm32v7/Dockerfile rename to test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine-arm32v7/Dockerfile index 56f071db..f191641d 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/alpine-arm32v7/Dockerfile +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine-arm32v7/Dockerfile @@ -1,10 +1,10 @@ # install from an image -# download first an appropiate tar.gz image into the current directory -# from: +# download first an appropriate tar.gz image into the current directory +# from FROM scratch # Substitute the image name that was downloaded -ADD alpine-minirootfs-20240329-armv7.tar.gz / +ADD alpine-minirootfs-20260127-armv7.tar.gz / # Install tools RUN apk add build-base make cmake @@ -15,7 +15,7 @@ RUN mkdir -p /home/dev WORKDIR /home/dev # Get mimalloc -RUN git clone https://github.com/microsoft/mimalloc -b dev-slice +RUN git clone https://github.com/microsoft/mimalloc -b dev3 RUN mkdir -p mimalloc/out/release RUN mkdir -p mimalloc/out/debug diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine-x86/Dockerfile b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine-x86/Dockerfile new file mode 100644 index 00000000..ce2f4928 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine-x86/Dockerfile @@ -0,0 +1,28 @@ +# install from an image +# download first an appropriate tar.gz image into the current directory +# from +FROM scratch + +# Substitute the image name that was downloaded +ADD alpine-minirootfs-20250108-x86.tar.gz / + +# Install tools +RUN apk add build-base make cmake +RUN apk add git +RUN apk add vim + +RUN mkdir -p /home/dev +WORKDIR /home/dev + +# Get mimalloc +RUN git clone https://github.com/microsoft/mimalloc -b dev3 +RUN mkdir -p mimalloc/out/release +RUN mkdir -p mimalloc/out/debug + +# Build mimalloc debug +WORKDIR /home/dev/mimalloc/out/debug +RUN cmake ../.. -DMI_DEBUG_FULL=ON +# RUN make -j +# RUN make test + +CMD ["/bin/sh"] diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/alpine/Dockerfile b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine/Dockerfile similarity index 85% rename from test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/alpine/Dockerfile rename to test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine/Dockerfile index b222b791..a8f29a59 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/alpine/Dockerfile +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/alpine/Dockerfile @@ -10,7 +10,7 @@ RUN mkdir -p /home/dev WORKDIR /home/dev # Get mimalloc -RUN git clone https://github.com/microsoft/mimalloc -b dev-slice +RUN git clone https://github.com/microsoft/mimalloc -b dev3 RUN mkdir -p mimalloc/out/release RUN mkdir -p mimalloc/out/debug diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/manylinux-x64/Dockerfile b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/manylinux-x64/Dockerfile similarity index 87% rename from test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/manylinux-x64/Dockerfile rename to test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/manylinux-x64/Dockerfile index 22d37e5a..6a9c8af6 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/manylinux-x64/Dockerfile +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/manylinux-x64/Dockerfile @@ -10,7 +10,7 @@ RUN mkdir -p /home/dev WORKDIR /home/dev # Get mimalloc -RUN git clone https://github.com/microsoft/mimalloc -b dev-slice +RUN git clone https://github.com/microsoft/mimalloc -b dev3 RUN mkdir -p mimalloc/out/release RUN mkdir -p mimalloc/out/debug diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/readme.md b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/readme.md similarity index 100% rename from test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/docker/readme.md rename to test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/docker/readme.md diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/portfile.cmake b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/portfile.cmake new file mode 100644 index 00000000..55485474 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/portfile.cmake @@ -0,0 +1,64 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO microsoft/mimalloc + HEAD_REF master + + # The "REF" can be a commit hash, branch name (dev3), or a version (v3.4.1). + REF "v${VERSION}" + + # The sha512 is the hash of the tar.gz bundle. + # (To get the sha512, run `vcpkg install "mimalloc[override]" --overlay-ports=

` and copy the sha from the error message.) + # (and maybe `vcpkg remove mimalloc` first to remove any previous install) + SHA512 601bdf622d0bc7521edf0cc73d1caec9d976bcd1faa689ff48cc18a9a6a3b2294b571fc71d3266b38907bc5aad10a41d92d03d2cdde139a30b08357ee7bc25c5 +) + +vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS + FEATURES + c MI_NO_USE_CXX + guarded MI_GUARDED + secure MI_SECURE + override MI_OVERRIDE + optarch MI_OPT_ARCH + nooptarch MI_NO_OPT_ARCH + optsimd MI_OPT_SIMD + xmalloc MI_XMALLOC + asm MI_SEE_ASM +) +string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" MI_BUILD_STATIC) +string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" MI_BUILD_SHARED) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DMI_USE_CXX=ON + -DMI_BUILD_TESTS=OFF + -DMI_BUILD_OBJECT=ON + -DMI_BUILD_STATIC=${MI_BUILD_STATIC} + -DMI_BUILD_SHARED=${MI_BUILD_SHARED} + -DMI_INSTALL_TOPLEVEL=ON + ${FEATURE_OPTIONS} +) + +vcpkg_cmake_install() +vcpkg_copy_pdbs() + +file(COPY + "${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" + "${CMAKE_CURRENT_LIST_DIR}/usage" + DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" +) +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/mimalloc) + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic") + # todo: why is this needed? + vcpkg_replace_string( + "${CURRENT_PACKAGES_DIR}/include/mimalloc.h" + "!defined(MI_SHARED_LIB)" + "0 // !defined(MI_SHARED_LIB)" + ) +endif() +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share") + +vcpkg_fixup_pkgconfig() +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/readme.md b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/readme.md new file mode 100644 index 00000000..014f2867 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/readme.md @@ -0,0 +1,40 @@ +# Vcpkg support + +This directory is meant to provide the sources for the official [vcpkg port] +of mimalloc, but can also be used to override the official port with +your own variant. + +For example, you can edit the [`portfile.cmake`](portfile.cmake) +to check out a specific commit, version, or branch of mimalloc, or set further options. +You can install such custom port as: + +```sh +$ vcpkg install "mimalloc[override]" --recurse --overlay-ports=./contrib/vcpkg +``` + +This will also show the correct sha512 hash if you use a custom version. +Another way is to refer to the overlay from the [vcpkg-configuration.json](https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-configuration-json) file. +See also the vcpkg [documentation](https://learn.microsoft.com/en-us/vcpkg/produce/update-package-version) for more information. + + +# Using mimalloc from vcpkg + +When using [cmake with vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/get-started?pivots=shell-powershell), +you can use mimalloc from the `CMakeLists.txt` as: + +```cmake +find_package(mimalloc CONFIG REQUIRED) +target_link_libraries(main PRIVATE mimalloc) +``` + +See [`test/CMakeLists.txt](../../test/CMakeLists.txt) for more examples. + + +# Acknowledgements + +The original port for vckpg was contributed by many people, including: @vicroms, @myd7349, @PhoubeHui, @LilyWangL, +@JonLiu1993, @RT2Code, Remy Tassoux, @wangao, @BillyONeal, @jiayuehua, @dg0yt, @gerar-ryan-immersaview, @nickdademo, +and @jimwang118 -- Thank you so much! + + +[vcpkg port]: https://github.com/microsoft/vcpkg/tree/master/ports/mimalloc diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/usage b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/usage new file mode 100644 index 00000000..7ce649a7 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/usage @@ -0,0 +1,20 @@ +Use the following CMake targets to import mimalloc: + + find_package(mimalloc CONFIG REQUIRED) + target_link_libraries(main PRIVATE mimalloc) + +And use mimalloc in your sources as: + + #include + #include + int main(int argc, char** argv) { + int* p = mi_malloc_tp(int); + *p = mi_version(); + printf("mimalloc version: %d\n", *p); + mi_free(p); + return 0; + } + +When dynamically overriding on Windows, ensure `mimalloc.dll` is linked through some call to +mimalloc (e.g. `mi_version()`), and that the `mimalloc-redirect.dll` is in the same directory. +See https://github.com/microsoft/mimalloc/blob/dev/bin/readme.md for detailed information. diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/vcpkg-cmake-wrapper.cmake b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/vcpkg-cmake-wrapper.cmake new file mode 100644 index 00000000..1b355722 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,20 @@ +_find_package(${ARGS}) + +if(CMAKE_CURRENT_LIST_DIR STREQUAL "${MIMALLOC_CMAKE_DIR}/${MIMALLOC_VERSION_DIR}") + set(MIMALLOC_INCLUDE_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include") + # As in vcpkg.cmake + if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$") + set(MIMALLOC_LIBRARY_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/lib") + else() + set(MIMALLOC_LIBRARY_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib") + endif() + set(MIMALLOC_OBJECT_DIR "${MIMALLOC_LIBRARY_DIR}") + set(MIMALLOC_TARGET_DIR "${MIMALLOC_LIBRARY_DIR}") +endif() + +# vcpkg always configures either a static or dynamic library. +# ensure to always expose the mimalloc target as either the static or dynamic build. +if(TARGET mimalloc-static AND NOT TARGET mimalloc) + add_library(mimalloc INTERFACE IMPORTED) + set_target_properties(mimalloc PROPERTIES INTERFACE_LINK_LIBRARIES mimalloc-static) +endif() diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/vcpkg.json b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/vcpkg.json new file mode 100644 index 00000000..b797c18f --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/contrib/vcpkg/vcpkg.json @@ -0,0 +1,48 @@ +{ + "name": "mimalloc", + "version": "3.4.1", + "port-version": 0, + "description": "Compact general purpose allocator with excellent performance", + "homepage": "https://github.com/microsoft/mimalloc", + "license": "MIT", + "supports": "!uwp", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ], + "features": { + "c": { + "description": "Use C11 compilation (this can still override new/delete)" + }, + "override": { + "description": "Override the standard malloc/free interface" + }, + "secure": { + "description": "Use full security mitigations (like guard pages and randomization)" + }, + "guarded": { + "description": "Use build that support guard pages after objects controlled with MIMALLOC_GUARDED_SAMPLE_RATE" + }, + "xmalloc": { + "description": "If out-of-memory, call abort() instead of returning NULL" + }, + "optarch": { + "description": "Use architecture specific optimizations (on x64: '-march=haswell;-mavx2', on arm64: '-march=armv8.1-a')" + }, + "nooptarch": { + "description": "Do _not_ use architecture specific optimizations (on x64: '-march=haswell;-mavx2', on arm64: '-march=armv8.1-a')" + }, + "optsimd": { + "description": "Allow use of SIMD instructions (avx2 or neon) (requires 'optarch' to be enabled)" + }, + "asm": { + "description": "Generate assembly files" + } + } +} \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/doxyfile b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/doxyfile index d03a70f5..53f874cf 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/doxyfile +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.1 +# Doxyfile 1.11.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -53,6 +63,12 @@ PROJECT_BRIEF = PROJECT_LOGO = mimalloc-logo.svg +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If @@ -60,16 +76,28 @@ PROJECT_LOGO = mimalloc-logo.svg OUTPUT_DIRECTORY = .. -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,26 +109,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -258,16 +278,16 @@ TAB_SIZE = 2 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -312,8 +332,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -344,11 +364,22 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. +# Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -361,8 +392,8 @@ AUTOLINK_SUPPORT = YES # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO @@ -374,9 +405,9 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO @@ -460,7 +491,7 @@ TYPEDEF_HIDES_STRUCT = YES LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing @@ -473,6 +504,14 @@ LOOKUP_CACHE_SIZE = 0 NUM_PROC_THREADS = 1 +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -554,7 +593,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -585,14 +625,15 @@ INTERNAL_DOCS = NO # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be be set to NO to properly deal with +# are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. -# The default value is: system dependent. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -610,6 +651,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -767,7 +814,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -813,27 +861,50 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. -# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO @@ -844,13 +915,27 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -871,10 +956,21 @@ INPUT = mimalloc-doc.h # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -886,12 +982,12 @@ INPUT_ENCODING = UTF-8 # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, -# *.ucf, *.qsf and *.ice. +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -973,10 +1069,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = @@ -1021,6 +1114,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -1062,6 +1160,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1076,7 +1183,8 @@ USE_MDFILE_AS_MAINPAGE = SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO @@ -1159,9 +1267,11 @@ VERBATIM_HEADERS = YES CLANG_ASSISTED_PARSING = NO -# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to -# YES then doxygen will add the directory of each input to the include path. +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. # The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_ADD_INC_PATHS = YES @@ -1197,10 +1307,11 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1279,7 +1390,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = mimalloc-doxygen.css @@ -1294,9 +1410,22 @@ HTML_EXTRA_STYLESHEET = mimalloc-doxygen.css HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1306,7 +1435,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 189 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1324,15 +1453,6 @@ HTML_COLORSTYLE_SAT = 12 HTML_COLORSTYLE_GAMMA = 240 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1352,6 +1472,33 @@ HTML_DYNAMIC_MENUS = NO HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1388,6 +1535,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1413,8 +1567,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: -# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1471,6 +1629,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1573,16 +1741,28 @@ DISABLE_INDEX = YES # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1607,6 +1787,13 @@ TREEVIEW_WIDTH = 180 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1627,17 +1814,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -1655,11 +1831,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1672,15 +1866,21 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = @@ -1860,29 +2060,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1925,10 +2127,16 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1941,16 +2149,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1959,14 +2157,6 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -2031,15 +2221,13 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_SOURCE_CODE = NO +RTF_EXTRA_FILES = #--------------------------------------------------------------------------- # Configuration options related to the man page output @@ -2137,21 +2325,12 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. @@ -2162,6 +2341,28 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2236,7 +2437,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2303,15 +2505,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2325,25 +2527,9 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2352,7 +2538,7 @@ HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. @@ -2369,49 +2555,77 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2453,8 +2667,8 @@ DOT_UML_DETAILS = NO # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold -# significantly it will wrapped across multiple lines. Some heuristics are apply -# to avoid ugly line breaks. +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2471,7 +2685,9 @@ TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2480,7 +2696,10 @@ INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2520,16 +2739,26 @@ GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2566,11 +2795,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2579,10 +2809,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2599,7 +2829,7 @@ PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2620,18 +2850,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2644,6 +2862,8 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2652,8 +2872,24 @@ GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. # -# Note: This setting is not only used for dot files but also for msc and -# plantuml temporary files. +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doc.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doc.h index d79eb2f8..4cc0d3ba 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doc.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doc.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -25,12 +25,15 @@ without code changes, for example, on Unix you can use it as: ``` Notable aspects of the design include: - -- __small and consistent__: the library is about 8k LOC using simple and +- __small and consistent__: the core library is about 10k LOC using simple and consistent data structures. This makes it very suitable to integrate and adapt in other projects. For runtime systems it provides hooks for a monotonic _heartbeat_ and deferred freeing (for bounded worst-case times with reference counting). + Partly due to its simplicity, mimalloc has been ported to many systems (Windows, macOS, + Linux, WASM, various BSD's, Haiku, MUSL, etc) and has excellent support for dynamic overriding. + At the same time, it is an industrial strength allocator that runs (very) large scale + distributed services on thousands of machines with excellent worst case latencies. - __free list sharding__: instead of one big free list (per size class) we have many smaller lists per "mimalloc page" which reduces fragmentation and increases locality -- @@ -45,28 +48,41 @@ Notable aspects of the design include: and the chance of contending on a single location will be low -- this is quite similar to randomized algorithms like skip lists where adding a random oracle removes the need for a more complex algorithm. -- __eager page reset__: when a "page" becomes empty (with increased chance - due to free list sharding) the memory is marked to the OS as unused ("reset" or "purged") +- __eager page purging__: when a "page" becomes empty (with increased chance + due to free list sharding) the memory is marked to the OS as unused (reset or decommitted) reducing (real) memory pressure and fragmentation, especially in long running programs. -- __secure__: _mimalloc_ can be build in secure mode, adding guard pages, +- __secure__: _mimalloc_ can be built in secure mode, adding guard pages, randomized allocation, encrypted free lists, etc. to protect against various - heap vulnerabilities. The performance penalty is only around 5% on average + heap vulnerabilities. The performance penalty is usually around 10% on average over our benchmarks. - __first-class heaps__: efficiently create and use multiple heaps to allocate across different regions. A heap can be destroyed at once instead of deallocating each object separately. + v3.2+ has true first-class heaps where one can allocate in a heap from any thread. - __bounded__: it does not suffer from _blowup_ \[1\], has bounded worst-case allocation - times (_wcat_), bounded space overhead (~0.2% meta-data, with low internal fragmentation), - and has no internal points of contention using only atomic operations. -- __fast__: In our benchmarks (see [below](#performance)), - _mimalloc_ outperforms all other leading allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc), - and usually uses less memory (up to 25% more in the worst case). A nice property - is that it does consistently well over a wide range of benchmarks. - -You can read more on the design of _mimalloc_ in the + times (_wcat_) (upto OS primitives), bounded space overhead (~0.2% meta-data, with low + internal fragmentation), and has no internal points of contention using only atomic operations. +- __fast__: In our benchmarks (see [below](#bench)), + _mimalloc_ outperforms other leading allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc), + and often uses less memory. A nice property is that it does consistently well over a wide range + of benchmarks. There is also good huge OS page support for larger server programs. + +There are three maintained versions of mimalloc. All versions are mostly equal except for +how the OS memory is handled. New development is mostly on v3, while v1 and v2 are maintained +with security and bug fixes. + +- __v1__: initial design of mimalloc (release tags: `v1.9.x`, development branch `dev`). Send PR's against this version if possible. +- __v2__: main mimalloc version. Uses thread-local segments to reduce fragmentation. (release tags: `v2.2.x`, development branch `dev2`) +- __v3__: simplifies the lock-free design of previous versions, and improves sharing of + memory between threads. On certain large workloads this version may use + (much) less memory. Also supports true first-class heaps (that can allocate from any thread) + and has more efficient heap-walking (for the CPython GC for example). + (release tags: `v3.2.x`, development branch `dev3`). + +You can read more on the design of mimalloc in the [technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action) which also has detailed benchmark results. - +To learn more about the internal implementation of mimalloc, see [include/mimalloc/types.h](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc/types.h). Further information: @@ -74,14 +90,25 @@ Further information: - \ref using - \ref environment - \ref overrides +- \ref modes +- \ref tools - \ref bench + +  + - \ref malloc -- \ref extended - \ref aligned -- \ref heap - \ref typed +- \ref zeroinit + +- \ref heap - \ref analysis +- \ref arenas +- \ref subproc + +- \ref extended - \ref options +- \ref theap - \ref posix - \ref cpp @@ -133,20 +160,9 @@ void* mi_calloc(size_t count, size_t size); /// \a mi_malloc(\a newsize). If \a newsize is larger than the /// original \a size allocated for \a p, the bytes after \a size /// are uninitialized. +/// @see mi_reallocf() void* mi_realloc(void* p, size_t newsize); -/// Re-allocate memory to \a count elements of \a size bytes, with extra memory initialized to zero. -/// @param p Pointer to a previously allocated block (or \a NULL). -/// @param count The number of elements. -/// @param size The size of each element. -/// @returns A pointer to a re-allocated block of \a count * \a size bytes, or \a NULL -/// if out of memory or if \a count * \a size overflows. -/// -/// If there is no overflow, it behaves exactly like `mi_rezalloc(p,count*size)`. -/// @see mi_reallocn() -/// @see [recallocarray()](http://man.openbsd.org/reallocarray) (on BSD). -void* mi_recalloc(void* p, size_t count, size_t size); - /// Try to re-allocate memory to \a newsize bytes _in place_. /// @param p pointer to previously allocated memory (or \a NULL). /// @param newsize the new required size in bytes. @@ -238,38 +254,6 @@ char* mi_strndup(const char* s, size_t n); /// such that mi_free() can be used on the returned result (if \a resolved_name was \a NULL). char* mi_realpath(const char* fname, char* resolved_name); -/// \} - -// ------------------------------------------------------ -// Extended functionality -// ------------------------------------------------------ - -/// \defgroup extended Extended Functions -/// Extended functionality. -/// \{ - -/// Maximum size allowed for small allocations in -/// #mi_malloc_small and #mi_zalloc_small (usually `128*sizeof(void*)` (= 1KB on 64-bit systems)) -#define MI_SMALL_SIZE_MAX (128*sizeof(void*)) - -/// Allocate a small object. -/// @param size The size in bytes, can be at most #MI_SMALL_SIZE_MAX. -/// @returns a pointer to newly allocated memory of at least \a size -/// bytes, or \a NULL if out of memory. -/// This function is meant for use in run-time systems for best -/// performance and does not check if \a size was indeed small -- use -/// with care! -void* mi_malloc_small(size_t size); - -/// Allocate a zero initialized small object. -/// @param size The size in bytes, can be at most #MI_SMALL_SIZE_MAX. -/// @returns a pointer to newly allocated zero-initialized memory of at -/// least \a size bytes, or \a NULL if out of memory. -/// This function is meant for use in run-time systems for best -/// performance and does not check if \a size was indeed small -- use -/// with care! -void* mi_zalloc_small(size_t size); - /// Return the available bytes in a memory block. /// @param p Pointer to previously allocated memory (or \a NULL) /// @returns Returns the available bytes in the memory block, or @@ -278,237 +262,52 @@ void* mi_zalloc_small(size_t size); /// The returned size can be /// used to call \a mi_expand successfully. /// The returned size is always at least equal to the -/// allocated size of \a p, and, in the current design, -/// should be less than 16.7% more. +/// allocated size of \a p. /// /// @see [_msize](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize?view=vs-2017) (Windows) /// @see [malloc_usable_size](http://man7.org/linux/man-pages/man3/malloc_usable_size.3.html) (Linux) /// @see mi_good_size() size_t mi_usable_size(void* p); -/// Return the used allocation size. +/// Return the probable allocation block size for a given required size. /// @param size The minimal required size in bytes. /// @returns the size `n` that will be allocated, where `n >= size`. /// /// Generally, `mi_usable_size(mi_malloc(size)) == mi_good_size(size)`. -/// This can be used to reduce internal wasted space when +/// This function can be used to reduce internal wasted space when /// allocating buffers for example. /// /// @see mi_usable_size() size_t mi_good_size(size_t size); -/// Eagerly free memory. -/// @param force If \a true, aggressively return memory to the OS (can be expensive!) -/// -/// Regular code should not have to call this function. It can be beneficial -/// in very narrow circumstances; in particular, when a long running thread -/// allocates a lot of blocks that are freed by other threads it may improve -/// resource usage by calling this every once in a while. -void mi_collect(bool force); - -/// Deprecated -/// @param out Ignored, outputs to the registered output function or stderr by default. -/// -/// Most detailed when using a debug build. -void mi_stats_print(void* out); - -/// Print the main statistics. -/// @param out An output function or \a NULL for the default. -/// @param arg Optional argument passed to \a out (if not \a NULL) -/// -/// Most detailed when using a debug build. -void mi_stats_print_out(mi_output_fun* out, void* arg); - -/// Reset statistics. -void mi_stats_reset(void); - -/// Merge thread local statistics with the main statistics and reset. -void mi_stats_merge(void); - -/// Initialize mimalloc on a thread. -/// Should not be used as on most systems (pthreads, windows) this is done -/// automatically. -void mi_thread_init(void); - -/// Uninitialize mimalloc on a thread. -/// Should not be used as on most systems (pthreads, windows) this is done -/// automatically. Ensures that any memory that is not freed yet (but will -/// be freed by other threads in the future) is properly handled. -void mi_thread_done(void); - -/// Print out heap statistics for this thread. -/// @param out An output function or \a NULL for the default. -/// @param arg Optional argument passed to \a out (if not \a NULL) -/// -/// Most detailed when using a debug build. -void mi_thread_stats_print_out(mi_output_fun* out, void* arg); - -/// Type of deferred free functions. -/// @param force If \a true all outstanding items should be freed. -/// @param heartbeat A monotonically increasing count. -/// @param arg Argument that was passed at registration to hold extra state. -/// -/// @see mi_register_deferred_free -typedef void (mi_deferred_free_fun)(bool force, unsigned long long heartbeat, void* arg); - -/// Register a deferred free function. -/// @param deferred_free Address of a deferred free-ing function or \a NULL to unregister. -/// @param arg Argument that will be passed on to the deferred free function. -/// -/// Some runtime systems use deferred free-ing, for example when using -/// reference counting to limit the worst case free time. -/// Such systems can register (re-entrant) deferred free function -/// to free more memory on demand. When the \a force parameter is -/// \a true all possible memory should be freed. -/// The per-thread \a heartbeat parameter is monotonically increasing -/// and guaranteed to be deterministic if the program allocates -/// deterministically. The \a deferred_free function is guaranteed -/// to be called deterministically after some number of allocations -/// (regardless of freeing or available free memory). -/// At most one \a deferred_free function can be active. -void mi_register_deferred_free(mi_deferred_free_fun* deferred_free, void* arg); - -/// Type of output functions. -/// @param msg Message to output. -/// @param arg Argument that was passed at registration to hold extra state. -/// -/// @see mi_register_output() -typedef void (mi_output_fun)(const char* msg, void* arg); - -/// Register an output function. -/// @param out The output function, use `NULL` to output to stderr. -/// @param arg Argument that will be passed on to the output function. -/// -/// The `out` function is called to output any information from mimalloc, -/// like verbose or warning messages. -void mi_register_output(mi_output_fun* out, void* arg); - -/// Type of error callback functions. -/// @param err Error code (see mi_register_error() for a complete list). -/// @param arg Argument that was passed at registration to hold extra state. -/// -/// @see mi_register_error() -typedef void (mi_error_fun)(int err, void* arg); - -/// Register an error callback function. -/// @param errfun The error function that is called on an error (use \a NULL for default) -/// @param arg Extra argument that will be passed on to the error function. -/// -/// The \a errfun function is called on an error in mimalloc after emitting -/// an error message (through the output function). It as always legal to just -/// return from the \a errfun function in which case allocation functions generally -/// return \a NULL or ignore the condition. The default function only calls abort() -/// when compiled in secure mode with an \a EFAULT error. The possible error -/// codes are: -/// * \a EAGAIN: Double free was detected (only in debug and secure mode). -/// * \a EFAULT: Corrupted free list or meta-data was detected (only in debug and secure mode). -/// * \a ENOMEM: Not enough memory available to satisfy the request. -/// * \a EOVERFLOW: Too large a request, for example in mi_calloc(), the \a count and \a size parameters are too large. -/// * \a EINVAL: Trying to free or re-allocate an invalid pointer. -void mi_register_error(mi_error_fun* errfun, void* arg); - -/// Is a pointer part of our heap? -/// @param p The pointer to check. -/// @returns \a true if this is a pointer into our heap. -/// This function is relatively fast. -bool mi_is_in_heap_region(const void* p); - -/// Reserve OS memory for use by mimalloc. Reserved areas are used -/// before allocating from the OS again. By reserving a large area upfront, -/// allocation can be more efficient, and can be better managed on systems -/// without `mmap`/`VirtualAlloc` (like WASM for example). -/// @param size The size to reserve. -/// @param commit Commit the memory upfront. -/// @param allow_large Allow large OS pages (2MiB) to be used? -/// @return \a 0 if successful, and an error code otherwise (e.g. `ENOMEM`). -int mi_reserve_os_memory(size_t size, bool commit, bool allow_large); - -/// Manage a particular memory area for use by mimalloc. -/// This is just like `mi_reserve_os_memory` except that the area should already be -/// allocated in some manner and available for use my mimalloc. -/// @param start Start of the memory area -/// @param size The size of the memory area. -/// @param commit Is the area already committed? -/// @param is_large Does it consist of large OS pages? Set this to \a true as well for memory -/// that should not be decommitted or protected (like rdma etc.) -/// @param is_zero Does the area consists of zero's? -/// @param numa_node Possible associated numa node or `-1`. -/// @return \a true if successful, and \a false on error. -bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node); - -/// Reserve \a pages of huge OS pages (1GiB) evenly divided over \a numa_nodes nodes, -/// but stops after at most `timeout_msecs` seconds. -/// @param pages The number of 1GiB pages to reserve. -/// @param numa_nodes The number of nodes do evenly divide the pages over, or 0 for using the actual number of NUMA nodes. -/// @param timeout_msecs Maximum number of milli-seconds to try reserving, or 0 for no timeout. -/// @returns 0 if successful, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out. -/// -/// The reserved memory is used by mimalloc to satisfy allocations. -/// May quit before \a timeout_msecs are expired if it estimates it will take more than -/// 1.5 times \a timeout_msecs. The time limit is needed because on some operating systems -/// it can take a long time to reserve contiguous memory if the physical memory is -/// fragmented. -int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs); - -/// Reserve \a pages of huge OS pages (1GiB) at a specific \a numa_node, -/// but stops after at most `timeout_msecs` seconds. -/// @param pages The number of 1GiB pages to reserve. -/// @param numa_node The NUMA node where the memory is reserved (start at 0). -/// @param timeout_msecs Maximum number of milli-seconds to try reserving, or 0 for no timeout. -/// @returns 0 if successful, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out. -/// -/// The reserved memory is used by mimalloc to satisfy allocations. -/// May quit before \a timeout_msecs are expired if it estimates it will take more than -/// 1.5 times \a timeout_msecs. The time limit is needed because on some operating systems -/// it can take a long time to reserve contiguous memory if the physical memory is -/// fragmented. -int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs); - - -/// Is the C runtime \a malloc API redirected? -/// @returns \a true if all malloc API calls are redirected to mimalloc. -/// -/// Currently only used on Windows. -bool mi_is_redirected(); - -/// Return process information (time and memory usage). -/// @param elapsed_msecs Optional. Elapsed wall-clock time of the process in milli-seconds. -/// @param user_msecs Optional. User time in milli-seconds (as the sum over all threads). -/// @param system_msecs Optional. System time in milli-seconds. -/// @param current_rss Optional. Current working set size (touched pages). -/// @param peak_rss Optional. Peak working set size (touched pages). -/// @param current_commit Optional. Current committed memory (backed by the page file). -/// @param peak_commit Optional. Peak committed memory (backed by the page file). -/// @param page_faults Optional. Count of hard page faults. -/// -/// The \a current_rss is precise on Windows and MacOSX; other systems estimate -/// this using \a current_commit. The \a commit is precise on Windows but estimated -/// on other systems as the amount of read/write accessible memory reserved by mimalloc. -void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults); - /// \} + // ------------------------------------------------------ // Aligned allocation // ------------------------------------------------------ /// \defgroup aligned Aligned Allocation -/// /// Allocating aligned memory blocks. /// +/// Note that `alignment` always follows `size` for consistency with the unaligned +/// allocation API, but unfortunately this differs from `posix_memalign` and `aligned_alloc` in the C library. +/// /// \{ -/// The maximum supported alignment size (currently 1MiB). -#define MI_BLOCK_ALIGNMENT_MAX (1024*1024UL) - /// Allocate \a size bytes aligned by \a alignment. /// @param size number of bytes to allocate. -/// @param alignment the minimal alignment of the allocated memory. Must be less than #MI_BLOCK_ALIGNMENT_MAX. -/// @returns pointer to the allocated memory or \a NULL if out of memory. -/// The returned pointer is aligned by \a alignment, i.e. -/// `(uintptr_t)p % alignment == 0`. -/// +/// @param alignment the minimal alignment of the allocated memory. +/// @returns pointer to the allocated memory or \a NULL if out of memory, +/// or if the alignment is not a power of 2 (including 0). The \a size is unrestricted +/// (and does not have to be an integral multiple of the \a alignment). +/// The returned pointer is aligned by \a alignment, i.e. `(uintptr_t)p % alignment == 0`. /// Returns a unique pointer if called with \a size 0. +/// +/// Note that `alignment` always follows `size` for consistency with the unaligned +/// allocation API, but unfortunately this differs from `posix_memalign` and `aligned_alloc` in the C library. +/// +/// @see [aligned_alloc](https://en.cppreference.com/w/c/memory/aligned_alloc) (in the standard C11 library, with switched arguments!) /// @see [_aligned_malloc](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc?view=vs-2017) (on Windows) /// @see [aligned_alloc](http://man.openbsd.org/reallocarray) (on BSD, with switched arguments!) /// @see [posix_memalign](https://linux.die.net/man/3/posix_memalign) (on Posix, with switched arguments!) @@ -522,11 +321,12 @@ void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment); /// @param size number of bytes to allocate. /// @param alignment the minimal alignment of the allocated memory at \a offset. /// @param offset the offset that should be aligned. -/// @returns pointer to the allocated memory or \a NULL if out of memory. -/// The returned pointer is aligned by \a alignment at \a offset, i.e. -/// `((uintptr_t)p + offset) % alignment == 0`. -/// +/// @returns pointer to the allocated memory or \a NULL if out of memory, +/// or if the alignment is not a power of 2 (including 0). The \a size is unrestricted +/// (and does not have to be an integral multiple of the \a alignment). +/// The returned pointer is aligned by \a alignment, i.e. `(uintptr_t)p % alignment == 0`. /// Returns a unique pointer if called with \a size 0. +/// /// @see [_aligned_offset_malloc](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-offset-malloc?view=vs-2017) (on Windows) void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset); void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset); @@ -535,61 +335,234 @@ void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t of /// \} -/// \defgroup heap Heap Allocation -/// -/// First-class heaps that can be destroyed in one go. + +/// \defgroup typed Typed Macros +/// Typed allocation macros. +/// For example: +/// ``` +/// int* p = mi_malloc_tp(int) +/// ``` /// /// \{ -/// Type of first-class heaps. -/// A heap can only be used for allocation in -/// the thread that created this heap! Any allocated -/// blocks can be freed or reallocated by any other thread though. -struct mi_heap_s; - -/// Type of first-class heaps. -/// A heap can only be used for (re)allocation in -/// the thread that created this heap! Any allocated -/// blocks can be freed by any other thread though. -typedef struct mi_heap_s mi_heap_t; - +/// Allocate a block of type \a tp. +/// @param tp The type of the block to allocate. +/// @returns A pointer to an object of type \a tp, or +/// \a NULL if out of memory. +/// +/// **Example:** +/// ``` +/// int* p = mi_malloc_tp(int) +/// ``` +/// +/// @see mi_malloc() +#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp))) + +/// Allocate a zero-initialized block of type \a tp. +#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp))) + +/// Allocate \a count zero-initialized blocks of type \a tp. +#define mi_calloc_tp(tp,count) ((tp*)mi_calloc(count,sizeof(tp))) + +/// Allocate \a count blocks of type \a tp. +#define mi_mallocn_tp(tp,count) ((tp*)mi_mallocn(count,sizeof(tp))) + +/// Re-allocate to \a count blocks of type \a tp. +#define mi_reallocn_tp(p,tp,count) ((tp*)mi_reallocn(p,count,sizeof(tp))) + +/// Allocate a block of type \a tp in a heap \a hp. +#define mi_heap_malloc_tp(tp,hp) ((tp*)mi_heap_malloc(hp,sizeof(tp))) + +/// Allocate a zero-initialized block of type \a tp in a heap \a hp. +#define mi_heap_zalloc_tp(tp,hp) ((tp*)mi_heap_zalloc(hp,sizeof(tp))) + +/// Allocate \a count zero-initialized blocks of type \a tp in a heap \a hp. +#define mi_heap_calloc_tp(tp,hp,count) ((tp*)mi_heap_calloc(hp,count,sizeof(tp))) + +/// Allocate \a count blocks of type \a tp in a heap \a hp. +#define mi_heap_mallocn_tp(tp,hp,count) ((tp*)mi_heap_mallocn(hp,count,sizeof(tp))) + +/// Re-allocate to \a count blocks of type \a tp in a heap \a hp. +#define mi_heap_reallocn_tp(tp,hp,p,count) ((tp*)mi_heap_reallocn(p,count,sizeof(tp))) + +/// Re-allocate to \a count zero initialized blocks of type \a tp in a heap \a hp. +#define mi_heap_recalloc_tp(tp,hp,p,count) ((tp*)mi_heap_recalloc(p,count,sizeof(tp))) + +/// \} + +/// \defgroup zeroinit Zero initialized re-allocation +/// Re-allocation of zero initialized blocks. +/// +/// __The zero-initialized re-allocations are only valid on memory that was +/// originally allocated with zero initialization too,__ +/// e.g. `mi_calloc`, `mi_zalloc`, `mi_zalloc_aligned` etc. +/// see also +/// +/// \{ + +/// Re-allocate memory to \a count elements of \a size bytes, with extra memory initialized to zero. +/// @param p Pointer to a previously allocated block (or \a NULL). +/// @param count The number of elements. +/// @param size The size of each element. +/// @returns A pointer to a re-allocated block of \a count * \a size bytes, or \a NULL +/// if out of memory or if \a count * \a size overflows. +/// +/// If there is no overflow, it behaves exactly like `mi_rezalloc(p,count*size)`. +/// @see mi_reallocn() +/// @see [recallocarray()](http://man.openbsd.org/reallocarray) (on BSD). +void* mi_recalloc(void* p, size_t count, size_t size); + +void* mi_rezalloc(void* p, size_t newsize); +void* mi_recalloc(void* p, size_t newcount, size_t size) ; + +void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment); +void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset); +void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment); +void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset); + +void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize); +void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t newcount, size_t size); + +void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment); +void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset); +void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment); +void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset); + +/// \} + + + +/// \defgroup heap Heaps +/// First-class heaps. +/// Heaps allow allocations to be grouped, and for example be +/// destroyed in one go. Heaps can also be associated with +/// a specific allocation arena (`mi_heap_new_in_arena()`). +/// +/// __v1__,__v2__: heaps are only semi-first-class and +/// __one can only use heap allocation functions from the thread that created the heap__. +/// (of course, mi_free() can always be used from any thread to free objects from any heap). +/// +/// __v3__: heaps are fully first-class and can be used to allocate efficiently from +/// from any thread. +/// In v3, the old v1/v2 heaps still exist but are now called _theaps_ (mi_theap_t()) for +/// thread-local heaps. A v3 heap creates internally such theaps on demand +/// to efficiently allocate without needing taking locks for example. +/// +/// \{ + +/// Type of first-class heaps. +/// +/// __v1__,__v2__: in mimalloc v1 and v2, +/// a heap can only be used for allocation in +/// the thread that created this heap! +struct mi_heap_s; + +/// Type of first-class heaps. +/// +/// __v1__,__v2__: in mimalloc v1 and v2, +/// a heap can only be used for allocation in +/// the thread that created this heap! +typedef struct mi_heap_s mi_heap_t; + /// Create a new heap that can be used for allocation. mi_heap_t* mi_heap_new(); /// Delete a previously allocated heap. -/// This will release resources and migrate any -/// still allocated blocks in this heap (efficiently) -/// to the default heap. +/// This will release internal resources but not free +/// still allocated blocks in this heap (and should be +/// `mi_free`'d later on as usual). /// -/// If \a heap is the default heap, the default -/// heap is set to the backing heap. +/// Note: trying to delete the main heap of a subprocess is ignored. void mi_heap_delete(mi_heap_t* heap); /// Destroy a heap, freeing all its still allocated blocks. /// Use with care as this will free all blocks still -/// allocated in the heap. However, this can be a very +/// allocated in the heap. However, this can be an /// efficient way to free all heap memory in one go. /// -/// If \a heap is the default heap, the default -/// heap is set to the backing heap. +/// Note: trying to destroy the main heap of a subprocess is ignored. void mi_heap_destroy(mi_heap_t* heap); -/// Set the default heap to use for mi_malloc() et al. +/// __v1__,__v2__: Set the default heap to use in the current thread for mi_malloc() et al. /// @param heap The new default heap. /// @returns The previous default heap. +/// @see mi_theap_set_default() for __v3__ mi_heap_t* mi_heap_set_default(mi_heap_t* heap); -/// Get the default heap that is used for mi_malloc() et al. +/// __v1__,__v2__: Get the default heap that is used for mi_malloc() et al. (for the current thread). /// @returns The current default heap. +/// @see mi_theap_get_default() for __v3__ mi_heap_t* mi_heap_get_default(); -/// Get the backing heap. +/// __v1__,__v2__: Get the backing heap. /// The _backing_ heap is the initial default heap for /// a thread and always available for allocations. /// It cannot be destroyed or deleted /// except by exiting the thread. +/// @see mi_heap_main() for __v3__ mi_heap_t* mi_heap_get_backing(); +/// @brief __v3__: set NUMA affinity for a heap. +/// @param heap the heap which should be associated with a specific numa node. +/// @param numa_node the numa node to associate to (`>=0`) +void mi_heap_set_numa_affinity(mi_heap_t* heap, int numa_node); + +/// @brief __v3__: the main heap (of the current sub-process) +/// @return a pointer to the main heap +/// Every (sub)process has a main heap that cannot be deleted +/// or destroyed until (sub)process exit. If other heaps are +/// deleted their live objects are migrated to the main heap. +mi_heap_t* mi_heap_main(void); + +/// @brief __v3__: return the heap that contains the given pointer. +/// @param p Any pointer -- not required to be previously allocated by mimalloc. +/// @return the heap that contains the given pointer, or `NULL` if +/// the pointer does not point into mimalloc allocated memory. +/// This is a constant time efficient function. +mi_heap_t* mi_heap_of(const void* p); + +/// @brief __v3__: does a heap contain a specific pointer? +/// @param heap The heap to query. +/// @param p Any pointer -- not required to be previously allocated by mimalloc. +/// This is a constant time efficient function. +bool mi_heap_contains(const mi_heap_t* heap, const void* p); + +/// @brief __v3__: is a pointer pointing into mimalloc managed memory? +/// @param p Any pointer -- not required to be previously allocated by mimalloc. +/// This is a constant time efficient function. +bool mi_any_heap_contains(const void* p); + + +/// __v1__,__v2__: Is a pointer part of our heap? +/// @param p The pointer to check. +/// @returns \a true if this is a pointer into our heap. +/// This function is relatively fast. +/// @see mi_any_heap_contains() for __v3__. +bool mi_is_in_heap_region(const void* p); + +/// __v1__,__v2__: Check if any pointer is part of the default heap of this thread. +/// @param p Any pointer -- not required to be previously allocated by us. +/// @returns \a true if \a p points to a block in default heap of this thread. +/// Note: expensive function, linear in the pages in the heap. +/// @see mi_any_heap_contains() for __v3__ +bool mi_check_owned(const void* p); + +/// __v1__,__v2__: Does a heap contain a pointer to a previously allocated block? +/// @param heap The heap. +/// @param p Pointer to a previously allocated block (in any heap)-- cannot be some +/// random pointer! +/// @returns \a true if the block pointed to by \a p is in the \a heap. +/// @see mi_heap_owned(), mi_heap_contains() for __v3__ +bool mi_heap_contains_block(mi_heap_t* heap, const void* p); + +/// __v1__,__v2__: Check safely if any pointer is part of a heap. +/// @param heap The heap. +/// @param p Any pointer -- not required to be previously allocated by us. +/// @returns \a true if \a p points to a block in \a heap. +/// Note: expensive function, linear in the pages in the heap. +/// @see mi_heap_contains_block(), mi_heap_contains() for __v3__ +bool mi_heap_check_owned(mi_heap_t* heap, const void* p); + /// Release outstanding resources in a specific heap. void mi_heap_collect(mi_heap_t* heap, bool force); @@ -599,13 +572,18 @@ void* mi_heap_malloc(mi_heap_t* heap, size_t size); /// Allocate a small object in a specific heap. /// \a size must be smaller or equal to MI_SMALL_SIZE_MAX(). -/// @see mi_malloc() +/// @see mi_malloc_small() void* mi_heap_malloc_small(mi_heap_t* heap, size_t size); /// Allocate zero-initialized in a specific heap. /// @see mi_zalloc() void* mi_heap_zalloc(mi_heap_t* heap, size_t size); +/// Allocate a small object in a specific heap. +/// \a size must be smaller or equal to MI_SMALL_SIZE_MAX(). +/// @see mi_zalloc_small() +void* mi_heap_zalloc_small(mi_heap_t* heap, size_t size); + /// Allocate \a count zero-initialized elements in a specific heap. /// @see mi_calloc() void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size); @@ -642,192 +620,653 @@ void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_ /// \} -/// \defgroup zeroinit Zero initialized re-allocation + +/// \defgroup analysis Heap Introspection +/// Walk areas and blocks of the heap at runtime. /// -/// The zero-initialized re-allocations are only valid on memory that was -/// originally allocated with zero initialization too. -/// e.g. `mi_calloc`, `mi_zalloc`, `mi_zalloc_aligned` etc. -/// see +/// \{ + + +/// An area of heap space contains blocks of a single size. +/// The bytes in freed blocks are `committed - used`. +typedef struct mi_heap_area_s { + void* blocks; ///< start of the area containing heap blocks + size_t reserved; ///< bytes reserved for this area + size_t committed; ///< current committed bytes of this area + size_t used; ///< bytes in use by allocated blocks + size_t block_size; ///< size in bytes of one block + size_t full_block_size; ///< size in bytes of a full block including padding and metadata. + int heap_tag; ///< heap tag associated with this area (see \a mi_heap_new_ex) +} mi_heap_area_t; + +/// Visitor function passed to mi_heap_visit_blocks() +/// @returns \a true if ok, \a false to stop visiting (i.e. break) +/// +/// This function is always first called for every \a area +/// with \a block as a \a NULL pointer. If \a visit_all_blocks +/// was \a true, the function is then called for every allocated +/// block in that area. +typedef bool (mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); + +/// Visit all areas and blocks in a heap. +/// @param heap The heap to visit. +/// @param visit_blocks If \a true visits all allocated blocks, otherwise +/// \a visitor is only called for every heap area. +/// @param visitor This function is called for every area in the heap +/// (with \a block as \a NULL). If \a visit_all_blocks is +/// \a true, \a visitor is also called for every allocated +/// block in every area (with `block!=NULL`). +/// return \a false from this function to stop visiting early. +/// @param arg Extra argument passed to \a visitor. +/// @returns \a true if all areas and blocks were visited. +/// +/// Note: requires the option mi_option_visit_abandoned() to be set +/// at the start of the program to visit all abandoned blocks as well. +bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg); + +/// @brief __v1__,__v2__: Visit all areas and blocks in abandoned heaps. +/// @param subproc_id The sub-process id associated with the abandoned heaps. +/// @param heap_tag Visit only abandoned memory with the specified heap tag, use -1 to visit all abandoned memory. +/// @param visit_blocks If \a true visits all allocated blocks, otherwise +/// \a visitor is only called for every heap area. +/// @param visitor This function is called for every area in the heap +/// (with \a block as \a NULL). If \a visit_all_blocks is +/// \a true, \a visitor is also called for every allocated +/// block in every area (with `block!=NULL`). +/// return \a false from this function to stop visiting early. +/// @param arg extra argument passed to the \a visitor. +/// @return \a true if all areas and blocks were visited. +/// +/// Note: requires the option mi_option_visit_abandoned() to be set +/// at the start of the program. +bool mi_abandoned_visit_blocks(mi_subproc_id_t subproc_id, int heap_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg); + +/// \} + +// ------------------------------------------------------ +// Arenas +// ------------------------------------------------------ + +/// \defgroup arenas Arenas +/// Arenas are large memory areas (usually 1GiB+) from which mimalloc allocates memory. +/// The arenas are usually allocated on-demand from the OS but can be reserved explicitly. +/// It is also possible to give previously allocated memory to mimalloc to manage. +/// Heaps can be associated with a specific arena to only allocate memory from that arena. /// /// \{ -void* mi_rezalloc(void* p, size_t newsize); -void* mi_recalloc(void* p, size_t newcount, size_t size) ; +/// Each arena has an associated identifier. +typedef int mi_arena_id_t; -void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment); -void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset); -void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment); -void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset); +/// Reserve OS memory for use by mimalloc. Reserved areas are used +/// before allocating from the OS again. By reserving a large area upfront, +/// allocation can be more efficient, and can be better managed on systems +/// without `mmap`/`VirtualAlloc` (like WASM for example). +/// @param size The size to reserve. +/// @param commit Commit the memory upfront. +/// @param allow_large Allow large OS pages (2MiB) to be used? +/// @return \a 0 if successful, and an error code otherwise (e.g. `ENOMEM`). +int mi_reserve_os_memory(size_t size, bool commit, bool allow_large); -void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize); -void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t newcount, size_t size); +/// @brief Reserve OS memory to be managed in an arena. +/// @param size Size the reserve. +/// @param commit Should the memory be initially committed? +/// @param allow_large Allow the use of large OS pages? +/// @param exclusive Is the returned arena exclusive? +/// @param arena_id The new arena identifier. +/// @return Zero on success, an error code otherwise. +int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id); -void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment); -void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset); -void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment); -void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset); +/// Reserve \a pages of huge OS pages (1GiB) evenly divided over \a numa_nodes nodes, +/// but stops after at most `timeout_msecs` seconds. +/// @param pages The number of 1GiB pages to reserve. +/// @param numa_nodes The number of nodes do evenly divide the pages over, or 0 for using the actual number of NUMA nodes. +/// @param timeout_msecs Maximum number of milli-seconds to try reserving, or 0 for no timeout. +/// @returns 0 if successful, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out. +/// +/// The reserved memory is used by mimalloc to satisfy allocations. +/// May quit before \a timeout_msecs are expired if it estimates it will take more than +/// 1.5 times \a timeout_msecs. The time limit is needed because on some operating systems +/// it can take a long time to reserve contiguous memory if the physical memory is +/// fragmented. +int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs); + +/// Reserve \a pages of huge OS pages (1GiB) at a specific \a numa_node, +/// but stops after at most `timeout_msecs` seconds. +/// @param pages The number of 1GiB pages to reserve. +/// @param numa_node The NUMA node where the memory is reserved (start at 0). Use -1 for no affinity. +/// @param timeout_msecs Maximum number of milli-seconds to try reserving, or 0 for no timeout. +/// @returns 0 if successful, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out. +/// +/// The reserved memory is used by mimalloc to satisfy allocations. +/// May quit before \a timeout_msecs are expired if it estimates it will take more than +/// 1.5 times \a timeout_msecs. The time limit is needed because on some operating systems +/// it can take a long time to reserve contiguous memory if the physical memory is +/// fragmented. +int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs); + +/// @brief Reserve huge OS pages (1GiB) into a single arena. +/// @param pages Number of 1GiB pages to reserve. +/// @param numa_node The associated NUMA node, or -1 for no NUMA preference. +/// @param timeout_msecs Max amount of milli-seconds this operation is allowed to take. (0 is infinite) +/// @param exclusive If exclusive, only a heap associated with this arena can allocate in it. +/// @param arena_id The arena identifier. +/// @return 0 if successful, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out. +int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id); + +/// @brief Return the minimal alignment required for managed OS memory. +/// @see mi_manage_os_memory(), mi_manage_os_memory_ex() +size_t mi_arena_min_alignment(void); + +/// Manage a particular memory area for use by mimalloc. +/// This is just like `mi_reserve_os_memory` except that the area should already be +/// allocated in some manner and available for use my mimalloc. +/// @param start Start of the memory area +/// @param size The size of the memory area. +/// @param is_committed Is the area already committed? +/// @param is_large Does it consist of large OS pages? Set this to \a true as well for memory +/// that should not be decommitted or protected (like rdma etc.) +/// @param is_zero Does the area consists of zero's? +/// @param numa_node Possible associated numa node or `-1`. +/// @return \a true if successful, and \a false on error. +bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node); + +/// @brief Manage externally allocated memory as a mimalloc arena. This memory will not be freed by mimalloc. +/// @param start Start address of the area. +/// @param size Size in bytes of the area. +/// @param is_committed Is the memory already committed? +/// @param is_large Does it consist of (pinned) large OS pages? +/// @param is_zero Is the memory zero-initialized? +/// @param numa_node Associated NUMA node, or -1 to have no NUMA preference. +/// @param exclusive Is the arena exclusive (where only heaps associated with the arena can allocate in it) +/// @param arena_id The new arena identifier. +/// @return `true` if successful. +bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id); + + +/// @brief Show all current arena's. +void mi_debug_show_arenas(void); + +/// @brief Return the start and size of an arena. +/// @param arena_id The arena identifier. +/// @param size Returned size in bytes of the (virtual) arena area. +/// @return base address of the arena. +void* mi_arena_area(mi_arena_id_t arena_id, size_t* size); + + + +/// @brief Create a new heap that only allocates in the specified arena. +/// @param arena_id The arena identifier. +/// @return The new heap or `NULL`. +mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t arena_id); + +/// @brief __v1__,__v2__: Create a new heap. +/// @param heap_tag The heap tag associated with this heap; heaps only reclaim memory between heaps with the same tag. +/// @param allow_destroy Is \a mi_heap_destroy allowed? Not allowing this allows the heap to reclaim memory from terminated threads. +/// @param arena_id If not 0, the heap will only allocate from the specified arena. +/// @return A new heap or `NULL` on failure. +/// +/// The \a arena_id can be used by runtimes to allocate only in a specified pre-reserved arena. +/// This is used for example for a compressed pointer heap in Koka. +/// The \a heap_tag enables heaps to keep objects of a certain type isolated to heaps with that tag. +/// This is used for example in the CPython integration. +/// @see mi_heap_new_in_arena() for __v3__ +mi_heap_t* mi_heap_new_ex(int heap_tag, bool allow_destroy, mi_arena_id_t arena_id); + +/// \} + + +/// \defgroup subproc Subprocesses +/// A sub-process contains its own arena's and heaps that are fully separate from the main (sub) process. +/// +/// A thread can belong to only one subprocess at a time. +/// This can be used to separate out logically separate parts +/// of a program (like running multiple interpreter instances in CPython). +/// +/// \{ + +/// A process can associate threads with sub-processes. +/// Sub-processes are isolated and will not reclaim or visit memory +/// from other sub-processes. +/// Each subprocess always has an associated main heap. +typedef void* mi_subproc_id_t; + +/// @brief Get the main sub-process identifier. +mi_subproc_id_t mi_subproc_main(void); + +/// @brief Get the current sub-process identifier of this thread. +mi_subproc_id_t mi_subproc_current(void); + +/// @brief Create a fresh sub-process (with no associated threads yet). +/// @return The new sub-process identifier. +mi_subproc_id_t mi_subproc_new(void); + +/// @brief __v3__: Destroy a previously created sub-process. +/// @param subproc The sub-process identifier. +/// Only destroy sub-processes if all associated threads have terminated. +void mi_subproc_destroy(mi_subproc_id_t subproc); + +/// @brief __v1__,__v2__: Delete a previously created sub-process. +/// @param subproc The sub-process identifier. +/// Only delete sub-processes if all associated threads have terminated. +/// @see mi_subproc_destroy() +void mi_subproc_delete(mi_subproc_id_t subproc); + +/// @brief Add the current thread to the given sub-process. +/// This should be called right after a thread is created +/// (and no allocation has taken place yet). +void mi_subproc_add_current_thread(mi_subproc_id_t subproc); + +/// @brief The type of a heap visitor function. +/// @return Return `true` to keep visiting. Returning `false` will +/// stop visiting further heaps. +typedef bool (mi_heap_visit_fun)(mi_heap_t* heap, void* arg); + +/// @brief Visit all heaps belonging to a subprocess. +/// @param subproc The subprocess that the heaps should belong to +/// @param visitor The visitor function is called with each heap in `subproc` +/// @param arg A user argument that is passed to each visitor function call. +/// Return `true` if all heaps are visited (and false otherwise). +bool mi_subproc_visit_heaps(mi_subproc_id_t subproc, mi_heap_visit_fun* visitor, void* arg); /// \} -/// \defgroup typed Typed Macros -/// -/// Typed allocation macros. For example: -/// ``` -/// int* p = mi_malloc_tp(int) -/// ``` -/// + +// ------------------------------------------------------ +// Extended functionality +// ------------------------------------------------------ + +/// \defgroup extended Extended Functions +/// Internal functionality. +/// +/// \{ + +/// Maximum size allowed for small allocations in +/// #mi_malloc_small, #mi_zalloc_small, #mi_heap_malloc_small, #mi_heap_zalloc_small, +/// #mi_theap_malloc_small, and #mi_theap_zalloc_small (usually `128*sizeof(void*)` (= 1KB on 64-bit systems)) +#define MI_SMALL_SIZE_MAX (128*sizeof(void*)) + +/// @brief Return the mimalloc version. +/// @return The version. For v1,v2 the version is 100*major + 10*minor + patch (for example 227 for v2.2.7). +/// For __v3__, it is 1000*major + 100*minor + path (for example, 3207 for v3.2.7). +int mi_version(void); + +/// Eagerly free memory. +/// @param force If \a true, aggressively return memory to the OS (can be expensive!) +/// +/// Regular code should not have to call this function. It can be beneficial +/// in very narrow circumstances; in particular, when a long running thread +/// allocates a lot of blocks that are freed by other threads it may improve +/// resource usage by calling this every once in a while. +void mi_collect(bool force); + +/// __v3__: Communicate that a thread is in a threadpool. +/// This is done automatically for threads in the Windows threadpool, +/// but if using a custom threadpool it is good to call this on worker threads. +/// Internally, mimalloc uses different locality heuristics for worker threads +/// to try to reduce non-local accesss. +void mi_thread_set_in_threadpool(void); + +/// Is the C runtime \a malloc API redirected? +/// @returns \a true if all malloc API calls are redirected to mimalloc. +/// +/// Currently only used on Windows. +bool mi_is_redirected(); + +/// Initialize mimalloc on a thread. +/// Should not be used as on most systems (pthreads, windows) this is done +/// automatically. +void mi_thread_init(void); + +/// Uninitialize mimalloc on a thread. +/// Should not be used as on most systems (pthreads, windows) this is done +/// automatically. Ensures that any memory that is not freed yet (but will +/// be freed by other threads in the future) is properly handled. +void mi_thread_done(void); + +/// Type of deferred free functions. +/// @param force If \a true all outstanding items should be freed. +/// @param heartbeat A monotonically increasing count. +/// @param arg Argument that was passed at registration to hold extra state. +/// +/// @see mi_register_deferred_free +typedef void (mi_deferred_free_fun)(bool force, unsigned long long heartbeat, void* arg); + +/// Register a deferred free function. +/// @param deferred_free Address of a deferred free-ing function or \a NULL to unregister. +/// @param arg Argument that will be passed on to the deferred free function. +/// +/// Some runtime systems use deferred free-ing, for example when using +/// reference counting to limit the worst case free time. +/// Such systems can register (re-entrant) deferred free function +/// to free more memory on demand. When the \a force parameter is +/// \a true all possible memory should be freed. +/// The per-thread \a heartbeat parameter is monotonically increasing +/// and guaranteed to be deterministic if the program allocates +/// deterministically. The \a deferred_free function is guaranteed +/// to be called deterministically after some number of allocations +/// (regardless of freeing or available free memory). +/// At most one \a deferred_free function can be active. +void mi_register_deferred_free(mi_deferred_free_fun* deferred_free, void* arg); + +/// Type of output functions. +/// @param msg Message to output. +/// @param arg Argument that was passed at registration to hold extra state. +/// +/// @see mi_register_output() +typedef void (mi_output_fun)(const char* msg, void* arg); + +/// Register an output function. +/// @param out The output function, use `NULL` to output to stderr. +/// @param arg Argument that will be passed on to the output function. +/// +/// The `out` function is called to output any information from mimalloc, +/// like verbose or warning messages. +void mi_register_output(mi_output_fun* out, void* arg); + +/// Type of error callback functions. +/// @param err Error code (see mi_register_error() for a complete list). +/// @param arg Argument that was passed at registration to hold extra state. +/// +/// @see mi_register_error() +typedef void (mi_error_fun)(int err, void* arg); + +/// Register an error callback function. +/// @param errfun The error function that is called on an error (use \a NULL for default) +/// @param arg Extra argument that will be passed on to the error function. +/// +/// The \a errfun function is called on an error in mimalloc after emitting +/// an error message (through the output function). It as always legal to just +/// return from the \a errfun function in which case allocation functions generally +/// return \a NULL or ignore the condition. The default function only calls abort() +/// when compiled in secure mode with an \a EFAULT error. The possible error +/// codes are: +/// * \a EAGAIN: Double free was detected (only in debug and secure mode). +/// * \a EFAULT: Corrupted free list or meta-data was detected (only in debug and secure mode). +/// * \a ENOMEM: Not enough memory available to satisfy the request. +/// * \a EOVERFLOW: Too large a request, for example in mi_calloc(), the \a count and \a size parameters are too large. +/// * \a EINVAL: Trying to free or re-allocate an invalid pointer. +void mi_register_error(mi_error_fun* errfun, void* arg); + +/// Allocate a small object. +/// @param size The size in bytes, can be at most #MI_SMALL_SIZE_MAX. +/// @returns a pointer to newly allocated memory of at least \a size +/// bytes, or \a NULL if out of memory. +/// This function is meant for use in run-time systems for best +/// performance and does not check if \a size was indeed small -- use +/// with care! +void* mi_malloc_small(size_t size); + +/// Allocate a zero initialized small object. +/// @param size The size in bytes, can be at most #MI_SMALL_SIZE_MAX. +/// @returns a pointer to newly allocated zero-initialized memory of at +/// least \a size bytes, or \a NULL if out of memory. +/// This function is meant for use in run-time systems for best +/// performance and does not check if \a size was indeed small -- use +/// with care! +void* mi_zalloc_small(size_t size); + +/// __v3__: Can be used to free an object that was allocated with #mi_malloc_small et al. +/// @param p Pointer that was returned from #mi_malloc_small et al. +/// @details +/// This function is meant for use in run-time systems for best +/// performance and does not check if the pointer was _indeed_ allocated +/// with #mi_malloc_small (et al) -- use with care! +/// This function has a small perf benefit but only if mimalloc was built with `MI_PAGE_META_ALIGNED_FREE_SMALL=1` +/// @see mi_malloc_small() +/// @see mi_zalloc_small() +/// @see mi_heap_malloc_small() +/// @see mi_heap_zalloc_small() +/// @see mi_theap_malloc_small() +/// @see mi_theap_zalloc_small() +void mi_free_small(void* p); + +/// \} + + + + + +// ------------------------------------------------------ +// Statistics +// ------------------------------------------------------ + +/// \defgroup stats Statistics +/// Print out allocation statistics. /// \{ -/// Allocate a block of type \a tp. -/// @param tp The type of the block to allocate. -/// @returns A pointer to an object of type \a tp, or -/// \a NULL if out of memory. -/// -/// **Example:** +/// Statistics version. Increased on each backward incompatible change. +#define MI_STAT_VERSION 4 + +/// Helper to declare a properly initialized mi_stats_t() local variable as \a name +/// where the \a size and \a version fields are properly initialized. +/// For example: /// ``` -/// int* p = mi_malloc_tp(int) +/// mi_stats_t_decl(stats); +/// if (mi_stats_get(&stats)) { +/// ... +/// } /// ``` -/// -/// @see mi_malloc() -#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp))) +#define mi_stats_t_decl(name) -/// Allocate a zero-initialized block of type \a tp. -#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp))) +/// Statistics. See [include/mimalloc-stats.h](https://github.com/microsoft/mimalloc/blob/main/include/mimalloc-stats.h) for the full definition. +struct mi_stats_s { + /// initialize with `sizeof(mi_stats_t)` (so linking dynamically with a separately compiled mimalloc is safe) + size_t size; + /// initialize with `MI_STAT_VERSION (so linking dynamically with a separately compiled mimalloc is safe) + size_t version; +}; -/// Allocate \a count zero-initialized blocks of type \a tp. -#define mi_calloc_tp(tp,count) ((tp*)mi_calloc(count,sizeof(tp))) +/// Statistics. See [include/mimalloc-stats.h](https://github.com/microsoft/mimalloc/blob/main/include/mimalloc-stats.h) for the full definition. +typedef struct mi_stats_s mi_stats_t; -/// Allocate \a count blocks of type \a tp. -#define mi_mallocn_tp(tp,count) ((tp*)mi_mallocn(count,sizeof(tp))) -/// Re-allocate to \a count blocks of type \a tp. -#define mi_reallocn_tp(p,tp,count) ((tp*)mi_reallocn(p,count,sizeof(tp))) +/// @brief Print out all process info. +/// @see mi_process_info() +void mi_process_info_print(void); -/// Allocate a block of type \a tp in a heap \a hp. -#define mi_heap_malloc_tp(hp,tp) ((tp*)mi_heap_malloc(hp,sizeof(tp))) +/// @brief Print out all process info. +/// @param out An output function or \a NULL for the default. +/// @param arg Optional argument passed to \a out (if not \a NULL) +/// @see mi_process_print() +void mi_process_info_print_out(mi_output_fun* out, void* arg); -/// Allocate a zero-initialized block of type \a tp in a heap \a hp. -#define mi_heap_zalloc_tp(hp,tp) ((tp*)mi_heap_zalloc(hp,sizeof(tp))) +/// Return process information (time and memory usage). +/// @param elapsed_msecs Optional. Elapsed wall-clock time of the process in milli-seconds. +/// @param user_msecs Optional. User time in milli-seconds (as the sum over all threads). +/// @param system_msecs Optional. System time in milli-seconds. +/// @param current_rss Optional. Current working set size (touched pages). +/// @param peak_rss Optional. Peak working set size (touched pages). +/// @param current_commit Optional. Current committed memory (backed by the page file). +/// @param peak_commit Optional. Peak committed memory (backed by the page file). +/// @param page_faults Optional. Count of hard page faults. +/// +/// The \a current_rss is precise on Windows and MacOSX; other systems estimate +/// this using \a current_commit. The \a commit is precise on Windows but estimated +/// on other systems as the amount of read/write accessible memory reserved by mimalloc. +void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults); -/// Allocate \a count zero-initialized blocks of type \a tp in a heap \a hp. -#define mi_heap_calloc_tp(hp,tp,count) ((tp*)mi_heap_calloc(hp,count,sizeof(tp))) -/// Allocate \a count blocks of type \a tp in a heap \a hp. -#define mi_heap_mallocn_tp(hp,tp,count) ((tp*)mi_heap_mallocn(hp,count,sizeof(tp))) +/// Deprecated +/// @param out Ignored, outputs to the registered output function or stderr by default. +/// +/// Most detailed when using a debug build. +void mi_stats_print(void* out); -/// Re-allocate to \a count blocks of type \a tp in a heap \a hp. -#define mi_heap_reallocn_tp(hp,p,tp,count) ((tp*)mi_heap_reallocn(p,count,sizeof(tp))) +/// Print statistics of the current subprocess aggregated over all its heaps. +/// @param out An output function or \a NULL for the default. +/// @param arg Optional argument passed to \a out (if not \a NULL) +/// +/// Most detailed when using a debug build. +void mi_stats_print_out(mi_output_fun* out, void* arg); -/// Re-allocate to \a count zero initialized blocks of type \a tp in a heap \a hp. -#define mi_heap_recalloc_tp(hp,p,tp,count) ((tp*)mi_heap_recalloc(p,count,sizeof(tp))) +/// @brief Get the statistics for the current subprocess aggregated over all its heaps. +/// @param stats Pointer to a mi_stats_t() structure (declared as `mi_stats_t_decl(name)`). +/// @return \a true if \a stats is not \a NULL, and if \a stats->size +/// and \a stats->version match the `sizeof(mi_stats_t)` and `MI_STAT_VERSION` +/// with the linked mimalloc version. +bool mi_stats_get(mi_stats_t* stats); -/// \} +/// @brief Get the statistics for the current subprocess aggregated over all its heaps as JSON. +/// @param buf_size Byte size of the buffer \a buf (or 0 if \a buf is \a NULL) +/// @param buf The buffer. Pass \a NULL to allocate a fresh buffer. +/// @return Pointer to the buffer or \a NULL on failure. +/// Use mi_free() to free the buffer if the \a buf parameter was \a NULL. +char* mi_stats_get_json(size_t buf_size, char* buf); -/// \defgroup analysis Heap Introspection -/// -/// Inspect the heap at runtime. -/// -/// \{ +/// @brief __v3__: Return the block size for the given bin. +size_t mi_stats_get_bin_size(size_t bin); -/// Does a heap contain a pointer to a previously allocated block? +/// @brief __v3__: Return statistics for a given heap. /// @param heap The heap. -/// @param p Pointer to a previously allocated block (in any heap)-- cannot be some -/// random pointer! -/// @returns \a true if the block pointed to by \a p is in the \a heap. -/// @see mi_heap_check_owned() -bool mi_heap_contains_block(mi_heap_t* heap, const void* p); +/// @param stats Pointer to a mi_stats_t() structure (declared as `mi_stats_t_decl(name)`). +/// @return \a true if \a stats is not \a NULL, and if \a stats->size +/// and \a stats->version match the `sizeof(mi_stats_t)` and `MI_STAT_VERSION` +/// with the linked mimalloc version. +bool mi_heap_stats_get(mi_heap_t* heap, mi_stats_t* stats); -/// Check safely if any pointer is part of a heap. +/// @brief __v3__: Get the statistics for a heap as JSON. /// @param heap The heap. -/// @param p Any pointer -- not required to be previously allocated by us. -/// @returns \a true if \a p points to a block in \a heap. -/// -/// Note: expensive function, linear in the pages in the heap. -/// @see mi_heap_contains_block() -/// @see mi_heap_get_default() -bool mi_heap_check_owned(mi_heap_t* heap, const void* p); +/// @param buf_size Byte size of the buffer \a buf (or 0 if \a buf is \a NULL). +/// @param buf The buffer. Pass \a NULL to allocate a fresh buffer. +/// @return Pointer to the buffer or \a NULL on failure. +/// Use mi_free() to free the buffer if the \a buf parameter was \a NULL. +char* mi_heap_stats_get_json(mi_heap_t* heap, size_t buf_size, char* buf); // use mi_free to free the result if the input buf == NULL -/// Check safely if any pointer is part of the default heap of this thread. -/// @param p Any pointer -- not required to be previously allocated by us. -/// @returns \a true if \a p points to a block in default heap of this thread. -/// -/// Note: expensive function, linear in the pages in the heap. -/// @see mi_heap_contains_block() -/// @see mi_heap_get_default() -bool mi_check_owned(const void* p); +/// @brief __v3__: Show the heap statistics as JSON. +/// @param heap The heap. +/// @param out An output function or \a NULL for the default. +/// @param arg Optional argument passed to \a out (if not \a NULL) +void mi_heap_stats_print_out(mi_heap_t* heap, mi_output_fun* out, void* arg); -/// An area of heap space contains blocks of a single size. -/// The bytes in freed blocks are `committed - used`. -typedef struct mi_heap_area_s { - void* blocks; ///< start of the area containing heap blocks - size_t reserved; ///< bytes reserved for this area - size_t committed; ///< current committed bytes of this area - size_t used; ///< bytes in use by allocated blocks - size_t block_size; ///< size in bytes of one block -} mi_heap_area_t; +/// @brief __v3__: xplicitly merge the statistics of the current heap with the subprocess. +/// @param heap The heap. +/// After this call, the heap statistics are reset. +void mi_heap_stats_merge_to_subproc(mi_heap_t* heap); + +/// @brief __v3__: Get statistics for a given subprocess aggregated over all its heaps. +/// @param subproc_id The subprocess +/// @param stats Pointer to a mi_stats_t() structure (declared as `mi_stats_t_decl(name)`). +/// @return \a true if \a stats is not \a NULL, and if \a stats->size +/// and \a stats->version match the `sizeof(mi_stats_t)` and `MI_STAT_VERSION` +/// with the linked mimalloc version. +bool mi_subproc_stats_get(mi_subproc_id_t subproc_id, mi_stats_t* stats); + +/// @brief __v3__: Show the subproc statistics aggregated over all its heaps as JSON. +/// @param subproc_id The subprocess. +/// @param buf_size Byte size of the buffer \a buf (or 0 if \a buf is \a NULL). +/// @param buf The buffer. Pass \a NULL to allocate a fresh buffer. +/// @return Pointer to the buffer or \a NULL on failure. +/// Use mi_free() to free the buffer if the \a buf parameter was \a NULL. +char* mi_subproc_stats_get_json(mi_subproc_id_t subproc_id, size_t buf_size, char* buf); // use mi_free to free the result if the input buf == NULL + +/// @brief __v3__: Print the subproc statistics aggregated over all its heaps. +/// @param subproc_id The subprocess. +/// @param out An output function or \a NULL for the default. +/// @param arg Optional argument passed to \a out (if not \a NULL) +void mi_subproc_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg); -/// Visitor function passed to mi_heap_visit_blocks() -/// @returns \a true if ok, \a false to stop visiting (i.e. break) -/// -/// This function is always first called for every \a area -/// with \a block as a \a NULL pointer. If \a visit_all_blocks -/// was \a true, the function is then called for every allocated -/// block in that area. -typedef bool (mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); -/// Visit all areas and blocks in a heap. -/// @param heap The heap to visit. -/// @param visit_all_blocks If \a true visits all allocated blocks, otherwise -/// \a visitor is only called for every heap area. -/// @param visitor This function is called for every area in the heap -/// (with \a block as \a NULL). If \a visit_all_blocks is -/// \a true, \a visitor is also called for every allocated -/// block in every area (with `block!=NULL`). -/// return \a false from this function to stop visiting early. -/// @param arg Extra argument passed to \a visitor. -/// @returns \a true if all areas and blocks were visited. -bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg); +/// @brief __v3__: Print statistics for a given subprocess with each heap separately printed. +/// @param subproc_id The subprocess +/// @param out An output function or \a NULL for the default. +/// @param arg Optional argument passed to \a out (if not \a NULL) +void mi_subproc_heap_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg); + +/// @brief __v3__: Show the given statistics as JSON. +/// @param stats The statistics. +/// @param buf_size Byte size of the buffer \a buf (or 0 if \a buf is \a NULL). +/// @param buf The buffer. Pass \a NULL to allocate a fresh buffer. +/// @return Pointer to the buffer or \a NULL on failure. +/// Use mi_free() to free the buffer if the \a buf parameter was \a NULL. +char* mi_stats_as_json( mi_stats_t* stats, size_t buf_size, char* buf); + +/// __v1__,__v2__: Reset statistics. +void mi_stats_reset(void); + +/// __v1__,__v2__: Merge thread local statistics with the main statistics and reset. +void mi_stats_merge(void); /// \} +// ------------------------------------------------------ +// Runtime Options +// ------------------------------------------------------ + /// \defgroup options Runtime Options -/// -/// Set runtime behavior. +/// Set runtime parameters. /// /// \{ +/// @brief Print out all runtime parameters for mimalloc. +/// Also printed with `MIMALLOC_VERBOSE=1` at startup. +void mi_options_print(void); + +/// @brief Print out all process info. +/// @param out An output function or \a NULL for the default. +/// @param arg Optional argument passed to \a out (if not \a NULL) +/// Also printed with `MIMALLOC_VERBOSE=1` at startup. +/// @see mi_options_print() +void mi_options_print_out(mi_output_fun* out, void* arg); + + /// Runtime options. typedef enum mi_option_e { // stable options - mi_option_show_errors, ///< Print error messages to `stderr`. - mi_option_show_stats, ///< Print statistics to `stderr` when the program is done. - mi_option_verbose, ///< Print verbose messages to `stderr`. - - // the following options are experimental - mi_option_eager_commit, ///< Eagerly commit segments (4MiB) (enabled by default). - mi_option_large_os_pages, ///< Use large OS pages (2MiB in size) if possible - mi_option_reserve_huge_os_pages, ///< The number of huge OS pages (1GiB in size) to reserve at the start of the program. - mi_option_reserve_huge_os_pages_at, ///< Reserve huge OS pages at node N. - mi_option_reserve_os_memory, ///< Reserve specified amount of OS memory at startup, e.g. "1g" or "512m". - mi_option_segment_cache, ///< The number of segments per thread to keep cached (0). - mi_option_page_reset, ///< Reset page memory after \a mi_option_reset_delay milliseconds when it becomes free. - mi_option_abandoned_page_reset, //< Reset free page memory when a thread terminates. - mi_option_use_numa_nodes, ///< Pretend there are at most N NUMA nodes; Use 0 to use the actual detected NUMA nodes at runtime. - mi_option_eager_commit_delay, ///< the first N segments per thread are not eagerly committed (=1). - mi_option_os_tag, ///< OS tag to assign to mimalloc'd memory - mi_option_limit_os_alloc, ///< If set to 1, do not use OS memory for allocation (but only pre-reserved arenas) - - // v1.x specific options - mi_option_eager_region_commit, ///< Eagerly commit large (256MiB) memory regions (enabled by default, except on Windows) - mi_option_segment_reset, ///< Experimental - mi_option_reset_delay, ///< Delay in milli-seconds before resetting a page (100ms by default) - mi_option_purge_decommits, ///< Experimental - - // v2.x specific options - mi_option_allow_purge, ///< Enable decommitting memory (=on) - mi_option_purge_delay, ///< Decommit page memory after N milli-seconds delay (25ms). - mi_option_segment_purge_delay, ///< Decommit large segment memory after N milli-seconds delay (500ms). - + mi_option_show_errors, ///< Print error messages. + mi_option_show_stats, ///< Print statistics on termination. + mi_option_verbose, ///< Print verbose messages. + mi_option_max_errors, ///< issue at most N error messages + mi_option_max_warnings, ///< issue at most N warning messages + + // advanced options + mi_option_reserve_huge_os_pages, ///< reserve N huge OS pages (1GiB pages) at startup + mi_option_reserve_huge_os_pages_at, ///< Reserve N huge OS pages at a specific NUMA node N. + mi_option_reserve_os_memory, ///< reserve specified amount of OS memory in an arena at startup (internally, this value is in KiB; use mi_option_get_size()) + mi_option_allow_large_os_pages, ///< allow large (2 or 4 MiB) OS pages, implies eager commit. + mi_option_purge_decommits, ///< should a memory purge decommit? (=1). Set to 0 to use memory reset on a purge (instead of decommit) + mi_option_arena_reserve, ///< initial memory size for arena reservation (= 1 GiB on 64-bit) (internally, this value is in KiB; use mi_option_get_size()) + mi_option_os_tag, ///< tag used for OS logging (macOS only for now) (=100) + mi_option_retry_on_oom, ///< retry on out-of-memory for N milli seconds (=400), set to 0 to disable retries. (only on windows) + mi_option_generic_collect, ///< collect heaps every N (=10000) generic allocation calls + mi_option_allow_thp, ///< allow transparent huge pages? (=1) (on Android =0 by default). Set to 0 to disable THP for the process. + + // guard pages + mi_option_guarded_min, ///< only used when building with MI_GUARDED: minimal rounded object size for guarded objects (=0) + mi_option_guarded_max, ///< only used when building with MI_GUARDED: maximal rounded object size for guarded objects (=0) + mi_option_guarded_precise, ///< disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0) + mi_option_guarded_sample_rate, ///< 1 out of N allocations in the min/max range will be guarded (=1000) + mi_option_guarded_sample_seed, ///< can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0) + + // experimental options + mi_option_eager_commit, ///< __v1__,__v2__: eager commit segments? (after `eager_commit_delay` segments) (enabled by default). + mi_option_eager_commit_delay, ///< __v2__: the first N segments per thread are not eagerly committed (but per page in the segment on demand) + mi_option_arena_eager_commit, ///< eager commit arenas? Use 2 to enable just on overcommit systems (=2) + mi_option_abandoned_page_purge, ///< __v1__,__v2__: immediately purge delayed purges on thread termination + mi_option_purge_delay, ///< memory purging is delayed by N milli seconds; use 0 for immediate purging or -1 for no purging at all. (=10) + mi_option_use_numa_nodes, ///< 0 = use all available numa nodes, otherwise use at most N nodes. + mi_option_disallow_os_alloc, ///< 1 = do not use OS memory for allocation (but only programmatically reserved arenas) + mi_option_max_segment_reclaim, ///< __v2__: max. percentage of the abandoned segments can be reclaimed per try (=10%) + mi_option_destroy_on_exit, ///< if set, release all memory on exit; sometimes used for dynamic unloading but can be unsafe + mi_option_arena_purge_mult, ///< multiplier for `purge_delay` for the purging delay for arenas (=10) + mi_option_abandoned_reclaim_on_free, ///< __v1__,__v2__: allow to reclaim an abandoned segment on a free (=1) + mi_option_purge_extend_delay, ///< __v1__,__v2__: extend purge delay on each subsequent delay (=1) + mi_option_disallow_arena_alloc, ///< 1 = do not use arena's for allocation (except if using specific arena id's) + mi_option_visit_abandoned, ///< allow visiting heap blocks from abandoned threads (=0) + mi_option_target_segments_per_thread, ///< __v1__,__v2__: experimental (=0) + + // v3 options + mi_option_page_reclaim_on_free, ///< __v3__: reclaim abandoned pages on a free (=0). -1 disallowr always, 0 allows if the page originated from the current theap, 1 allow always + mi_option_page_full_retain, ///< __v3__: retain N full (small) pages per size class (=2). Use -1 for infinite (as in __v1__,__v2__). + mi_option_page_max_candidates, ///< __v3__: max candidate pages to consider for allocation (=4) + mi_option_max_vabits, ///< __v3__: max user space virtual address bits to consider (=48) + mi_option_pagemap_commit, ///< __v3__: commit the full pagemap (to always catch invalid pointer uses) (=0) + mi_option_page_commit_on_demand, ///< __v3__: commit page memory on-demand (=0) + mi_option_page_max_reclaim, ///< __v3__: don't reclaim pages of the same originating theap if we already own N pages (in that size class) (=-1 (unlimited)) + mi_option_page_cross_thread_max_reclaim, ///< __v3__: don't reclaim pages across threads if we already own N pages (in that size class) (=16) + mi_option_minimal_purge_size, ///< __v3__: set minimal purge size (in KiB) (=0). By default set to either 64 or 2048 if THP is enabled. (internal value is in KiB so use mi_option_get_size()) + mi_option_arena_max_object_size, ///< __v3__: set maximal object size that can be allocated in an arena (in KiB) (=2GiB on 64-bit). + _mi_option_last } mi_option_t; @@ -838,35 +1277,106 @@ void mi_option_disable(mi_option_t option); void mi_option_set_enabled(mi_option_t option, bool enable); void mi_option_set_enabled_default(mi_option_t option, bool enable); -long mi_option_get(mi_option_t option); +long mi_option_get(mi_option_t option); +long mi_option_get_clamp(mi_option_t option, long min, long max); +size_t mi_option_get_size(mi_option_t option); + void mi_option_set(mi_option_t option, long value); void mi_option_set_default(mi_option_t option, long value); +/// \} + +/// \defgroup theap Thread-local heaps +/// __v3__: Thread local heaps. +/// +/// The use of thread-local heaps is discouraged and only recommended +/// for special cases like runtime systems that keep their own thread-local +/// state. +/// The "theaps" are thread-local heaps that __v3__ uses internally +/// to implement efficient first-class heaps. +/// +/// __v1__,__v2__: the `mi_heap_t` heaps in v1/v2 are exactly these `mi_theap_t` +/// theaps in v3. +/// \{ + +/// Type of thread-local heaps. +struct mi_theap_s; + +/// Type of thread-local heaps. +typedef struct mi_theap_s mi_theap_t; + +/// @brief Return the thread-local theap for the given heap. +/// @param heap The owning heap. +/// @return The theap that serves thread-local allocations for the given `heap`. +/// This can be used by runtime systems to request the thread-local theap +/// once and then use it in its allocation functions (like mi_theap_malloc() or mi_theap_malloc_small() ) to avoid re-looking up +/// the theap at each allocation call. +mi_theap_t* mi_heap_theap(mi_heap_t* heap); + +/// Release outstanding resources in a specific theap. +void mi_theap_collect(mi_theap_t* theap, bool force); + +/// Set the default thread-local theap to use in the current thread for mi_malloc() et al. +/// @param theap The new default theap. +/// @returns The previous default theap. +/// By default it will point to the theap belonging to the main heap (mi_heap_main()). +mi_theap_t* mi_theap_set_default(mi_theap_t* theap); + +/// Get the default theap that is used for mi_malloc() et al. for the current thread. +/// @returns The current default theap. +mi_theap_t* mi_theap_get_default(); + +/// Allocate in a specific theap. +/// @see mi_malloc() +void* mi_theap_malloc(mi_theap_t* theap, size_t size); + +/// Allocate a small object in a specific theap. +/// \a size must be smaller or equal to MI_SMALL_SIZE_MAX(). +/// @see mi_malloc_small() +void* mi_theap_malloc_small(mi_theap_t* theap, size_t size); + +/// Allocate zero-initialized in a specific heap. +/// @see mi_zalloc() +void* mi_theap_zalloc(mi_theap_t* theap, size_t size); + +/// Allocate a small object in a specific theap. +/// \a size must be smaller or equal to MI_SMALL_SIZE_MAX(). +/// @see mi_zalloc_small() +void* mi_theap_zalloc_small(mi_theap_t* theap, size_t size); + +void* mi_theap_calloc(mi_theap_t* theap, size_t count, size_t size); +void* mi_theap_malloc_aligned(mi_theap_t* theap, size_t size, size_t alignment); +void* mi_theap_realloc(mi_theap_t* theap, void* p, size_t newsize); /// \} + /// \defgroup posix Posix -/// /// `mi_` prefixed implementations of various Posix, Unix, and C++ allocation functions. /// Defined for convenience as all redirect to the regular mimalloc API. /// /// \{ -void* mi_recalloc(void* p, size_t count, size_t size); -size_t mi_malloc_size(const void* p); -size_t mi_malloc_usable_size(const void *p); - /// Just as `free` but also checks if the pointer `p` belongs to our heap. void mi_cfree(void* p); +void* mi__expand(void* p, size_t newsize); + +size_t mi_malloc_size(const void* p); +size_t mi_malloc_good_size(size_t size); +size_t mi_malloc_usable_size(const void *p); int mi_posix_memalign(void** p, size_t alignment, size_t size); int mi__posix_memalign(void** p, size_t alignment, size_t size); void* mi_memalign(size_t alignment, size_t size); void* mi_valloc(size_t size); - void* mi_pvalloc(size_t size); void* mi_aligned_alloc(size_t alignment, size_t size); +unsigned short* mi_wcsdup(const unsigned short* s); +unsigned char* mi_mbsdup(const unsigned char* s); +int mi_dupenv_s(char** buf, size_t* size, const char* name); +int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name); + /// Correspond s to [reallocarray](https://www.freebsd.org/cgi/man.cgi?query=reallocarray&sektion=3&manpath=freebsd-release-ports) /// in FreeBSD. void* mi_reallocarray(void* p, size_t count, size_t size); @@ -874,6 +1384,9 @@ void* mi_reallocarray(void* p, size_t count, size_t size); /// Corresponds to [reallocarr](https://man.netbsd.org/reallocarr.3) in NetBSD. int mi_reallocarr(void* p, size_t count, size_t size); +void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment); +void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset); + void mi_free_size(void* p, size_t size); void mi_free_size_aligned(void* p, size_t size, size_t alignment); void mi_free_aligned(void* p, size_t alignment); @@ -881,9 +1394,9 @@ void mi_free_aligned(void* p, size_t alignment); /// \} /// \defgroup cpp C++ wrappers +/// `mi_` prefixed implementations of various C++ allocation functions. /// -/// `mi_` prefixed implementations of various allocation functions -/// that use C++ semantics on out-of-memory, generally calling +/// These use C++ semantics on out-of-memory, generally calling /// `std::get_new_handler` and raising a `std::bad_alloc` exception on failure. /// /// Note: use the `mimalloc-new-delete.h` header to override the \a new @@ -934,14 +1447,14 @@ git clone https://github.com/microsoft/mimalloc ## Windows -Open `ide/vs2019/mimalloc.sln` in Visual Studio 2019 and build (or `ide/vs2017/mimalloc.sln`). -The `mimalloc` project builds a static library (in `out/msvc-x64`), while the -`mimalloc-override` project builds a DLL for overriding malloc +Open `ide/vs2022/mimalloc.sln` in Visual Studio 2022 and build. +The `mimalloc-lib` project builds a static library (in `out/msvc-x64`), while the +`mimalloc-override-dll` project builds a DLL for overriding malloc in the entire program. -## macOS, Linux, BSD, etc. +## Linux, macOS, BSD, etc. -We use [`cmake`](https://cmake.org)1 as the build system: +We use [`cmake`](https://cmake.org) as the build system: ``` > mkdir -p out/release @@ -964,27 +1477,59 @@ maintains detailed statistics as: > cmake -DCMAKE_BUILD_TYPE=Debug ../.. > make ``` + This will name the shared library as `libmimalloc-debug.so`. -Finally, you can build a _secure_ version that uses guard pages, encrypted -free lists, etc, as: +Finally, you can build a _secure_ version that uses guard pages, encrypted free lists, etc., as: + ``` > mkdir -p out/secure > cd out/secure > cmake -DMI_SECURE=ON ../.. > make ``` + This will name the shared library as `libmimalloc-secure.so`. -Use `ccmake`2 instead of `cmake` -to see and customize all the available build options. +Use `cmake ../.. -LH` to see all the available build options. + +The examples use the default compiler. If you like to use another, use: + +``` +> CC=clang CXX=clang++ cmake ../.. +``` + +## Cmake with Visual Studio + +You can also use cmake on Windows. Open a Visual Studio 2022 development prompt +and invoke `cmake` with the right [generator](https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2017%202022.html) +and architecture, like: + +``` +> cmake ..\.. -G "Visual Studio 17 2022" -A x64 -DMI_OVERRIDE=ON +``` + +The cmake build type is specified when actually building, for example: + +``` +> cmake --build . --config=Release +``` -Notes: -1. Install CMake: `sudo apt-get install cmake` -2. Install CCMake: `sudo apt-get install cmake-curses-gui` +You can also install the [LLVM toolset](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-170#install-1) +on Windows to build with the `clang-cl` compiler directly: + +``` +> cmake ../.. -G "Visual Studio 17 2022" -T ClangCl +``` + + +## Single Source + +You can also directly build the single `src/static.c` file as part of your project without +needing `cmake` at all. Make sure to also add the mimalloc `include` directory to the include path. */ -/*! \page using Using the library +/*! \page using Using the Library ### Build @@ -998,7 +1543,7 @@ mimalloc uses only safe OS calls (`mmap` and `VirtualAlloc`) and can co-exist with other allocators linked to the same program. If you use `cmake`, you can simply use: ``` -find_package(mimalloc 1.0 REQUIRED) +find_package(mimalloc 2.1 REQUIRED) ``` in your `CMakeLists.txt` to find a locally installed mimalloc. Then use either: ``` @@ -1012,7 +1557,7 @@ to link with the static library. See `test\CMakeLists.txt` for an example. ### C++ For best performance in C++ programs, it is also recommended to override the -global `new` and `delete` operators. For convience, mimalloc provides +global `new` and `delete` operators. For convenience, mimalloc provides [`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) which does this for you -- just include it in a single(!) source file in your project. In C++, mimalloc also provides the `mi_stl_allocator` struct which implements the `std::allocator` @@ -1031,33 +1576,52 @@ and statistics (`MIMALLOC_SHOW_STATS=1`) (in the debug version): 175451865205073170563711388363 = 374456281610909315237213 * 468551 -heap stats: peak total freed unit -normal 2: 16.4 kb 17.5 mb 17.5 mb 16 b ok -normal 3: 16.3 kb 15.2 mb 15.2 mb 24 b ok -normal 4: 64 b 4.6 kb 4.6 kb 32 b ok -normal 5: 80 b 118.4 kb 118.4 kb 40 b ok -normal 6: 48 b 48 b 48 b 48 b ok -normal 17: 960 b 960 b 960 b 320 b ok - -heap stats: peak total freed unit - normal: 33.9 kb 32.8 mb 32.8 mb 1 b ok - huge: 0 b 0 b 0 b 1 b ok - total: 33.9 kb 32.8 mb 32.8 mb 1 b ok -malloc requested: 32.8 mb - - committed: 58.2 kb 58.2 kb 58.2 kb 1 b ok - reserved: 2.0 mb 2.0 mb 2.0 mb 1 b ok - reset: 0 b 0 b 0 b 1 b ok - segments: 1 1 1 --abandoned: 0 - pages: 6 6 6 --abandoned: 0 - mmaps: 3 - mmap fast: 0 - mmap slow: 1 - threads: 0 - elapsed: 2.022s - process: user: 1.781s, system: 0.016s, faults: 756, reclaims: 0, rss: 2.7 mb +subproc 0 + blocks peak total current block total# + bin S 4: 75.3 KiB 55.2 MiB 0 32 B 1.8 M ok + bin S 6: 31.0 KiB 180.4 KiB 0 48 B 3.8 K ok + bin S 8: 64 B 64 B 0 64 B 1 ok + bin S 9: 160 B 160 B 0 80 B 2 ok + bin S 17: 1.2 KiB 1.2 KiB 0 320 B 4 ok + bin S 21: 640 B 3.1 KiB 0 640 B 5 ok + bin S 33: 5.0 KiB 5.0 KiB 0 5.0 KiB 1 ok + + binned : 84.2 Ki 41.5 Mi 0 ok + huge : 0 0 0 ok + total : 84.2 KiB 41.5 MiB 0 + malloc req: 29.7 MiB + + pages peak total current block total# + touched : 152.8 KiB 152.8 KiB 152.8 KiB + pages : 8 14 0 ok + abandoned : 1 249 0 ok + reclaima : 0 + reclaimf : 249 + reabandon : 0 + waits : 0 + extended : 38 + retire : 35 + searches : 0.7 avg + + arenas peak total current block total# + reserved : 1.0 GiB 1.0 GiB 1.0 GiB + committed : 4.8 MiB 4.8 MiB 4.4 MiB + reset : 0 + purged : 385.5 Ki + arenas : 1 + rollback : 0 + mmaps : 3 + commits : 0 + resets : 1 + purges : 2 + guarded : 0 + heaps : 1 1 1 + + process peak total current block total# + threads : 1 1 1 + numa nodes: 1 + elapsed : 0.553 s + process : user: 0.557 s, system: 0.013 s, faults: 29, peak rss: 2.1 MiB, peak commit: 4.8 MiB ``` The above model of using the `mi_` prefixed API is not always possible @@ -1071,38 +1635,62 @@ See \ref overrides for more info. /*! \page environment Environment Options -You can set further options either programmatically (using [`mi_option_set`](https://microsoft.github.io/mimalloc/group__options.html)), -or via environment variables. +You can set further options either programmatically (using mi_option_set(), see \ref options), or via environment variables: - `MIMALLOC_SHOW_STATS=1`: show statistics when the program terminates. -- `MIMALLOC_VERBOSE=1`: show verbose messages. +- `MIMALLOC_VERBOSE=1`: show verbose messages (including statistics). - `MIMALLOC_SHOW_ERRORS=1`: show error and warning messages. -- `MIMALLOC_PAGE_RESET=0`: by default, mimalloc will reset (or purge) OS pages when not in use to signal to the OS - that the underlying physical memory can be reused. This can reduce memory fragmentation in long running (server) - programs. By setting it to `0` no such page resets will be done which can improve performance for programs that are not long - running. As an alternative, the `MIMALLOC_DECOMMIT_DELAY=` can be set higher (100ms by default) to make the page - reset occur less frequently instead of turning it off completely. -- `MIMALLOC_LARGE_OS_PAGES=1`: use large OS pages (2MiB) when available; for some workloads this can significantly - improve performance. Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs - to explicitly allow large OS pages (as on [Windows][windows-huge] and [Linux][linux-huge]). However, sometimes + +Advanced options: + +- `MIMALLOC_ARENA_EAGER_COMMIT=2`: turns on eager commit for the large arenas (usually 1GiB) from which mimalloc + allocates segments and pages. Set this to 2 (default) to + only enable this on overcommit systems (e.g. Linux). Set this to 1 to enable explicitly on other systems + as well (like Windows or macOS) which may improve performance (as the whole arena is committed at once). + Note that eager commit only increases the commit but not the actual the peak resident set + (rss) so it is generally ok to enable this. +- `MIMALLOC_PURGE_DELAY=N`: the delay in `N` milli-seconds (by default `1000` in v3) after which mimalloc will purge + OS pages that are not in use. This signals to the OS that the underlying physical memory can be reused which + can reduce memory fragmentation especially in long running (server) programs. Setting `N` to `0` purges immediately when + a page becomes unused which can improve memory usage but also decreases performance. + Setting it to `-1` disables purging completely. +- `MIMALLOC_PURGE_DECOMMITS=1`: By default "purging" memory means unused memory is decommitted (`MEM_DECOMMIT` on Windows, + `MADV_DONTNEED` (which decresease rss immediately) on `mmap` systems). Set this to 0 to instead "reset" unused + memory on a purge (`MEM_RESET` on Windows, generally `MADV_FREE` (which does not decrease rss immediately) on `mmap` systems). + Mimalloc generally does not "free" OS memory but only "purges" OS memory, in other words, it tries to keep virtual + address ranges and decommits within those ranges (to make the underlying physical memory available to other processes). + +Further options for large workloads and services: + +- `MIMALLOC_ALLOW_THP=1`: By default always allow transparent huge pages (THP) on Linux systems. On Android only this is + by default off. When set to `0`, THP is disabled for the process that mimalloc runs in. + In mimalloc v3.2+, If enabled, mimalloc also sets + the `MIMALLOC_MINIMAL_PURGE_SIZE` to 2MiB to avoid potentially breaking up transparent huge pages. +- `MIMALLOC_USE_NUMA_NODES=N`: pretend there are at most `N` NUMA nodes. If not set, the actual NUMA nodes are detected + at runtime. Setting `N` to 1 may avoid problems in some virtual environments. Also, setting it to a lower number than + the actual NUMA nodes is fine and will only cause threads to potentially allocate more memory across actual NUMA + nodes (but this can happen in any case as NUMA local allocation is always a best effort but not guaranteed). +- `MIMALLOC_ALLOW_LARGE_OS_PAGES=0`: Set to 1 to use large OS pages (2 or 4MiB) when available; for some workloads this can + significantly improve performance. However, large OS pages cannot be purged or shared with other processes so may lead + to increased memory usage in some cases. + Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs + to explicitly give permissions for large OS pages (as on [Windows][windows-huge] and [Linux][linux-huge]). However, sometimes the OS is very slow to reserve contiguous physical memory for large OS pages so use with care on systems that - can have fragmented memory (for that reason, we generally recommend to use `MIMALLOC_RESERVE_HUGE_OS_PAGES` instead when possible). -- `MIMALLOC_RESERVE_HUGE_OS_PAGES=N`: where N is the number of 1GiB _huge_ OS pages. This reserves the huge pages at + can have fragmented memory (for that reason, we generally recommend to use `MIMALLOC_RESERVE_HUGE_OS_PAGES` instead whenever possible). +- `MIMALLOC_RESERVE_HUGE_OS_PAGES=N`: where `N` is the number of 1GiB _huge_ OS pages. This reserves the huge pages at startup and sometimes this can give a large (latency) performance improvement on big workloads. - Usually it is better to not use - `MIMALLOC_LARGE_OS_PAGES` in combination with this setting. Just like large OS pages, use with care as reserving + Usually it is better to not use `MIMALLOC_ALLOW_LARGE_OS_PAGES=1` in combination with this setting. Just like large + OS pages, use with care as reserving contiguous physical memory can take a long time when memory is fragmented (but reserving the huge pages is done at startup only once). - Note that we usually need to explicitly enable huge OS pages (as on [Windows][windows-huge] and [Linux][linux-huge])). With huge OS pages, it may be beneficial to set the setting - `MIMALLOC_EAGER_COMMIT_DELAY=N` (`N` is 1 by default) to delay the initial `N` segments (of 4MiB) - of a thread to not allocate in the huge OS pages; this prevents threads that are short lived - and allocate just a little to take up space in the huge OS page area (which cannot be reset). -- `MIMALLOC_RESERVE_HUGE_OS_PAGES_AT=N`: where N is the numa node. This reserves the huge pages at a specific numa node. - (`N` is -1 by default to reserve huge pages evenly among the given number of numa nodes (or use the available ones as detected)) + Note that we usually need to explicitly give permission for huge OS pages (as on [Windows][windows-huge] and [Linux][linux-huge])). + The huge pages are usually allocated evenly among NUMA nodes. + We can use `MIMALLOC_RESERVE_HUGE_OS_PAGES_AT=N` where `N` is the numa node (starting at 0) to allocate all + the huge pages at a specific numa node instead. Use caution when using `fork` in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write for all pages in the original process including the huge OS pages. When any memory is now written in that area, the -OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in big increments. +OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in large increments. [linux-huge]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5 [windows-huge]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017 @@ -1111,88 +1699,106 @@ OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the /*! \page overrides Overriding Malloc -Overriding the standard `malloc` can be done either _dynamically_ or _statically_. +Overriding the standard `malloc` (and `new`) can be done either _dynamically_ or _statically_. ## Dynamic override This is the recommended way to override the standard malloc interface. +### Dynamic Override on Linux, BSD -### Linux, BSD - -On these systems we preload the mimalloc shared +On these ELF-based systems we preload the mimalloc shared library so all calls to the standard `malloc` interface are resolved to the _mimalloc_ library. - -- `env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram` +``` +> env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram +``` You can set extra environment variables to check that mimalloc is running, like: ``` -env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram +> env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram ``` or run with the debug version to get detailed statistics: ``` -env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram +> env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram ``` -### MacOS +### Dynamic Override on MacOS On macOS we can also preload the mimalloc shared library so all calls to the standard `malloc` interface are resolved to the _mimalloc_ library. - -- `env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram` +``` +> env DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram +``` Note that certain security restrictions may apply when doing this from the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash). -(Note: macOS support for dynamic overriding is recent, please report any issues.) +### Dynamic Override on Windows + +Dynamically overriding on mimalloc on Windows +is robust and has the particular advantage to be able to redirect all malloc/free calls +that go through the (dynamic) C runtime allocator, including those from other DLL's or +libraries. As it intercepts all allocation calls on a low level, it can be used reliably +on large programs that include other 3rd party components. +There are four requirements to make the overriding work well: + +1. Use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch). -### Windows +2. Link your program explicitly with the `mimalloc.lib` export library for the `mimalloc.dll`. + (which must be compiled with `-DMI_OVERRIDE=ON`, which is the default though). + To ensure the `mimalloc.dll` is actually loaded at run-time it is easiest + to insert some call to the mimalloc API in the `main` function, like `mi_version()` + (or use the `/include:mi_version` switch on the linker command, or + similarly, `#pragma comment(linker, "/include:mi_version")` in some source file). + See the `mimalloc-test-override` project for an example on how to use this. -Overriding on Windows is robust and has the -particular advantage to be able to redirect all malloc/free calls that go through -the (dynamic) C runtime allocator, including those from other DLL's or libraries. +3. The `mimalloc-redirect.dll` must be put in the same directory as the main + `mimalloc.dll` at runtime (as it is a dependency of that DLL). + The redirection DLL ensures that all calls to the C runtime malloc API get + redirected to mimalloc functions (which reside in `mimalloc.dll`). -The overriding on Windows requires that you link your program explicitly with -the mimalloc DLL and use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch). -Also, the `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be available -in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency). -The redirection DLL ensures that all calls to the C runtime malloc API get redirected to -mimalloc (in `mimalloc-override.dll`). +4. Ensure the `mimalloc.dll` comes as early as possible in the import + list of the final executable (so it can intercept all potential allocations). + You can use `minject -l ` to check this if needed. -To ensure the mimalloc DLL is loaded at run-time it is easiest to insert some -call to the mimalloc API in the `main` function, like `mi_version()` -(or use the `/INCLUDE:mi_version` switch on the linker). See the `mimalloc-override-test` project -for an example on how to use this. For best performance on Windows with C++, it +For best performance on Windows with C++, it is also recommended to also override the `new`/`delete` operations (by including -[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) a single(!) source file in your project). +[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/main/include/mimalloc-new-delet.h) +a single(!) source file in your project). The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic -overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully redirected. +overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully +redirected. -(Note: in principle, it is possible to even patch existing executables without any recompilation -if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the `mimalloc-override.dll` -into the import table (and put `mimalloc-redirect.dll` in the same folder) -Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388)). +For different platforms than x64, you may need a specific [redirection dll](bin). +Furthermore, we cannot always re-link an executable or ensure `mimalloc.dll` comes +first in the import table. In such cases the [`minject`](bin) tool can be used +to patch the executable's import tables. ## Static override -On Unix systems, you can also statically link with _mimalloc_ to override the standard +On Unix-like systems, you can also statically link with _mimalloc_ to override the standard malloc interface. The recommended way is to link the final program with the -_mimalloc_ single object file (`mimalloc-override.o`). We use +_mimalloc_ single object file (`mimalloc.o`). We use an object file instead of a library file as linkers give preference to that over archives to resolve symbols. To ensure that the standard malloc interface resolves to the _mimalloc_ library, link it as the first object file. For example: - ``` -gcc -o myprogram mimalloc-override.o myfile1.c ... +> gcc -o myprogram mimalloc.o myfile1.c ... ``` +Another way to override statically that works on all platforms, is to +link statically to mimalloc (as shown in the introduction) and include a +header file in each source file that re-defines `malloc` etc. to `mi_malloc`. +This is provided by [`mimalloc-override.h`](https://github.com/microsoft/mimalloc/blob/main/include/mimalloc-override.h). This only works reliably though if all sources are +under your control or otherwise mixing of pointers from different heaps may occur! + ## List of Overrides: The specific functions that get redirected to the _mimalloc_ library are: @@ -1261,6 +1867,159 @@ void _free_dbg(void* p, int block_type); */ +/*! \page modes Build Modes + +We can build mimalloc in various modes. + +## Secure Mode + +_mimalloc_ can be build in secure mode by using the `-DMI_SECURE=ON` flags in `cmake`. This build enables various mitigations +to make mimalloc more robust against exploits. In particular: + +- All internal mimalloc pages are surrounded by guard pages and the heap metadata is behind a guard page as well (so a buffer overflow + exploit cannot reach into the metadata). +- All free list pointers are + [encoded](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396) + with per-page keys which is used both to prevent overwrites with a known pointer, as well as to detect heap corruption. +- Double free's are detected (and ignored). +- The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to + mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc + from the OS are also address randomized. + +As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees. + +## Debug Mode + +When _mimalloc_ is built using debug mode, (`-DCMAKE_BUILD_TYPE=Debug`), +various checks are done at runtime to catch development errors. + +- Statistics are maintained in detail for each object size. They can be shown using `MIMALLOC_SHOW_STATS=1` at runtime. +- All objects have padding at the end to detect (byte precise) heap block overflows. +- Double free's, and freeing invalid heap pointers are detected. +- Corrupted free-lists and some forms of use-after-free are detected. + +## Guarded Mode + +_mimalloc_ can be build in guarded mode using the `-DMI_GUARDED=ON` flags in `cmake`. +This enables placing OS guard pages behind certain object allocations to catch buffer overflows as they occur. +This can be invaluable to catch buffer-overflow bugs in large programs. However, it also means that any object +allocated with a guard page takes at least 8 KiB memory for the guard page and its alignment. As such, allocating +a guard page for every allocation may be too expensive both in terms of memory, and in terms of performance with +many system calls. Therefore, there are various environment variables (and options) to tune this: + +- `MIMALLOC_GUARDED_SAMPLE_RATE=N`: Set the sample rate to `N` (by default 4000). This mode places a guard page + behind every `N` suitable object allocations (per thread). Since the performance in guarded mode without placing + guard pages is close to release mode, this can be used to enable guard pages even in production to catch latent + buffer overflow bugs. Set the sample rate to `1` to guard every object, and to `0` to place no guard pages at all. + +- `MIMALLOC_GUARDED_SAMPLE_SEED=N`: Start sampling at `N` (by default random). Can be used to reproduce a buffer + overflow if needed. + +- `MIMALLOC_GUARDED_MIN=N`, `MIMALLOC_GUARDED_MAX=N`: Minimal and maximal _rounded_ object sizes for which a guard + page is considered (`0` and `1GiB` respectively). If you suspect a buffer overflow occurs with an object of size + 141, set the minimum and maximum to `148` and the sample rate to `1` to have all of those guarded. + +- `MIMALLOC_GUARDED_PRECISE=1`: If we have an object of size 13, we would usually place it an aligned 16 bytes in + front of the guard page. Using `MIMALLOC_GUARDED_PRECISE` places it exactly 13 bytes before a page so that even + a 1 byte overflow is detected. This violates the C/C++ minimal alignment guarantees though so use with care. + +*/ + +/*! \page tools Tools + +Generally, we recommend using the standard allocator with memory tracking tools, but mimalloc +can also be build to support the [address sanitizer][asan] or the excellent [Valgrind] tool. +Moreover, it can be build to support Windows event tracing ([ETW]). +This has a small performance overhead but does allow detecting memory leaks and byte-precise +buffer overflows directly on final executables. See also the `test/test-wrong.c` file to test with various tools. + +## Valgrind + +To build with [valgrind] support, use the `MI_TRACK_VALGRIND=ON` cmake option: + +``` +> cmake ../.. -DMI_TRACK_VALGRIND=ON +``` + +This can also be combined with secure mode or debug mode. +You can then run your programs directly under valgrind: + +``` +> valgrind +``` + +If you rely on overriding `malloc`/`free` by mimalloc (instead of using the `mi_malloc`/`mi_free` API directly), +you also need to tell `valgrind` to not intercept those calls itself, and use: + +``` +> MIMALLOC_SHOW_STATS=1 valgrind --soname-synonyms=somalloc=*mimalloc* -- +``` + +By setting the `MIMALLOC_SHOW_STATS` environment variable you can check that mimalloc is indeed +used and not the standard allocator. Even though the [Valgrind option][valgrind-soname] +is called `--soname-synonyms`, this also works when overriding with a static library or object file. +To dynamically override mimalloc using `LD_PRELOAD` together with `valgrind`, use: + +``` +> valgrind --trace-children=yes --soname-synonyms=somalloc=*mimalloc* /usr/bin/env LD_PRELOAD=/usr/lib/libmimalloc.so -- +``` + +See also the `test/test-wrong.c` file to test with `valgrind`. + +Valgrind support is in its initial development -- please report any issues. + +[Valgrind]: https://valgrind.org/ +[valgrind-soname]: https://valgrind.org/docs/manual/manual-core.html#opt.soname-synonyms + +## ASAN + +To build with the address sanitizer, use the `-DMI_TRACK_ASAN=ON` cmake option: + +``` +> cmake ../.. -DMI_TRACK_ASAN=ON +``` + +This can also be combined with secure mode or debug mode. +You can then run your programs as:' + +``` +> ASAN_OPTIONS=verbosity=1 +``` + +When you link a program with an address sanitizer build of mimalloc, you should +generally compile that program too with the address sanitizer enabled. +For example, assuming you build mimalloc in `out/debug`: + +``` +clang -g -o test-wrong -Iinclude test/test-wrong.c out/debug/libmimalloc-asan-debug.a -lpthread -fsanitize=address -fsanitize-recover=address +``` + +Since the address sanitizer redirects the standard allocation functions, on some platforms (macOSX for example) +it is required to compile mimalloc with `-DMI_OVERRIDE=OFF`. +Address sanitizer support is in its initial development -- please report any issues. + +[asan]: https://github.com/google/sanitizers/wiki/AddressSanitizer + +## ETW + +Event tracing for Windows ([ETW]) provides a high performance way to capture all allocations though +mimalloc and analyze them later. To build with ETW support, use the `-DMI_TRACK_ETW=ON` cmake option. + +You can then capture an allocation trace using the Windows performance recorder (WPR), using the +`src/prim/windows/etw-mimalloc.wprp` profile. In an admin prompt, you can use: +``` +> wpr -start src\prim\windows\etw-mimalloc.wprp -filemode +> +> wpr -stop .etl +``` +and then open `.etl` in the Windows Performance Analyzer (WPA), or +use a tool like [TraceControl] that is specialized for analyzing mimalloc traces. + +[ETW]: https://learn.microsoft.com/en-us/windows-hardware/test/wpt/event-tracing-for-windows +[TraceControl]: https://github.com/xinglonghe/TraceControl + +*/ + /*! \page bench Performance We tested _mimalloc_ against many other top allocators over a wide diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doxygen.css b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doxygen.css index b24f5643..c889a8d2 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doxygen.css +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/mimalloc-doxygen.css @@ -47,3 +47,14 @@ div.fragment { #nav-sync img { display: none; } +h1,h2,h3,h4,h5,h6 { + transition:none; +} +.memtitle { + background-image: none; + background-color: #EEE; +} +table.memproto, .memproto { + text-shadow: none; + font-size: 110%; +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/release-notes.md b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/release-notes.md new file mode 100644 index 00000000..1e59f4a5 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/doc/release-notes.md @@ -0,0 +1,15 @@ +Binary and source releases for mimalloc. + +- __v3__: recommended: latest mimalloc design that tends to use less memory then v2 but should perform similarly. +- __v2__: stable: most widely used. +- __v1__: legacy. + +Notes: + +- Release versions follow the v3 version (and v1 and v2 versions are incremented independently). +- Generally it is recommended to download sources (or use `vcpkg` etc.) and build mimalloc as + part of your project. +- Source releases can also be downloaded directly from github by the tag. + For example . +- Binary releases include a release-, debug-, and secure build. +- Linux binaries are built on Ubuntu 22. diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-override-test.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-override-test.vcxproj deleted file mode 100644 index 04c16a9f..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-override-test.vcxproj +++ /dev/null @@ -1,190 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FEF7868F-750E-4C21-A04D-22707CC66879} - mimalloc-override-test - mimalloc-override-test - 10.0.19041.0 - - - - Application - true - v141 - - - Application - false - v141 - true - - - Application - true - v141 - - - Application - false - v141 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - MultiThreadedDebugDLL - false - Default - false - - - Console - kernel32.lib;%(AdditionalDependencies) - - - - - - - - - - Level3 - Disabled - true - true - ..\..\include - MultiThreadedDebugDLL - Sync - Default - false - - - Console - - - kernel32.lib;%(AdditionalDependencies) - - - - - - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - MultiThreadedDLL - - - true - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - MultiThreadedDLL - - - true - true - Console - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - {abb5eae7-b3e6-432e-b636-333449892ea7} - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-override.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-override.vcxproj deleted file mode 100644 index 6d20eb57..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-override.vcxproj +++ /dev/null @@ -1,260 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {ABB5EAE7-B3E6-432E-B636-333449892EA7} - mimalloc-override - mimalloc-override - 10.0.19041.0 - - - - DynamicLibrary - true - v141 - - - DynamicLibrary - false - v141 - - - DynamicLibrary - true - v141 - - - DynamicLibrary - false - v141 - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - - Level3 - Disabled - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); - MultiThreadedDebugDLL - false - Default - - - $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) - - - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" - - - Copy mimalloc-redirect32.dll to the output directory - - - - - Level3 - Disabled - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); - MultiThreadedDebugDLL - false - Default - - - $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;bcrypt.lib;%(AdditionalDependencies) - - - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" - - - copy mimalloc-redirect.dll to the output directory - - - - - Level3 - MaxSpeed - true - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - MultiThreadedDLL - Default - false - - - true - true - $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" - - - Copy mimalloc-redirect32.dll to the output directory - - - - - Level3 - MaxSpeed - true - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - MultiThreadedDLL - Default - false - - - true - true - $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;bcrypt.lib;%(AdditionalDependencies) - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" - - - copy mimalloc-redirect.dll to the output directory - - - - - - - - - - - - - - - - false - false - false - false - - - true - true - true - true - - - - - - - - - - - - - true - true - true - true - - - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-test-stress.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-test-stress.vcxproj deleted file mode 100644 index 061b8605..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-test-stress.vcxproj +++ /dev/null @@ -1,159 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FEF7958F-750E-4C21-A04D-22707CC66878} - mimalloc-test-stress - mimalloc-test-stress - 10.0.19041.0 - - - - Application - true - v141 - - - Application - false - v141 - true - - - Application - true - v141 - - - Application - false - v141 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - - - Console - - - - - Level3 - Disabled - true - true - ..\..\include - - - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - %(PreprocessorDefinitions);NDEBUG - - - true - true - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - %(PreprocessorDefinitions);NDEBUG - - - true - true - Console - - - - - false - false - false - false - - - - - {abb5eae7-b3e6-432e-b636-333449892ea6} - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-test.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-test.vcxproj deleted file mode 100644 index 04bd6537..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc-test.vcxproj +++ /dev/null @@ -1,158 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FEF7858F-750E-4C21-A04D-22707CC66878} - mimalloctest - mimalloc-test - 10.0.19041.0 - - - - Application - true - v141 - - - Application - false - v141 - true - - - Application - true - v141 - - - Application - false - v141 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - stdcpp17 - - - Console - - - - - Level3 - Disabled - true - true - ..\..\include - stdcpp14 - - - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - stdcpp17 - - - true - true - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - stdcpp17 - - - true - true - Console - - - - - {abb5eae7-b3e6-432e-b636-333449892ea6} - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc.sln b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc.sln deleted file mode 100644 index 515c03f2..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc.sln +++ /dev/null @@ -1,71 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.102 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} - EndGlobalSection -EndGlobal diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc.vcxproj deleted file mode 100644 index ece9a14d..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2017/mimalloc.vcxproj +++ /dev/null @@ -1,260 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {ABB5EAE7-B3E6-432E-B636-333449892EA6} - mimalloc - 10.0.19041.0 - mimalloc - - - - StaticLibrary - true - v141 - - - StaticLibrary - false - v141 - true - - - StaticLibrary - true - v141 - - - StaticLibrary - false - v141 - true - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - false - - - false - - - false - - - false - - - - Level3 - Disabled - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;MI_DEBUG=3;%(PreprocessorDefinitions); - CompileAsC - false - stdcpp17 - - - - - - - - - - - Level4 - Disabled - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;MI_DEBUG=3;%(PreprocessorDefinitions); - CompileAsCpp - false - stdcpp14 - - - - - - - - - - - - - - - - - - - Level3 - MaxSpeed - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - false - Default - CompileAsC - true - - - true - true - - - - - - - - - - - Level4 - MaxSpeed - true - true - ../../include - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - false - Default - CompileAsC - true - - - true - true - - - - - - - - - - - - - - - - - false - false - false - false - - - true - true - true - true - - - - - - - - - - - - true - true - true - true - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-override-test.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-override-test.vcxproj deleted file mode 100644 index 7a9202f1..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-override-test.vcxproj +++ /dev/null @@ -1,190 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FEF7868F-750E-4C21-A04D-22707CC66879} - mimalloc-override-test - 10.0 - mimalloc-override-test - - - - Application - true - v142 - - - Application - false - v142 - true - - - Application - true - v142 - - - Application - false - v142 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - MultiThreadedDebugDLL - Sync - Default - false - - - Console - kernel32.lib;%(AdditionalDependencies) - - - - - - - - - - Level3 - Disabled - true - true - ..\..\include - MultiThreadedDebugDLL - Sync - Default - false - - - Console - - - kernel32.lib;%(AdditionalDependencies) - - - - - - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - MultiThreadedDLL - - - true - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - MultiThreadedDLL - - - true - true - Console - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - - - - {abb5eae7-b3e6-432e-b636-333449892ea7} - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-override.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-override.vcxproj deleted file mode 100644 index a84a5178..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-override.vcxproj +++ /dev/null @@ -1,260 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {ABB5EAE7-B3E6-432E-B636-333449892EA7} - mimalloc-override - 10.0 - mimalloc-override - - - - DynamicLibrary - true - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - true - v142 - - - DynamicLibrary - false - v142 - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - - Level3 - Disabled - true - true - ../../include - MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); - MultiThreadedDebugDLL - false - Default - - - $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) - - - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" - - - Copy mimalloc-redirect32.dll to the output directory - - - - - Level3 - Disabled - true - true - ../../include - MI_DEBUG=3;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); - MultiThreadedDebugDLL - false - Default - - - $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) - - - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" - - - copy mimalloc-redirect.dll to the output directory - - - - - Level3 - MaxSpeed - true - true - true - ../../include - MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - MultiThreadedDLL - Default - false - - - true - true - $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" - - - Copy mimalloc-redirect32.dll to the output directory - - - - - Level3 - MaxSpeed - true - true - true - ../../include - MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - MultiThreadedDLL - Default - false - - - true - true - $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" - - - copy mimalloc-redirect.dll to the output directory - - - - - - - - - - - - - - - - false - false - false - false - - - true - true - true - true - - - - - - - - - - - - - true - true - true - true - - - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test-api.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test-api.vcxproj deleted file mode 100644 index 812a9cb1..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test-api.vcxproj +++ /dev/null @@ -1,155 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FFF7958F-750E-4C21-A04D-22707CC66878} - mimalloc-test-api - 10.0 - mimalloc-test-api - - - - Application - true - v142 - - - Application - false - v142 - true - - - Application - true - v142 - - - Application - false - v142 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - - - Console - - - - - Level3 - Disabled - true - true - ..\..\include - - - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - %(PreprocessorDefinitions);NDEBUG - - - true - true - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - %(PreprocessorDefinitions);NDEBUG - - - true - true - Console - - - - - - - - - {abb5eae7-b3e6-432e-b636-333449892ea6} - - - - - - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test-stress.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test-stress.vcxproj deleted file mode 100644 index ef7ab357..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test-stress.vcxproj +++ /dev/null @@ -1,159 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FEF7958F-750E-4C21-A04D-22707CC66878} - mimalloc-test-stress - 10.0 - mimalloc-test-stress - - - - Application - true - v142 - - - Application - false - v142 - true - - - Application - true - v142 - - - Application - false - v142 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - - - Console - - - - - Level3 - Disabled - true - true - ..\..\include - - - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - %(PreprocessorDefinitions);NDEBUG - - - true - true - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - %(PreprocessorDefinitions);NDEBUG - - - true - true - Console - - - - - false - false - false - false - - - - - {abb5eae7-b3e6-432e-b636-333449892ea6} - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test.vcxproj deleted file mode 100644 index 13af6ab4..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc-test.vcxproj +++ /dev/null @@ -1,158 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {FEF7858F-750E-4C21-A04D-22707CC66878} - mimalloctest - 10.0 - mimalloc-test - - - - Application - true - v142 - - - Application - false - v142 - true - - - Application - true - v142 - - - Application - false - v142 - true - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - true - true - ..\..\include - stdcpp17 - - - Console - - - - - Level3 - Disabled - true - true - ..\..\include - stdcpp17 - - - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - stdcpp17 - - - true - true - Console - - - - - Level3 - MaxSpeed - true - true - true - true - ..\..\include - _MBCS;%(PreprocessorDefinitions);NDEBUG - stdcpp17 - - - true - true - Console - - - - - {abb5eae7-b3e6-432e-b636-333449892ea6} - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc.sln b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc.sln deleted file mode 100644 index 6ff01d3b..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc.sln +++ /dev/null @@ -1,81 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} - EndGlobalSection -EndGlobal diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc.vcxproj deleted file mode 100644 index 0076b1db..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2019/mimalloc.vcxproj +++ /dev/null @@ -1,258 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {ABB5EAE7-B3E6-432E-B636-333449892EA6} - mimalloc - 10.0 - mimalloc - - - - StaticLibrary - true - v142 - - - StaticLibrary - false - v142 - true - - - StaticLibrary - true - v142 - - - StaticLibrary - false - v142 - true - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - - Level4 - Disabled - true - true - ../../include - MI_DEBUG=3;%(PreprocessorDefinitions); - CompileAsCpp - false - Default - - - - - - - - - - - Level4 - Disabled - true - Default - ../../include - MI_DEBUG=3;%(PreprocessorDefinitions); - CompileAsCpp - false - Default - - - - - - - - - - - - - - - - - - - Level4 - MaxSpeed - true - true - ../../include - %(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - false - Default - CompileAsCpp - true - Default - - - true - true - - - - - - - - - - - Level4 - MaxSpeed - true - true - ../../include - %(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - false - Default - CompileAsCpp - true - Default - - - true - true - - - - - - - - - - - - - - - - - false - false - false - false - - - true - true - true - true - - - - - - false - - - - - - - true - true - true - true - - - - true - true - true - true - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-lib.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-lib.vcxproj new file mode 100644 index 00000000..92d4a90c --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-lib.vcxproj @@ -0,0 +1,502 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {ABB5EAE7-B3E6-432E-B636-333449892EA6} + mimalloc-lib + 10.0 + mimalloc-lib + + + + StaticLibrary + true + v143 + + + StaticLibrary + false + v143 + true + + + StaticLibrary + true + v143 + + + StaticLibrary + true + v143 + + + StaticLibrary + true + v143 + + + StaticLibrary + false + v143 + true + + + StaticLibrary + false + v143 + true + + + StaticLibrary + false + v143 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc + + + false + + + + Level4 + Disabled + true + Default + ../../include + MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + /Zc:__cplusplus %(AdditionalOptions) + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + /Zc:__cplusplus %(AdditionalOptions) + + + + + + + + + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + /Zc:__cplusplus %(AdditionalOptions) + + + + + + + + + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_DEBUG=3;MI_GUARDED=0;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + /Zc:__cplusplus %(AdditionalOptions) + + + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + %(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + %(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + AdvancedVectorExtensions2 + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + %(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + CPUExtensionRequirementsARMv81 + Sync + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + %(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + CPUExtensionRequirementsARMv81 + Sync + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + + + + + + + false + false + false + + + true + true + true + true + true + true + true + true + + + + + + + + + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-dll.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-dll.vcxproj new file mode 100644 index 00000000..ab80dd65 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-dll.vcxproj @@ -0,0 +1,517 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {ABB5EAE7-B3E6-432E-B636-333449892EA7} + mimalloc-override-dll + 10.0 + mimalloc-override-dll + + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc + + + false + + + + Level3 + Disabled + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); + MultiThreadedDebugDLL + false + CompileAsCpp + /Zc:__cplusplus %(AdditionalOptions) + + + $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) + + + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" + + + Copy mimalloc-redirect32.dll to the output directory + + + + + Level3 + Disabled + true + true + ../../include + MI_DEBUG=4;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); + MultiThreadedDebugDLL + false + CompileAsCpp + /Zc:__cplusplus %(AdditionalOptions) + + + $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) + + + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" + + + copy mimalloc-redirect.dll to the output directory + + + + + Level3 + Disabled + true + true + ../../include + MI_DEBUG=4;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); + MultiThreadedDebugDLL + false + CompileAsCpp + /Zc:__cplusplus %(AdditionalOptions) + + + $(ProjectDir)\..\..\bin\mimalloc-redirect-arm64.lib;%(AdditionalDependencies) + + + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64.dll" "$(OutputPath)" + + + copy mimalloc-redirect-arm64.dll to the output directory + + + + + Level3 + Disabled + true + true + ../../include + MI_DEBUG=4;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); + MultiThreadedDebugDLL + false + CompileAsCpp + /Zc:__cplusplus %(AdditionalOptions) + + + $(ProjectDir)\..\..\bin\mimalloc-redirect-arm64ec.lib;%(AdditionalDependencies) + + + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64ec.dll" "$(OutputPath)" + + + copy mimalloc-redirect-arm64ec.dll to the output directory + + + + + Level3 + MaxSpeed + true + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + MultiThreadedDLL + CompileAsCpp + false + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" + + + Copy mimalloc-redirect32.dll to the output directory + + + + + Level3 + MaxSpeed + true + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + MultiThreadedDLL + CompileAsCpp + false + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" + + + copy mimalloc-redirect.dll to the output directory + + + + + Level3 + MaxSpeed + true + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + MultiThreadedDLL + CompileAsCpp + false + CPUExtensionRequirementsARMv81 + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + $(ProjectDir)\..\..\bin\mimalloc-redirect-arm64.lib;%(AdditionalDependencies) + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64.dll" "$(OutputPath)" + + + copy mimalloc-redirect-arm64.dll to the output directory + + + + + Level3 + MaxSpeed + true + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + MultiThreadedDLL + CompileAsCpp + false + CPUExtensionRequirementsARMv81 + /Zc:__cplusplus %(AdditionalOptions) + + + true + true + $(ProjectDir)\..\..\bin\mimalloc-redirect-arm64ec.lib;%(AdditionalDependencies) + + + Default + false + $(OutDir)$(TargetName).dll.lib + $(OutDir)$(TargetName).dll.pdb + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64ec.dll" "$(OutputPath)" + + + copy mimalloc-redirect-arm64ec.dll to the output directory + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + + + + + + + + true + true + true + true + true + true + true + true + + + + + + + + + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + + + + + + + + + + \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-static-lib.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-static-lib.vcxproj new file mode 100644 index 00000000..a5a39452 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-static-lib.vcxproj @@ -0,0 +1,511 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {ABB5EAE7-B3E6-432E-B636-333449892EA8} + mimalloc-override-static-lib + 10.0 + mimalloc-override-static-lib + + + + StaticLibrary + true + v143 + + + StaticLibrary + false + v143 + true + + + StaticLibrary + true + v143 + + + StaticLibrary + true + v143 + + + StaticLibrary + true + v143 + + + StaticLibrary + false + v143 + true + + + StaticLibrary + false + v143 + true + + + StaticLibrary + false + v143 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + false + + + + Level4 + Disabled + true + Default + ../../include + MI_MALLOC_OVERRIDE;MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + MultiThreaded + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_MALLOC_OVERRIDE;MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + MultiThreaded + + + + + + + + + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_MALLOC_OVERRIDE;MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + MultiThreaded + + + + + + + + + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_MALLOC_OVERRIDE;MI_DEBUG=3;MI_GUARDED=0;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + MultiThreaded + + + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + MultiThreaded + + + true + true + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG;MI_GUARDED=1 + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + MultiThreaded + + + true + true + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + CPUExtensionRequirementsARMv81 + Sync + MultiThreaded + + + true + true + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + CPUExtensionRequirementsARMv81 + Sync + MultiThreaded + + + true + true + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + + + + + false + false + false + + + true + true + true + true + true + true + true + true + + + + + + + + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test-dep.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test-dep.vcxproj new file mode 100644 index 00000000..c1b89690 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test-dep.vcxproj @@ -0,0 +1,355 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FEF7869F-750E-4C21-A04D-22707CC66879} + mimalloc-test-override-dep + 10.0 + mimalloc-test-override-dep + + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + true + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + true + + + DynamicLibrary + false + v143 + true + + + DynamicLibrary + false + v143 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test.vcxproj index a3c56f7b..1c7a50c5 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test.vcxproj +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override-test.vcxproj @@ -1,10 +1,26 @@ + + Debug + ARM64 + + + Debug + ARM64EC + Debug Win32 + + Release + ARM64 + + + Release + ARM64EC + Release Win32 @@ -23,7 +39,7 @@ {FEF7868F-750E-4C21-A04D-22707CC66879} mimalloc-override-test 10.0 - mimalloc-override-test + mimalloc-test-override @@ -42,12 +58,34 @@ true v143 + + Application + true + v143 + + + Application + true + v143 + Application false v143 true + + Application + false + v143 + true + + + Application + false + v143 + true + @@ -62,9 +100,21 @@ + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ @@ -78,10 +128,29 @@ $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + Level3 @@ -128,6 +197,54 @@ + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + Level3 @@ -139,6 +256,7 @@ ..\..\include _MBCS;%(PreprocessorDefinitions);NDEBUG MultiThreadedDLL + Default true @@ -162,6 +280,59 @@ ..\..\include _MBCS;%(PreprocessorDefinitions);NDEBUG MultiThreadedDLL + Default + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + Default + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + Default true @@ -180,9 +351,12 @@ - + {abb5eae7-b3e6-432e-b636-333449892ea7} + + {fef7869f-750e-4c21-a04d-22707cc66879} + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override.vcxproj deleted file mode 100644 index df2a0816..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-override.vcxproj +++ /dev/null @@ -1,271 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {ABB5EAE7-B3E6-432E-B636-333449892EA7} - mimalloc-override - 10.0 - mimalloc-override - - - - DynamicLibrary - true - v143 - - - DynamicLibrary - false - v143 - - - DynamicLibrary - true - v143 - - - DynamicLibrary - false - v143 - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .dll - mimalloc-override - - - - Level3 - Disabled - true - true - ../../include - MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); - MultiThreadedDebugDLL - false - Default - - - $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) - - - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" - - - Copy mimalloc-redirect32.dll to the output directory - - - - - Level3 - Disabled - true - true - ../../include - MI_DEBUG=4;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); - MultiThreadedDebugDLL - false - Default - - - $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) - - - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" - - - copy mimalloc-redirect.dll to the output directory - - - - - Level3 - MaxSpeed - true - true - true - ../../include - MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - MultiThreadedDLL - Default - false - - - true - true - $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" - - - Copy mimalloc-redirect32.dll to the output directory - - - - - Level3 - MaxSpeed - true - true - true - ../../include - MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - MultiThreadedDLL - Default - false - - - true - true - $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) - - - Default - false - - - COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" - - - copy mimalloc-redirect.dll to the output directory - - - - - - - - - - - - - - - - - - false - false - false - false - - - true - true - true - true - - - - - - - - - - - true - true - true - true - - - - - true - true - true - true - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-api.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-api.vcxproj index d9b9cae4..440693a2 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-api.vcxproj +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-api.vcxproj @@ -1,10 +1,26 @@ + + Debug + ARM64 + + + Debug + ARM64EC + Debug Win32 + + Release + ARM64 + + + Release + ARM64EC + Release Win32 @@ -42,12 +58,34 @@ true v143 + + Application + true + v143 + + + Application + true + v143 + Application false v143 true + + Application + false + v143 + true + + + Application + false + v143 + true + @@ -62,9 +100,21 @@ + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ @@ -78,10 +128,29 @@ $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + Level3 @@ -106,6 +175,30 @@ Console + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + Level3 @@ -140,19 +233,59 @@ Console + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + + + true + true + Console + + true true true + true + true true + true + true false + false + false - + {abb5eae7-b3e6-432e-b636-333449892ea6} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-dep.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-dep.vcxproj new file mode 100644 index 00000000..17353452 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-dep.vcxproj @@ -0,0 +1,360 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FEF78690-750E-4C21-A04D-22707CC66879} + mimalloc-test-static-dep + 10.0 + mimalloc-test-static-dep + + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + true + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + true + + + DynamicLibrary + false + v143 + true + + + DynamicLibrary + false + v143 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + {abb5eae7-b3e6-432e-b636-333449892ea6} + + + + + + \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-override-static.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-override-static.vcxproj new file mode 100644 index 00000000..baf4fc90 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-override-static.vcxproj @@ -0,0 +1,310 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FEF7858F-750E-4C21-A04D-22707CC66879} + mimalloc-test-override-static + 10.0 + mimalloc-test-override-static + + + + Application + true + v143 + + + Application + false + v143 + true + + + Application + true + v143 + + + Application + true + v143 + + + Application + true + v143 + + + Application + false + v143 + true + + + Application + false + v143 + true + + + Application + false + v143 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + false + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + true + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + false + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + false + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + true + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + MultiThreaded + MI_DEBUG=3;;MI_MALLOC_OVERRIDE + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + MultiThreaded + MI_DEBUG=3;;MI_MALLOC_OVERRIDE + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + MultiThreaded + MI_DEBUG=3;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions);MI_MALLOC_OVERRIDE + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + MultiThreaded + MI_DEBUG=3;_AMD64_;_ARM64EC_;%(ClCompile.PreprocessorDefinitions);MI_MALLOC_OVERRIDE + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG;MI_MALLOC_OVERRIDE + stdcpp17 + MultiThreaded + false + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG;MI_MALLOC_OVERRIDE + stdcpp17 + MultiThreaded + + + true + true + Console + Default + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG;MI_MALLOC_OVERRIDE + stdcpp17 + MultiThreaded + false + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG;MI_MALLOC_OVERRIDE + stdcpp17 + MultiThreaded + false + + + true + true + Console + + + + + + + + {abb5eae7-b3e6-432e-b636-333449892ea8} + + + + + + \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-stress.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-stress.vcxproj index 14bd3e69..d6af71ce 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-stress.vcxproj +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test-stress.vcxproj @@ -1,10 +1,26 @@ + + Debug + ARM64 + + + Debug + ARM64EC + Debug Win32 + + Release + ARM64 + + + Release + ARM64EC + Release Win32 @@ -42,12 +58,34 @@ true v143 + + Application + true + v143 + + + Application + true + v143 + Application false v143 true + + Application + false + v143 + true + + + Application + false + v143 + true + @@ -62,9 +100,21 @@ + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ @@ -78,10 +128,29 @@ $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + Level3 @@ -106,6 +175,30 @@ Console + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + Level3 @@ -140,17 +233,57 @@ Console + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + CPUExtensionRequirementsARMv81 + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + CPUExtensionRequirementsARMv81 + + + true + true + Console + + false false + false + false false false + false + false - - {abb5eae7-b3e6-432e-b636-333449892ea7} + + {abb5eae7-b3e6-432e-b636-333449892ea6} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test.vcxproj index 506dd7d4..1e41fca1 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test.vcxproj +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc-test.vcxproj @@ -1,10 +1,26 @@ + + Debug + ARM64 + + + Debug + ARM64EC + Debug Win32 + + Release + ARM64 + + + Release + ARM64EC + Release Win32 @@ -23,7 +39,7 @@ {FEF7858F-750E-4C21-A04D-22707CC66878} mimalloctest 10.0 - mimalloc-test + mimalloc-test-static @@ -42,12 +58,34 @@ true v143 + + Application + true + v143 + + + Application + true + v143 + Application false v143 true + + Application + false + v143 + true + + + Application + false + v143 + true + @@ -62,9 +100,21 @@ + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ @@ -78,10 +128,29 @@ $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + Level3 @@ -108,6 +177,32 @@ Console + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + + + Console + + Level3 @@ -144,13 +239,49 @@ Console + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + stdcpp17 + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + stdcpp17 + + + true + true + Console + + - - {abb5eae7-b3e6-432e-b636-333449892ea6} - + - + + {abb5eae7-b3e6-432e-b636-333449892ea6} + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.sln b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.sln index 6ff01d3b..578221e5 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.sln +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.sln @@ -1,81 +1,208 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-lib", "mimalloc-lib.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-dll", "mimalloc-override-dll.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-static-lib", "mimalloc-override-static-lib.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-override", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-override-dep", "mimalloc-override-test-dep.vcxproj", "{FEF7869F-750E-4C21-A04D-22707CC66879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-static", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" + ProjectSection(ProjectDependencies) = postProject + {FEF78690-750E-4C21-A04D-22707CC66879} = {FEF78690-750E-4C21-A04D-22707CC66879} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-static-dep", "mimalloc-test-dep.vcxproj", "{FEF78690-750E-4C21-A04D-22707CC66879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-override-static", "mimalloc-test-override-static.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|ARM64EC = Debug|ARM64EC + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|ARM64EC = Release|ARM64EC + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|ARM64.Build.0 = Debug|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|ARM64.ActiveCfg = Release|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|ARM64.Build.0 = Release|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|ARM64EC.Build.0 = Release|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|ARM64.Build.0 = Debug|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|ARM64.ActiveCfg = Release|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|ARM64.Build.0 = Release|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|ARM64EC.Build.0 = Release|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|ARM64.Build.0 = Debug|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|ARM64.ActiveCfg = Release|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|ARM64.Build.0 = Release|ARM64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|ARM64EC.Build.0 = Release|ARM64EC + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA8}.Release|x86.Build.0 = Release|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.Build.0 = Debug|ARM64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|ARM64.ActiveCfg = Release|ARM64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|ARM64.Build.0 = Release|ARM64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.Build.0 = Debug|ARM64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64.ActiveCfg = Release|ARM64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64.Build.0 = Release|ARM64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 + {FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|ARM64.Build.0 = Debug|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|ARM64.ActiveCfg = Release|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|ARM64.Build.0 = Release|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|ARM64.Build.0 = Debug|ARM64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF78690-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|ARM64.ActiveCfg = Release|ARM64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|ARM64.Build.0 = Release|ARM64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 + {FEF78690-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.Build.0 = Debug|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|ARM64.ActiveCfg = Release|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|ARM64.Build.0 = Release|ARM64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64.Build.0 = Debug|ARM64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64.ActiveCfg = Release|ARM64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64.Build.0 = Release|ARM64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64.Build.0 = Debug|ARM64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|ARM64EC.Build.0 = Debug|ARM64EC + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64.ActiveCfg = Release|ARM64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64.Build.0 = Release|ARM64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64EC.ActiveCfg = Release|ARM64EC + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|ARM64EC.Build.0 = Release|ARM64EC + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} + EndGlobalSection +EndGlobal diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.vcxproj b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.vcxproj deleted file mode 100644 index 33ad9cef..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/ide/vs2022/mimalloc.vcxproj +++ /dev/null @@ -1,264 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {ABB5EAE7-B3E6-432E-B636-333449892EA6} - mimalloc - 10.0 - mimalloc - - - - StaticLibrary - true - v143 - - - StaticLibrary - false - v143 - true - - - StaticLibrary - true - v143 - - - StaticLibrary - false - v143 - true - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ - $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ - .lib - mimalloc-static - - - - Level4 - Disabled - true - Default - ../../include - MI_DEBUG=3;%(PreprocessorDefinitions); - CompileAsCpp - false - stdcpp20 - - - - - - - - - - - Level4 - Disabled - true - Default - ../../include - MI_DEBUG=4;MI_SECURE=0;%(PreprocessorDefinitions); - CompileAsCpp - false - stdcpp20 - - - - - - - - - - - - - - - - - - - Level4 - MaxSpeed - true - Default - ../../include - %(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - false - Default - CompileAsCpp - true - stdcpp20 - - - true - true - - - - - - - - - - - Level4 - MaxSpeed - true - Default - ../../include - %(PreprocessorDefinitions);NDEBUG - AssemblyAndSourceCode - $(IntDir) - false - false - Default - CompileAsCpp - true - stdcpp20 - - - true - true - - - - - - - - - - - - - - - - - false - false - false - false - - - true - true - true - true - - - - - - false - - - true - true - true - true - - - - - - - true - true - true - true - - - - true - true - true - true - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-override.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-override.h index 48a8a622..46e05452 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-override.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-override.h @@ -19,7 +19,7 @@ not accidentally mix pointers from different allocators). // Standard C allocation #define malloc(n) mi_malloc(n) -#define calloc(n,c) mi_calloc(n,c) +#define calloc(c,n) mi_calloc(c,n) #define realloc(p,n) mi_realloc(p,n) #define free(p) mi_free(p) @@ -30,26 +30,26 @@ not accidentally mix pointers from different allocators). // Microsoft extensions #define _expand(p,n) mi_expand(p,n) #define _msize(p) mi_usable_size(p) -#define _recalloc(p,n,c) mi_recalloc(p,n,c) +#define _recalloc(p,c,n) mi_recalloc(p,c,n) #define _strdup(s) mi_strdup(s) #define _strndup(s,n) mi_strndup(s,n) -#define _wcsdup(s) (wchar_t*)mi_wcsdup((const unsigned short*)(s)) +#define _wcsdup(s) mi_wcsdup(s) #define _mbsdup(s) mi_mbsdup(s) -#define _dupenv_s(b,n,v) mi_dupenv_s(b,n,v) -#define _wdupenv_s(b,n,v) mi_wdupenv_s((unsigned short*)(b),n,(const unsigned short*)(v)) +#define _dupenv_s(buf,n,nm) mi_dupenv_s(buf,n,nm) +#define _wdupenv_s(buf,n,nm) mi_wdupenv_s(buf,n,nm) // Various Posix and Unix variants #define reallocf(p,n) mi_reallocf(p,n) #define malloc_size(p) mi_usable_size(p) #define malloc_usable_size(p) mi_usable_size(p) -#define malloc_good_size(sz) mi_malloc_good_size(sz) +#define malloc_good_size(n) mi_malloc_good_size(n) #define cfree(p) mi_free(p) #define valloc(n) mi_valloc(n) #define pvalloc(n) mi_pvalloc(n) -#define reallocarray(p,s,n) mi_reallocarray(p,s,n) -#define reallocarr(p,s,n) mi_reallocarr(p,s,n) +#define reallocarray(p,c,n) mi_reallocarray(p,c,n) +#define reallocarr(ptrp,c,n) mi_reallocarr(ptrp,c,n) #define memalign(a,n) mi_memalign(a,n) #define aligned_alloc(a,n) mi_aligned_alloc(a,n) #define posix_memalign(p,a,n) mi_posix_memalign(p,a,n) @@ -58,11 +58,11 @@ not accidentally mix pointers from different allocators). // Microsoft aligned variants #define _aligned_malloc(n,a) mi_malloc_aligned(n,a) #define _aligned_realloc(p,n,a) mi_realloc_aligned(p,n,a) -#define _aligned_recalloc(p,s,n,a) mi_aligned_recalloc(p,s,n,a) +#define _aligned_recalloc(p,c,n,a) mi_aligned_recalloc(p,c,n,a) #define _aligned_msize(p,a,o) mi_usable_size(p) #define _aligned_free(p) mi_free(p) #define _aligned_offset_malloc(n,a,o) mi_malloc_aligned_at(n,a,o) #define _aligned_offset_realloc(p,n,a,o) mi_realloc_aligned_at(p,n,a,o) -#define _aligned_offset_recalloc(p,s,n,a,o) mi_recalloc_aligned_at(p,s,n,a,o) +#define _aligned_offset_recalloc(p,c,n,a,o) mi_recalloc_aligned_at(p,c,n,a,o) #endif // MIMALLOC_OVERRIDE_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-stats.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-stats.h new file mode 100644 index 00000000..1c620fd1 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc-stats.h @@ -0,0 +1,168 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2024-2025, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#pragma once +#ifndef MIMALLOC_STATS_H +#define MIMALLOC_STATS_H + +#include +#include // memset +#include // int64_t + +#define MI_STAT_VERSION 5 // increased on every backward incompatible change + +// alignment for atomic fields +#if defined(_MSC_VER) +#define mi_decl_align(a) __declspec(align(a)) +#elif defined(__GNUC__) +#define mi_decl_align(a) __attribute__((aligned(a))) +#elif __cplusplus >= 201103L +#define mi_decl_align(a) alignas(a) +#else +#define mi_decl_align(a) +#endif + + +// count allocation over time +typedef struct mi_stat_count_s { + int64_t total; // total allocated + int64_t peak; // peak allocation + int64_t current; // current allocation +} mi_stat_count_t; + +// counters only increase +typedef struct mi_stat_counter_s { + int64_t total; // total count +} mi_stat_counter_t; + +#define MI_STAT_FIELDS() \ + MI_STAT_COUNT(pages) /* count of mimalloc pages */ \ + MI_STAT_COUNT(reserved) /* reserved memory bytes */ \ + MI_STAT_COUNT(committed) /* committed bytes */ \ + MI_STAT_COUNTER(reset) /* reset bytes */ \ + MI_STAT_COUNTER(purged) /* purged bytes */ \ + MI_STAT_COUNT(page_committed) /* committed memory inside pages */ \ + MI_STAT_COUNT(pages_abandoned) /* abandoned pages count */ \ + MI_STAT_COUNT(threads) /* number of threads */ \ + MI_STAT_COUNT(malloc_normal) /* allocated bytes <= MI_LARGE_OBJ_SIZE_MAX */ \ + MI_STAT_COUNT(malloc_huge) /* allocated bytes in huge pages */ \ + MI_STAT_COUNT(malloc_requested) /* malloc requested bytes */ \ + \ + MI_STAT_COUNTER(mmap_calls) \ + MI_STAT_COUNTER(commit_calls) \ + MI_STAT_COUNTER(reset_calls) \ + MI_STAT_COUNTER(purge_calls) \ + MI_STAT_COUNTER(arena_count) /* number of memory arena's */ \ + MI_STAT_COUNTER(malloc_normal_count) /* number of blocks <= MI_LARGE_OBJ_SIZE_MAX */ \ + MI_STAT_COUNTER(malloc_huge_count) /* number of huge bloks */ \ + MI_STAT_COUNTER(malloc_guarded_count) /* number of allocations with guard pages */ \ + \ + /* internal statistics */ \ + MI_STAT_COUNTER(arena_rollback_count) \ + MI_STAT_COUNTER(arena_purges) \ + MI_STAT_COUNTER(pages_extended) /* number of page extensions */ \ + MI_STAT_COUNTER(pages_retire) /* number of pages that are retired */ \ + MI_STAT_COUNTER(page_searches) /* total pages searched for a fresh page */ \ + MI_STAT_COUNTER(page_searches_count) /* searched count for a fresh page */ \ + /* only on v1 and v2 */ \ + MI_STAT_COUNT(segments) \ + MI_STAT_COUNT(segments_abandoned) \ + MI_STAT_COUNT(segments_cache) \ + MI_STAT_COUNT(_segments_reserved) \ + /* only on v3 */ \ + MI_STAT_COUNT(heaps) \ + MI_STAT_COUNT(theaps) \ + MI_STAT_COUNTER(pages_reclaim_on_alloc) \ + MI_STAT_COUNTER(pages_reclaim_on_free) \ + MI_STAT_COUNTER(pages_reabandon_full) \ + MI_STAT_COUNTER(pages_unabandon_busy_wait) \ + MI_STAT_COUNTER(heaps_delete_wait) + +// Size bins for chunks +typedef enum mi_chunkbin_e { + MI_CBIN_SMALL, // slice_count == 1 + MI_CBIN_OTHER, // slice_count: any other from the other bins, and 1 <= slice_count <= MI_BCHUNK_BITS + MI_CBIN_MEDIUM, // slice_count == 8 + MI_CBIN_LARGE, // slice_count == MI_SIZE_BITS (only used if MI_ENABLE_LARGE_PAGES is 1) + MI_CBIN_HUGE, // slice_count > MI_BCHUNK_BITS + MI_CBIN_NONE, // no bin assigned yet (the chunk is completely free) + MI_CBIN_COUNT +} mi_chunkbin_t; + + +// Define the statistics structure +#define MI_BIN_HUGE (73U) // see types.h +#define MI_STAT_COUNT(stat) mi_stat_count_t stat; +#define MI_STAT_COUNTER(stat) mi_stat_counter_t stat; + +typedef struct mi_stats_s +{ + size_t size; // size of the mi_stats_t structure + size_t version; + + mi_decl_align(8) MI_STAT_FIELDS() + + // future extension + mi_stat_count_t _stat_reserved[4]; + mi_stat_counter_t _stat_counter_reserved[4]; + + // size segregated statistics + mi_stat_count_t malloc_bins[MI_BIN_HUGE+1]; // allocation per size bin + mi_stat_count_t page_bins[MI_BIN_HUGE+1]; // pages allocated per size bin + mi_stat_count_t chunk_bins[MI_CBIN_COUNT]; // chunks per page sizes +} mi_stats_t; + +#undef MI_STAT_COUNT +#undef MI_STAT_COUNTER + +// Initialization +static inline void mi_stats_header_init(mi_stats_t* stats) { + stats->size = sizeof(*stats); + stats->version = MI_STAT_VERSION; +} +static inline void mi_stats_init(mi_stats_t* stats) { + memset(stats,0,sizeof(*stats)); + mi_stats_header_init(stats); +} + +#define mi_stats_t_decl(name) mi_stats_t name; mi_stats_init(&name); + +// Exported definitions +#ifdef __cplusplus +extern "C" { +#endif + +// stats from a heap +mi_decl_export bool mi_heap_stats_get(mi_heap_t* heap, mi_stats_t* stats) mi_attr_noexcept; +mi_decl_export char* mi_heap_stats_get_json(mi_heap_t* heap, size_t buf_size, char* buf) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL +mi_decl_export void mi_heap_stats_print_out(mi_heap_t* heap, mi_output_fun* out, void* arg) mi_attr_noexcept; + +// stats from a subprocess and its heaps aggregated +mi_decl_export bool mi_subproc_stats_get(mi_subproc_id_t subproc_id, mi_stats_t* stats) mi_attr_noexcept; +mi_decl_export char* mi_subproc_stats_get_json(mi_subproc_id_t subproc_id, size_t buf_size, char* buf) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL +mi_decl_export void mi_subproc_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg) mi_attr_noexcept; +// print subprocess and all its heap stats segregated +mi_decl_export void mi_subproc_heap_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg) mi_attr_noexcept; + +// stats aggregated for the current subprocess and all its heaps. +mi_decl_export bool mi_stats_get(mi_stats_t* stats) mi_attr_noexcept; +mi_decl_export char* mi_stats_get_json(size_t buf_size, char* buf) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL +mi_decl_export void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; + +// add the stats of the heap to the subprocess and clear the heap stats +mi_decl_export void mi_heap_stats_merge_to_subproc(mi_heap_t* heap); + +// stats from the subprocess without aggregating its heaps +mi_decl_export bool mi_subproc_stats_get_exclusive(mi_subproc_id_t subproc_id, mi_stats_t* stats) mi_attr_noexcept; + +mi_decl_export char* mi_stats_as_json(mi_stats_t* stats, size_t buf_size, char* buf) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL +mi_decl_export size_t mi_stats_get_bin_size(size_t bin) mi_attr_noexcept; + +#ifdef __cplusplus +} +#endif + +#endif // MIMALLOC_STATS_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc.h index c41bcc80..14c06c07 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2026, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_H #define MIMALLOC_H -#define MI_MALLOC_VERSION 217 // major + 2 digits minor +#define MI_MALLOC_VERSION 30401 // major + 2 digits minor + 2 digits patch // ------------------------------------------------------ // Compiler specific attributes @@ -95,9 +95,8 @@ terms of the MIT license. A copy of the license can be found in the file // Includes // ------------------------------------------------------ -#include // size_t +#include // size_t, wchar_t #include // bool -#include // INTPTR_MAX #ifdef __cplusplus extern "C" { @@ -115,10 +114,10 @@ mi_decl_export void* mi_expand(void* p, size_t newsize) mi_decl_export void mi_free(void* p) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept mi_attr_malloc; mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept mi_attr_malloc; -mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept mi_attr_malloc; +mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept; // ------------------------------------------------------ -// Extended functionality +// Extended allocation functions // ------------------------------------------------------ #define MI_SMALL_WSIZE_MAX (128) #define MI_SMALL_SIZE_MAX (MI_SMALL_WSIZE_MAX*sizeof(void*)) @@ -134,9 +133,48 @@ mi_decl_nodiscard mi_decl_export void* mi_reallocf(void* p, size_t newsize) mi_decl_nodiscard mi_decl_export size_t mi_usable_size(const void* p) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export size_t mi_good_size(size_t size) mi_attr_noexcept; +// `mi_free_small` is for special applications like language runtimes. +// it should only be used to free objects from `mi_(heap_)(m|z)alloc_small` and is potentially a tiny bit faster than `mi_free` +mi_decl_export void mi_free_small(void* p) mi_attr_noexcept; + +// ------------------------------------------------------------------------------------- +// Aligned allocation +// Note that `alignment` always follows `size` for consistency with unaligned +// allocation, but unfortunately this differs from `posix_memalign` and `aligned_alloc`. +// ------------------------------------------------------------------------------------- + +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1, 2) mi_attr_alloc_align(3); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1, 2); +mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(2) mi_attr_alloc_align(3); +mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(2); + + +// ------------------------------------------------------ +// Typed allocation, the type is always the first parameter +// ------------------------------------------------------ + +#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp))) +#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp))) +#define mi_calloc_tp(tp,n) ((tp*)mi_calloc(n,sizeof(tp))) +#define mi_mallocn_tp(tp,n) ((tp*)mi_mallocn(n,sizeof(tp))) +#define mi_reallocn_tp(tp,p,n) ((tp*)mi_reallocn(p,n,sizeof(tp))) +#define mi_recalloc_tp(tp,p,n) ((tp*)mi_recalloc(p,n,sizeof(tp))) + +#define mi_heap_malloc_tp(tp,hp) ((tp*)mi_heap_malloc(hp,sizeof(tp))) +#define mi_heap_zalloc_tp(tp,hp) ((tp*)mi_heap_zalloc(hp,sizeof(tp))) +#define mi_heap_calloc_tp(tp,hp,n) ((tp*)mi_heap_calloc(hp,n,sizeof(tp))) +#define mi_heap_mallocn_tp(tp,hp,n) ((tp*)mi_heap_mallocn(hp,n,sizeof(tp))) +#define mi_heap_reallocn_tp(tp,hp,p,n) ((tp*)mi_heap_reallocn(hp,p,n,sizeof(tp))) +#define mi_heap_recalloc_tp(tp,hp,p,n) ((tp*)mi_heap_recalloc(hp,p,n,sizeof(tp))) + // ------------------------------------------------------ // Internals +// See also `mimalloc-stats.h` for statistics // ------------------------------------------------------ typedef void (mi_cdecl mi_deferred_free_fun)(bool force, unsigned long long heartbeat, void* arg); @@ -148,66 +186,75 @@ mi_decl_export void mi_register_output(mi_output_fun* out, void* arg) mi_attr_no typedef void (mi_cdecl mi_error_fun)(int err, void* arg); mi_decl_export void mi_register_error(mi_error_fun* fun, void* arg); -mi_decl_export void mi_collect(bool force) mi_attr_noexcept; -mi_decl_export int mi_version(void) mi_attr_noexcept; -mi_decl_export void mi_stats_reset(void) mi_attr_noexcept; -mi_decl_export void mi_stats_merge(void) mi_attr_noexcept; -mi_decl_export void mi_stats_print(void* out) mi_attr_noexcept; // backward compatibility: `out` is ignored and should be NULL -mi_decl_export void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; +mi_decl_export void mi_collect(bool force) mi_attr_noexcept; +mi_decl_export int mi_version(void) mi_attr_noexcept; +mi_decl_export void mi_options_print(void) mi_attr_noexcept; +mi_decl_export void mi_process_info_print(void) mi_attr_noexcept; +mi_decl_export void mi_options_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; +mi_decl_export void mi_process_info_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; +mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, + size_t* current_rss, size_t* peak_rss, + size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept; + + +// Generally do not use the following as these are usually called automatically mi_decl_export void mi_process_init(void) mi_attr_noexcept; +mi_decl_export void mi_cdecl mi_process_done(void) mi_attr_noexcept; mi_decl_export void mi_thread_init(void) mi_attr_noexcept; mi_decl_export void mi_thread_done(void) mi_attr_noexcept; -mi_decl_export void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; +mi_decl_export void mi_thread_set_in_threadpool(void) mi_attr_noexcept; // communicate that a thread is in a threadpool -mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, - size_t* current_rss, size_t* peak_rss, - size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept; -// ------------------------------------------------------------------------------------- -// Aligned allocation -// Note that `alignment` always follows `size` for consistency with unaligned -// allocation, but unfortunately this differs from `posix_memalign` and `aligned_alloc`. -// ------------------------------------------------------------------------------------- +// ----------------------------------------------------------------- +// Return allocated block size (if the return value is not NULL) +// ----------------------------------------------------------------- -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2) mi_attr_alloc_align(3); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2); -mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(2) mi_attr_alloc_align(3); -mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_umalloc(size_t size, size_t* block_size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_ucalloc(size_t count, size_t size, size_t* block_size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2); +mi_decl_nodiscard mi_decl_export void* mi_urealloc(void* p, size_t newsize, size_t* block_size_pre, size_t* block_size_post) mi_attr_noexcept mi_attr_alloc_size(2); +mi_decl_export void mi_ufree(void* p, size_t* block_size) mi_attr_noexcept; + +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_umalloc_aligned(size_t size, size_t alignment, size_t* block_size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_uzalloc_aligned(size_t size, size_t alignment, size_t* block_size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); + +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_umalloc_small(size_t size, size_t* block_size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_uzalloc_small(size_t size, size_t* block_size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); // ------------------------------------------------------------------------------------- -// Heaps: first-class, but can only allocate from the same thread that created it. +// Heaps: first-class. Can allocate from any thread (and be free'd from any thread) +// Heaps keep allocations in separate pages from each other (but share the arena's and free'd pages) // ------------------------------------------------------------------------------------- struct mi_heap_s; typedef struct mi_heap_s mi_heap_t; mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new(void); -mi_decl_export void mi_heap_delete(mi_heap_t* heap); -mi_decl_export void mi_heap_destroy(mi_heap_t* heap); -mi_decl_export mi_heap_t* mi_heap_set_default(mi_heap_t* heap); -mi_decl_export mi_heap_t* mi_heap_get_default(void); -mi_decl_export mi_heap_t* mi_heap_get_backing(void); -mi_decl_export void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept; - -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3); +mi_decl_export void mi_heap_delete(mi_heap_t* heap); // move live blocks to the main heap +mi_decl_export void mi_heap_destroy(mi_heap_t* heap); // free all live blocks +mi_decl_export void mi_heap_set_numa_affinity(mi_heap_t* heap, int numa_node); +mi_decl_export void mi_heap_collect(mi_heap_t* heap, bool force); + +mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_main(void); +mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_of(const void* p); +mi_decl_nodiscard mi_decl_export bool mi_heap_contains(const mi_heap_t* heap, const void* p); +mi_decl_nodiscard mi_decl_export bool mi_any_heap_contains(const void* p); + +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc(mi_heap_t* theap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); mi_decl_nodiscard mi_decl_export void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3); -mi_decl_nodiscard mi_decl_export void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(3,4); -mi_decl_nodiscard mi_decl_export void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3); +mi_decl_nodiscard mi_decl_export void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(3, 4); +mi_decl_nodiscard mi_decl_export void* mi_heap_reallocf(mi_heap_t* theap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3); mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept mi_attr_malloc; mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept mi_attr_malloc; -mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept mi_attr_malloc; +mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(3); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); @@ -235,81 +282,152 @@ mi_decl_nodiscard mi_decl_export void* mi_recalloc_aligned(void* p, size_t newco mi_decl_nodiscard mi_decl_export void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size2(2,3); mi_decl_nodiscard mi_decl_export void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3); -mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t newcount, size_t size) mi_attr_noexcept mi_attr_alloc_size2(3,4); +mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t newcount, size_t size) mi_attr_noexcept mi_attr_alloc_size2(3, 4); mi_decl_nodiscard mi_decl_export void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(3) mi_attr_alloc_align(4); mi_decl_nodiscard mi_decl_export void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(3); -mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept mi_attr_alloc_size2(3,4) mi_attr_alloc_align(5); -mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size2(3,4); +mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept mi_attr_alloc_size2(3, 4) mi_attr_alloc_align(5); +mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size2(3, 4); + // ------------------------------------------------------ -// Analysis +// Visiting pages and individual blocks in a heap. // ------------------------------------------------------ -mi_decl_export bool mi_heap_contains_block(mi_heap_t* heap, const void* p); -mi_decl_export bool mi_heap_check_owned(mi_heap_t* heap, const void* p); -mi_decl_export bool mi_check_owned(const void* p); - // An area of heap space contains blocks of a single size. typedef struct mi_heap_area_s { - void* blocks; // start of the area containing heap blocks + void* blocks; // start of the area containing theap blocks size_t reserved; // bytes reserved for this area (virtual) size_t committed; // current available bytes for this area size_t used; // number of allocated blocks size_t block_size; // size in bytes of each block size_t full_block_size; // size in bytes of a full block including padding and metadata. + void* reserved1; // internal } mi_heap_area_t; typedef bool (mi_cdecl mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); -mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg); +mi_decl_export bool mi_heap_visit_blocks(mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg); +mi_decl_export bool mi_heap_visit_abandoned_blocks(mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg); + + +// ------------------------------------------------------ +// Arena memory management +// Arena's are larger memory area's provided by the OS or user +// ------------------------------------------------------ -// Experimental -mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export bool mi_is_redirected(void) mi_attr_noexcept; -mi_decl_export int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept; -mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept; +mi_decl_export int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept; +mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept; -mi_decl_export int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept; -mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept; +mi_decl_export int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept; +mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_pinned /* cannot decommit/reset? */, bool is_zero, int numa_node) mi_attr_noexcept; -mi_decl_export void mi_debug_show_arenas(bool show_inuse, bool show_abandoned, bool show_purge) mi_attr_noexcept; +mi_decl_export void mi_debug_show_arenas(void) mi_attr_noexcept; +mi_decl_export void mi_arenas_print(void) mi_attr_noexcept; +mi_decl_export size_t mi_arena_min_alignment(void); +mi_decl_export size_t mi_arena_min_size(void); -// Experimental: heaps associated with specific memory arena's -typedef int mi_arena_id_t; -mi_decl_export void* mi_arena_area(mi_arena_id_t arena_id, size_t* size); -mi_decl_export int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; -mi_decl_export int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; -mi_decl_export bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +typedef void* mi_arena_id_t; +mi_decl_export void* mi_arena_area(mi_arena_id_t arena_id, size_t* size); +mi_decl_export int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_pinned, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export bool mi_arena_contains(mi_arena_id_t arena_id, const void* p); -#if MI_MALLOC_VERSION >= 182 // Create a heap that only allocates in the specified arena mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t arena_id); -#endif -// deprecated -mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept; + +// ------------------------------------------------------ +// Subprocesses +// Advanced: allow sub-processes whose memory arena's stay fully separated (and no reclamation between them). +// Used for example for separate interpreters in one process. +// ------------------------------------------------------ + +typedef struct { void* _mi_subproc_id; } mi_subproc_id_t; // abstract type +mi_decl_export mi_subproc_id_t mi_subproc_main(void); +mi_decl_export mi_subproc_id_t mi_subproc_current(void); +mi_decl_export mi_subproc_id_t mi_subproc_new(void); +mi_decl_export void mi_subproc_destroy(mi_subproc_id_t subproc); +mi_decl_export void mi_subproc_add_current_thread(mi_subproc_id_t subproc); // this should be called right after a thread is created (and no allocation has taken place yet) + +typedef bool (mi_cdecl mi_heap_visit_fun)(mi_heap_t* heap, void* arg); +mi_decl_export bool mi_subproc_visit_heaps(mi_subproc_id_t subproc, mi_heap_visit_fun* visitor, void* arg); + + +// ------------------------------------------------------------------------------------- +// A "theap" is a thread-local heap. This API is only provided for special circumstances like runtimes +// that already have a thread-local context and can store the theap there for (slightly) faster allocations. +// This also allows to set a default theap for the current thread so that `malloc` etc. allocate from +// that theap (instead of the main (t)heap). +// Theaps are first-class, but can only allocate from the same thread that created it. +// Allocation through a `theap` may be a tiny bit faster than using plain malloc +// (as we don't need to lookup the thread local variable). +// ------------------------------------------------------------------------------------- + +struct mi_theap_s; +typedef struct mi_theap_s mi_theap_t; + +mi_decl_export mi_theap_t* mi_heap_theap(mi_heap_t* heap); +mi_decl_export mi_theap_t* mi_theap_set_default(mi_theap_t* theap); +mi_decl_export mi_theap_t* mi_theap_get_default(void); +mi_decl_export void mi_theap_collect(mi_theap_t* theap, bool force) mi_attr_noexcept; + +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_theap_malloc(mi_theap_t* theap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_theap_zalloc(mi_theap_t* theap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_theap_calloc(mi_theap_t* theap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_theap_malloc_small(mi_theap_t* theap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_theap_zalloc_small(mi_theap_t* theap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_theap_malloc_aligned(mi_theap_t* theap, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(3); +mi_decl_nodiscard mi_decl_export void* mi_theap_realloc(mi_theap_t* theap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3); // ------------------------------------------------------ -// Convenience +// Experimental // ------------------------------------------------------ -#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp))) -#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp))) -#define mi_calloc_tp(tp,n) ((tp*)mi_calloc(n,sizeof(tp))) -#define mi_mallocn_tp(tp,n) ((tp*)mi_mallocn(n,sizeof(tp))) -#define mi_reallocn_tp(p,tp,n) ((tp*)mi_reallocn(p,n,sizeof(tp))) -#define mi_recalloc_tp(p,tp,n) ((tp*)mi_recalloc(p,n,sizeof(tp))) +// Experimental: objects followed by a guard page. +// Setting the sample rate on a specific theap can be used to test parts of the program more +// specifically (in combination with `mi_theap_set_default`). +// A sample rate of 0 disables guarded objects, while 1 uses a guard page for every object. +// A seed of 0 uses a random start point. Only objects within the size bound are eligable for guard pages. +mi_decl_export void mi_theap_guarded_set_sample_rate(mi_theap_t* theap, size_t sample_rate, size_t seed); +mi_decl_export void mi_theap_guarded_set_size_bound(mi_theap_t* theap, size_t min, size_t max); + +// very experimental +typedef bool (mi_cdecl mi_commit_fun_t)(bool commit, void* start, size_t size, bool* is_zero, void* user_arg); +mi_decl_export bool mi_manage_memory(void* start, size_t size, bool is_committed, bool is_pinned, bool is_zero, int numa_node, bool exclusive, + mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept; + +//mi_decl_export bool mi_arena_unload(mi_arena_id_t arena_id, void** base, size_t* accessed_size, size_t* size); +//mi_decl_export bool mi_arena_reload(void* start, size_t size, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id); +//mi_decl_export bool mi_theap_reload(mi_theap_t* theap, mi_arena_id_t arena); +//mi_decl_export void mi_theap_unload(mi_theap_t* theap); + +// unsafe: assumes the page belonging to `p` is only accessed by the calling thread. +mi_decl_export bool mi_unsafe_heap_page_is_under_utilized(mi_heap_t* heap, void* p, size_t perc_threshold) mi_attr_noexcept; + +// ------------------------------------------------------ +// Deprecated +// ------------------------------------------------------ + +mi_decl_export bool mi_check_owned(const void* p); + +mi_decl_export void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; +mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept; +mi_decl_export bool mi_theap_visit_blocks(const mi_theap_t* theap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg); -#define mi_heap_malloc_tp(hp,tp) ((tp*)mi_heap_malloc(hp,sizeof(tp))) -#define mi_heap_zalloc_tp(hp,tp) ((tp*)mi_heap_zalloc(hp,sizeof(tp))) -#define mi_heap_calloc_tp(hp,tp,n) ((tp*)mi_heap_calloc(hp,n,sizeof(tp))) -#define mi_heap_mallocn_tp(hp,tp,n) ((tp*)mi_heap_mallocn(hp,n,sizeof(tp))) -#define mi_heap_reallocn_tp(hp,p,tp,n) ((tp*)mi_heap_reallocn(hp,p,n,sizeof(tp))) -#define mi_heap_recalloc_tp(hp,p,tp,n) ((tp*)mi_heap_recalloc(hp,p,n,sizeof(tp))) +mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept; +mi_decl_export void mi_collect_reduce(size_t target_thread_owned) mi_attr_noexcept; + +mi_decl_export void mi_stats_reset(void) mi_attr_noexcept; +mi_decl_export void mi_stats_merge(void) mi_attr_noexcept; +mi_decl_export void mi_stats_print(void* out) mi_attr_noexcept; // backward compatibility: `out` is ignored and should be NULL + +mi_decl_export void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; // not deprecated but declared in `mimalloc-stats.h` now. // ------------------------------------------------------ @@ -322,39 +440,56 @@ typedef enum mi_option_e { mi_option_show_stats, // print statistics on termination mi_option_verbose, // print verbose messages // advanced options - mi_option_eager_commit, // eager commit segments? (after `eager_commit_delay` segments) (=1) + mi_option_deprecated_eager_commit, mi_option_arena_eager_commit, // eager commit arenas? Use 2 to enable just on overcommit systems (=2) mi_option_purge_decommits, // should a memory purge decommit? (=1). Set to 0 to use memory reset on a purge (instead of decommit) - mi_option_allow_large_os_pages, // allow large (2 or 4 MiB) OS pages, implies eager commit. If false, also disables THP for the process. + mi_option_allow_large_os_pages, // allow use of large (2 or 4 MiB) OS pages, implies eager commit. mi_option_reserve_huge_os_pages, // reserve N huge OS pages (1GiB pages) at startup mi_option_reserve_huge_os_pages_at, // reserve huge OS pages at a specific NUMA node mi_option_reserve_os_memory, // reserve specified amount of OS memory in an arena at startup (internally, this value is in KiB; use `mi_option_get_size`) mi_option_deprecated_segment_cache, mi_option_deprecated_page_reset, - mi_option_abandoned_page_purge, // immediately purge delayed purges on thread termination - mi_option_deprecated_segment_reset, - mi_option_eager_commit_delay, // the first N segments per thread are not eagerly committed (but per page in the segment on demand) + mi_option_deprecated_abandoned_page_purge, + mi_option_deprecated_segment_reset, + mi_option_deprecated_eager_commit_delay, mi_option_purge_delay, // memory purging is delayed by N milli seconds; use 0 for immediate purging or -1 for no purging at all. (=10) mi_option_use_numa_nodes, // 0 = use all available numa nodes, otherwise use at most N nodes. mi_option_disallow_os_alloc, // 1 = do not use OS memory for allocation (but only programmatically reserved arenas) mi_option_os_tag, // tag used for OS logging (macOS only for now) (=100) mi_option_max_errors, // issue at most N error messages mi_option_max_warnings, // issue at most N warning messages - mi_option_max_segment_reclaim, // max. percentage of the abandoned segments can be reclaimed per try (=10%) + mi_option_deprecated_max_segment_reclaim, // max. percentage of the abandoned segments can be reclaimed per try (=10%) mi_option_destroy_on_exit, // if set, release all memory on exit; sometimes used for dynamic unloading but can be unsafe mi_option_arena_reserve, // initial memory size for arena reservation (= 1 GiB on 64-bit) (internally, this value is in KiB; use `mi_option_get_size`) mi_option_arena_purge_mult, // multiplier for `purge_delay` for the purging delay for arenas (=10) - mi_option_purge_extend_delay, - mi_option_abandoned_reclaim_on_free, // allow to reclaim an abandoned segment on a free (=1) + mi_option_deprecated_purge_extend_delay, mi_option_disallow_arena_alloc, // 1 = do not use arena's for allocation (except if using specific arena id's) mi_option_retry_on_oom, // retry on out-of-memory for N milli seconds (=400), set to 0 to disable retries. (only on windows) + mi_option_visit_abandoned, // allow visiting theap blocks from abandoned threads (=0) + mi_option_guarded_min, // only used when building with MI_GUARDED: minimal rounded object size for guarded objects (=0) + mi_option_guarded_max, // only used when building with MI_GUARDED: maximal rounded object size for guarded objects (=0) + mi_option_guarded_precise, // disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0) + mi_option_guarded_sample_rate, // 1 out of N allocations in the min/max range will be guarded (=1000) + mi_option_guarded_sample_seed, // can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0) + mi_option_generic_collect, // collect theaps every N (=10000) generic allocation calls + mi_option_page_reclaim_on_free, // reclaim abandoned pages on a free (=0). -1 disallowr always, 0 allows if the page originated from the current theap, 1 allow always + mi_option_page_full_retain, // retain N full (small) pages per size class (=2) + mi_option_page_max_candidates, // max candidate pages to consider for allocation (=4) + mi_option_max_vabits, // max user space virtual address bits to consider (=48) + mi_option_pagemap_commit, // commit the full pagemap (to always catch invalid pointer uses) (=0) + mi_option_page_commit_on_demand, // commit page memory on-demand + mi_option_page_max_reclaim, // don't reclaim pages of the same originating theap if we already own N pages (in that size class) (=-1 (unlimited)) + mi_option_page_cross_thread_max_reclaim, // don't reclaim pages across threads if we already own N pages (in that size class) (=16) + mi_option_allow_thp, // allow transparent huge pages? (=1) (on Android =0 by default). Set to 0 to disable THP for the process. + mi_option_minimal_purge_size, // set minimal purge size (in KiB) (=0). By default set to either 64 or 2048 if THP is enabled. + mi_option_arena_max_object_size, // set maximal object size that can be allocated in an arena (in KiB) (=2GiB on 64-bit). + mi_option_arena_is_numa_local, // experimental _mi_option_last, // legacy option names mi_option_large_os_pages = mi_option_allow_large_os_pages, mi_option_eager_region_commit = mi_option_arena_eager_commit, mi_option_reset_decommits = mi_option_purge_decommits, mi_option_reset_delay = mi_option_purge_delay, - mi_option_abandoned_page_reset = mi_option_abandoned_page_purge, mi_option_limit_os_alloc = mi_option_disallow_os_alloc } mi_option_t; @@ -375,7 +510,7 @@ mi_decl_export void mi_option_set_default(mi_option_t option, long value); // ------------------------------------------------------------------------------------------------------- // "mi" prefixed implementations of various posix, Unix, Windows, and C++ allocation functions. // (This can be convenient when providing overrides of these functions as done in `mimalloc-override.h`.) -// note: we use `mi_cfree` as "checked free" and it checks if the pointer is in our heap before free-ing. +// note: we use `mi_cfree` as "checked free" and it checks if the pointer is in our theap before free-ing. // ------------------------------------------------------------------------------------------------------- mi_decl_export void mi_cfree(void* p) mi_attr_noexcept; @@ -384,25 +519,26 @@ mi_decl_nodiscard mi_decl_export size_t mi_malloc_size(const void* p) mi_ mi_decl_nodiscard mi_decl_export size_t mi_malloc_good_size(size_t size) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept; -mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept; +mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size); // mi_attr_noexcept; mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(1); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(1); mi_decl_nodiscard mi_decl_export void* mi_reallocarray(void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(2,3); -mi_decl_nodiscard mi_decl_export int mi_reallocarr(void* p, size_t count, size_t size) mi_attr_noexcept; +mi_decl_nodiscard mi_decl_export int mi_reallocarr(void* ptrp, size_t count, size_t size) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept; -mi_decl_nodiscard mi_decl_export mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept mi_attr_malloc; -mi_decl_nodiscard mi_decl_export mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept mi_attr_malloc; -mi_decl_export int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept; -mi_decl_export int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept; - mi_decl_export void mi_free_size(void* p, size_t size) mi_attr_noexcept; mi_decl_export void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept; mi_decl_export void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept; +mi_decl_export int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept; + +// wide characters +mi_decl_export int mi_wdupenv_s(wchar_t** buf, size_t* size, const wchar_t* name) mi_attr_noexcept; +mi_decl_nodiscard mi_decl_export mi_decl_restrict wchar_t* mi_wcsdup(const wchar_t* s) mi_attr_noexcept mi_attr_malloc; +mi_decl_nodiscard mi_decl_export mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept mi_attr_malloc; // The `mi_new` wrappers implement C++ semantics on out-of-memory instead of directly returning `NULL`. // (and call `std::get_new_handler` and potentially raise a `std::bad_alloc` exception). @@ -414,7 +550,7 @@ mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new_n(size_t count, s mi_decl_nodiscard mi_decl_export void* mi_new_realloc(void* p, size_t newsize) mi_attr_alloc_size(2); mi_decl_nodiscard mi_decl_export void* mi_new_reallocn(void* p, size_t newcount, size_t size) mi_attr_alloc_size2(2, 3); -mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) mi_attr_malloc mi_attr_alloc_size(2); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) mi_attr_malloc mi_attr_alloc_size2(2, 3); #ifdef __cplusplus @@ -492,7 +628,7 @@ template bool operator!=(const mi_stl_allocator& , const #include // std::shared_ptr -// Common base class for STL allocators in a specific heap +// Common base class for STL allocators in a specific theap template struct _mi_heap_stl_allocator_common : public _mi_stl_allocator_common { using typename _mi_stl_allocator_common::size_type; using typename _mi_stl_allocator_common::value_type; diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/atomic.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/atomic.h index d5333dd9..e8960293 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/atomic.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/atomic.h @@ -1,17 +1,28 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023 Microsoft Research, Daan Leijen +Copyright (c) 2018-2024 Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #pragma once -#ifndef MIMALLOC_ATOMIC_H -#define MIMALLOC_ATOMIC_H +#ifndef MI_ATOMIC_H +#define MI_ATOMIC_H + +// include windows.h or pthreads.h +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#elif !defined(__wasi__) && (!defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__)) +#define MI_USE_PTHREADS +#include +#endif // -------------------------------------------------------------------------------------------- // Atomics // We need to be portable between C, C++, and MSVC. -// We base the primitives on the C/C++ atomics and create a mimimal wrapper for MSVC in C compilation mode. +// We base the primitives on the C/C++ atomics and create a minimal wrapper for MSVC in C compilation mode. // This is why we try to use only `uintptr_t` and `*` as atomic types. // To gain better insight in the range of used atomics, we use explicitly named memory order operations // instead of passing the memory order as a parameter. @@ -20,33 +31,33 @@ terms of the MIT license. A copy of the license can be found in the file #if defined(__cplusplus) // Use C++ atomics #include -#define _Atomic(tp) std::atomic -#define mi_atomic(name) std::atomic_##name -#define mi_memory_order(name) std::memory_order_##name -#if (__cplusplus >= 202002L) // c++20, see issue #571 -#define MI_ATOMIC_VAR_INIT(x) x +#define _Atomic(tp) std::atomic +#define mi_atomic(name) std::atomic_##name +#define mi_memory_order(name) std::memory_order_##name +#if (__cplusplus >= 202002L) // c++20, see issue #571 + #define MI_ATOMIC_VAR_INIT(x) x #elif !defined(ATOMIC_VAR_INIT) -#define MI_ATOMIC_VAR_INIT(x) x + #define MI_ATOMIC_VAR_INIT(x) x #else - #define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) + #define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) #endif #elif defined(_MSC_VER) // Use MSVC C wrapper for C11 atomics -#define _Atomic(tp) tp -#define MI_ATOMIC_VAR_INIT(x) x -#define mi_atomic(name) mi_atomic_##name -#define mi_memory_order(name) mi_memory_order_##name +#define _Atomic(tp) tp +#define MI_ATOMIC_VAR_INIT(x) x +#define mi_atomic(name) mi_atomic_##name +#define mi_memory_order(name) mi_memory_order_##name #else // Use C11 atomics #include -#define mi_atomic(name) atomic_##name -#define mi_memory_order(name) memory_order_##name +#define mi_atomic(name) atomic_##name +#define mi_memory_order(name) memory_order_##name #if (__STDC_VERSION__ >= 201710L) // c17, see issue #735 - #define MI_ATOMIC_VAR_INIT(x) x + #define MI_ATOMIC_VAR_INIT(x) x #elif !defined(ATOMIC_VAR_INIT) - #define MI_ATOMIC_VAR_INIT(x) x + #define MI_ATOMIC_VAR_INIT(x) x #else - #define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) + #define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) #endif #endif @@ -61,18 +72,24 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_atomic_load_relaxed(p) mi_atomic(load_explicit)(p,mi_memory_order(relaxed)) #define mi_atomic_store_release(p,x) mi_atomic(store_explicit)(p,x,mi_memory_order(release)) #define mi_atomic_store_relaxed(p,x) mi_atomic(store_explicit)(p,x,mi_memory_order(relaxed)) +#define mi_atomic_exchange_relaxed(p,x) mi_atomic(exchange_explicit)(p,x,mi_memory_order(relaxed)) #define mi_atomic_exchange_release(p,x) mi_atomic(exchange_explicit)(p,x,mi_memory_order(release)) #define mi_atomic_exchange_acq_rel(p,x) mi_atomic(exchange_explicit)(p,x,mi_memory_order(acq_rel)) + +#define mi_atomic_cas_weak_relaxed(p,exp,des) mi_atomic_cas_weak(p,exp,des,mi_memory_order(relaxed),mi_memory_order(relaxed)) #define mi_atomic_cas_weak_release(p,exp,des) mi_atomic_cas_weak(p,exp,des,mi_memory_order(release),mi_memory_order(relaxed)) #define mi_atomic_cas_weak_acq_rel(p,exp,des) mi_atomic_cas_weak(p,exp,des,mi_memory_order(acq_rel),mi_memory_order(acquire)) +#define mi_atomic_cas_strong_relaxed(p,exp,des) mi_atomic_cas_strong(p,exp,des,mi_memory_order(relaxed),mi_memory_order(relaxed)) #define mi_atomic_cas_strong_release(p,exp,des) mi_atomic_cas_strong(p,exp,des,mi_memory_order(release),mi_memory_order(relaxed)) #define mi_atomic_cas_strong_acq_rel(p,exp,des) mi_atomic_cas_strong(p,exp,des,mi_memory_order(acq_rel),mi_memory_order(acquire)) #define mi_atomic_add_relaxed(p,x) mi_atomic(fetch_add_explicit)(p,x,mi_memory_order(relaxed)) -#define mi_atomic_sub_relaxed(p,x) mi_atomic(fetch_sub_explicit)(p,x,mi_memory_order(relaxed)) #define mi_atomic_add_acq_rel(p,x) mi_atomic(fetch_add_explicit)(p,x,mi_memory_order(acq_rel)) +#define mi_atomic_sub_relaxed(p,x) mi_atomic(fetch_sub_explicit)(p,x,mi_memory_order(relaxed)) #define mi_atomic_sub_acq_rel(p,x) mi_atomic(fetch_sub_explicit)(p,x,mi_memory_order(acq_rel)) +#define mi_atomic_and_relaxed(p,x) mi_atomic(fetch_and_explicit)(p,x,mi_memory_order(relaxed)) #define mi_atomic_and_acq_rel(p,x) mi_atomic(fetch_and_explicit)(p,x,mi_memory_order(acq_rel)) +#define mi_atomic_or_relaxed(p,x) mi_atomic(fetch_or_explicit)(p,x,mi_memory_order(relaxed)) #define mi_atomic_or_acq_rel(p,x) mi_atomic(fetch_or_explicit)(p,x,mi_memory_order(acq_rel)) #define mi_atomic_increment_relaxed(p) mi_atomic_add_relaxed(p,(uintptr_t)1) @@ -80,7 +97,6 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_atomic_increment_acq_rel(p) mi_atomic_add_acq_rel(p,(uintptr_t)1) #define mi_atomic_decrement_acq_rel(p) mi_atomic_sub_acq_rel(p,(uintptr_t)1) -static inline void mi_atomic_yield(void); static inline intptr_t mi_atomic_addi(_Atomic(intptr_t)*p, intptr_t add); static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub); @@ -99,6 +115,8 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub); #define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release(p,exp,(tp*)des) #define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel(p,exp,(tp*)des) #define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release(p,exp,(tp*)des) +#define mi_atomic_cas_ptr_strong_acq_rel(tp,p,exp,des) mi_atomic_cas_strong_acq_rel(p,exp,(tp*)des) +#define mi_atomic_exchange_ptr_relaxed(tp,p,x) mi_atomic_exchange_relaxed(p,(tp*)x) #define mi_atomic_exchange_ptr_release(tp,p,x) mi_atomic_exchange_release(p,(tp*)x) #define mi_atomic_exchange_ptr_acq_rel(tp,p,x) mi_atomic_exchange_acq_rel(p,(tp*)x) #else @@ -107,6 +125,8 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub); #define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release(p,exp,des) #define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel(p,exp,des) #define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release(p,exp,des) +#define mi_atomic_cas_ptr_strong_acq_rel(tp,p,exp,des) mi_atomic_cas_strong_acq_rel(p,exp,des) +#define mi_atomic_exchange_ptr_relaxed(tp,p,x) mi_atomic_exchange_relaxed(p,x) #define mi_atomic_exchange_ptr_release(tp,p,x) mi_atomic_exchange_release(p,x) #define mi_atomic_exchange_ptr_acq_rel(tp,p,x) mi_atomic_exchange_acq_rel(p,x) #endif @@ -115,6 +135,12 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub); static inline int64_t mi_atomic_addi64_relaxed(volatile int64_t* p, int64_t add) { return mi_atomic(fetch_add_explicit)((_Atomic(int64_t)*)p, add, mi_memory_order(relaxed)); } +static inline void mi_atomic_void_addi64_relaxed(volatile int64_t* p, const volatile int64_t* padd) { + const int64_t add = mi_atomic_load_relaxed((_Atomic(int64_t)*)padd); + if (add != 0) { + mi_atomic(fetch_add_explicit)((_Atomic(int64_t)*)p, add, mi_memory_order(relaxed)); + } +} static inline void mi_atomic_maxi64_relaxed(volatile int64_t* p, int64_t x) { int64_t current = mi_atomic_load_relaxed((_Atomic(int64_t)*)p); while (current < x && !mi_atomic_cas_weak_release((_Atomic(int64_t)*)p, ¤t, x)) { /* nothing */ }; @@ -132,18 +158,18 @@ static inline void mi_atomic_maxi64_relaxed(volatile int64_t* p, int64_t x) { #elif defined(_MSC_VER) -// Legacy MSVC plain C compilation wrapper that uses Interlocked operations to model C11 atomics. -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include +// Deprecated: MSVC plain C compilation wrapper that uses Interlocked operations to model C11 atomics. +// It is recommended to always compile as C++ when using MSVC. + #include #ifdef _WIN64 -typedef LONG64 msc_intptr_t; -#define MI_64(f) f##64 +typedef LONG64 msc_intptr_t; +#define MI_MSC_64(f) f##64 +#define MI_MSC_XX(f) f##64 #else -typedef LONG msc_intptr_t; -#define MI_64(f) f +typedef LONG msc_intptr_t; +#define MI_MSC_64(f) f +#define MI_MSC_XX(f) f##32 #endif typedef enum mi_memory_order_e { @@ -157,23 +183,23 @@ typedef enum mi_memory_order_e { static inline uintptr_t mi_atomic_fetch_add_explicit(_Atomic(uintptr_t)*p, uintptr_t add, mi_memory_order mo) { (void)(mo); - return (uintptr_t)MI_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, (msc_intptr_t)add); + return (uintptr_t)MI_MSC_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, (msc_intptr_t)add); } static inline uintptr_t mi_atomic_fetch_sub_explicit(_Atomic(uintptr_t)*p, uintptr_t sub, mi_memory_order mo) { (void)(mo); - return (uintptr_t)MI_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, -((msc_intptr_t)sub)); + return (uintptr_t)MI_MSC_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, -((msc_intptr_t)sub)); } static inline uintptr_t mi_atomic_fetch_and_explicit(_Atomic(uintptr_t)*p, uintptr_t x, mi_memory_order mo) { (void)(mo); - return (uintptr_t)MI_64(_InterlockedAnd)((volatile msc_intptr_t*)p, (msc_intptr_t)x); + return (uintptr_t)MI_MSC_64(_InterlockedAnd)((volatile msc_intptr_t*)p, (msc_intptr_t)x); } static inline uintptr_t mi_atomic_fetch_or_explicit(_Atomic(uintptr_t)*p, uintptr_t x, mi_memory_order mo) { (void)(mo); - return (uintptr_t)MI_64(_InterlockedOr)((volatile msc_intptr_t*)p, (msc_intptr_t)x); + return (uintptr_t)MI_MSC_64(_InterlockedOr)((volatile msc_intptr_t*)p, (msc_intptr_t)x); } static inline bool mi_atomic_compare_exchange_strong_explicit(_Atomic(uintptr_t)*p, uintptr_t* expected, uintptr_t desired, mi_memory_order mo1, mi_memory_order mo2) { (void)(mo1); (void)(mo2); - uintptr_t read = (uintptr_t)MI_64(_InterlockedCompareExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)desired, (msc_intptr_t)(*expected)); + const uintptr_t read = (uintptr_t)MI_MSC_64(_InterlockedCompareExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)desired, (msc_intptr_t)(*expected)); if (read == *expected) { return true; } @@ -187,69 +213,128 @@ static inline bool mi_atomic_compare_exchange_weak_explicit(_Atomic(uintptr_t)*p } static inline uintptr_t mi_atomic_exchange_explicit(_Atomic(uintptr_t)*p, uintptr_t exchange, mi_memory_order mo) { (void)(mo); - return (uintptr_t)MI_64(_InterlockedExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)exchange); + return (uintptr_t)MI_MSC_64(_InterlockedExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)exchange); } static inline void mi_atomic_thread_fence(mi_memory_order mo) { (void)(mo); _Atomic(uintptr_t) x = 0; mi_atomic_exchange_explicit(&x, 1, mo); } + static inline uintptr_t mi_atomic_load_explicit(_Atomic(uintptr_t) const* p, mi_memory_order mo) { (void)(mo); -#if defined(_M_IX86) || defined(_M_X64) - return *p; -#else - uintptr_t x = *p; - if (mo > mi_memory_order_relaxed) { - while (!mi_atomic_compare_exchange_weak_explicit((_Atomic(uintptr_t)*)p, &x, x, mo, mi_memory_order_relaxed)) { /* nothing */ }; - } - return x; -#endif + // assert(mo<=mi_memory_order_acquire); // others are not used by mimalloc + #if defined(_M_IX86) || defined(_M_X64) + return (uintptr_t)MI_MSC_XX(__iso_volatile_load)((volatile const intptr_t*)p); + #elif defined(_M_ARM) || defined(_M_ARM64) + if (mo == mi_memory_order_relaxed) { + return (uintptr_t)MI_MSC_XX(__iso_volatile_load)((volatile const intptr_t*)p); + } + else if (mo <= mi_memory_order_acquire) { + return MI_MSC_XX(__ldar)((volatile const uintptr_t*)p); + } + else { + const uintptr_t u = (uintptr_t)MI_MSC_XX(__iso_volatile_load)((volatile const intptr_t*)p); + __dmb(15); // _ARM(64)_BARRIER_SY + return u; + } + #else + #warning "define mi_atomic_load_explicit for MSVC C compilation on this platform (which should be readonly, see issue #1277)" + return MI_MSC_XX(__iso_volatile_load)((volatile const intptr_t*)p); + #endif } static inline void mi_atomic_store_explicit(_Atomic(uintptr_t)*p, uintptr_t x, mi_memory_order mo) { (void)(mo); -#if defined(_M_IX86) || defined(_M_X64) - *p = x; -#else - mi_atomic_exchange_explicit(p, x, mo); -#endif + // assert(mo<=mi_memory_order_release); // others are not used by mimalloc + #if defined(_M_IX86) || defined(_M_X64) + MI_MSC_XX(__iso_volatile_store)((volatile intptr_t*)p, x); + #elif defined(_M_ARM) || defined(_M_ARM64) + if (mo == mi_memory_order_relaxed) { + MI_MSC_XX(__iso_volatile_store)((volatile intptr_t*)p, x); + } + else if (mo <= mi_memory_order_release) { + MI_MSC_XX(__stlr)((volatile uintptr_t*)p,x); + } + else { + mi_atomic_exchange_explicit(p, x, mo); + } + #else + mi_atomic_exchange_explicit(p, x, mo); + #endif } + static inline int64_t mi_atomic_loadi64_explicit(_Atomic(int64_t)*p, mi_memory_order mo) { (void)(mo); -#if defined(_M_X64) - return *p; -#else - int64_t old = *p; - int64_t x = old; - while ((old = InterlockedCompareExchange64(p, x, old)) != x) { - x = old; - } - return x; -#endif + // assert(mo<=mi_memory_order_acquire); // others are not used by mimalloc + #if defined(_M_IX86) || defined(_M_X64) + return __iso_volatile_load64((volatile const int64_t*)p); + #elif defined(_M_ARM) || defined(_M_ARM64) + if (mo == mi_memory_order_relaxed) { + return __iso_volatile_load64((volatile const int64_t*)p); + } + #if defined(_M_ARM64) + else if (mo <= mi_memory_order_acquire) { + return __ldar64((volatile const uintptr_t*)p); + } + #endif + else { + const int64_t i = __iso_volatile_load64((volatile const int64_t*)p); + __dmb(15); // _ARM(64)_BARRIER_SY + return i; + } + #else + #warning "define mi_atomic_loadi64_explicit for MSVC C compilation on this platform (which should be readonly, see issue #1277)" + return __iso_volatile_load64((volatile const int64_t*)p); + #endif } + static inline void mi_atomic_storei64_explicit(_Atomic(int64_t)*p, int64_t x, mi_memory_order mo) { (void)(mo); -#if defined(x_M_IX86) || defined(_M_X64) - *p = x; -#else - InterlockedExchange64(p, x); -#endif + // assert(mo<=mi_memory_order_release); // others are not used by mimalloc + #if defined(_M_IX86) || defined(_M_X64) + __iso_volatile_store64((volatile int64_t*)p,x); + #elif defined(_M_ARM) || defined(_M_ARM64) + if (mo == mi_memory_order_relaxed) { + __iso_volatile_store64((volatile int64_t*)p,x); + } + #if defined(_M_ARM64) + else if (mo == mi_memory_order_release) { + __stlr64((volatile uint64_t*)p, (uint64_t)x); + } + #endif + else { + InterlockedExchange64(p, x); + } + #else + InterlockedExchange64(p, x); + #endif } // These are used by the statistics static inline int64_t mi_atomic_addi64_relaxed(volatile _Atomic(int64_t)*p, int64_t add) { -#ifdef _WIN64 - return (int64_t)mi_atomic_addi((int64_t*)p, add); -#else - int64_t current; - int64_t sum; - do { - current = *p; - sum = current + add; - } while (_InterlockedCompareExchange64(p, sum, current) != current); - return current; -#endif + #ifdef _WIN64 + return (int64_t)mi_atomic_addi((int64_t*)p, add); + #elif defined(_M_ARM) + return _InterlockedExchangeAdd64(p, add); + #else + // x86 + int64_t current; + int64_t sum; + do { + current = __iso_volatile_load64((volatile const int64_t*)p); + sum = current + add; + } while (_InterlockedCompareExchange64(p, sum, current) != current); + return current; + #endif +} + +static inline void mi_atomic_void_addi64_relaxed(volatile int64_t* p, const volatile int64_t* padd) { + const int64_t add = *padd; + if (add != 0) { + mi_atomic_addi64_relaxed((volatile _Atomic(int64_t)*)p, add); + } } + static inline void mi_atomic_maxi64_relaxed(volatile _Atomic(int64_t)*p, int64_t x) { int64_t current; do { @@ -262,7 +347,7 @@ static inline void mi_atomic_addi64_acq_rel(volatile _Atomic(int64_t*)p, int64_t } static inline bool mi_atomic_casi64_strong_acq_rel(volatile _Atomic(int64_t*)p, int64_t* exp, int64_t des) { - int64_t read = _InterlockedCompareExchange64(p, des, *exp); + const int64_t read = _InterlockedCompareExchange64(p, des, *exp); if (read == *exp) { return true; } @@ -280,6 +365,8 @@ static inline bool mi_atomic_casi64_strong_acq_rel(volatile _Atomic(int64_t*)p, #define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des) #define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des) #define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des) +#define mi_atomic_cas_ptr_strong_acq_rel(tp,p,exp,des) mi_atomic_cas_strong_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des) +#define mi_atomic_exchange_ptr_relaxed(tp,p,x) (tp*)mi_atomic_exchange_relaxed((_Atomic(uintptr_t)*)(p),(uintptr_t)x) #define mi_atomic_exchange_ptr_release(tp,p,x) (tp*)mi_atomic_exchange_release((_Atomic(uintptr_t)*)(p),(uintptr_t)x) #define mi_atomic_exchange_ptr_acq_rel(tp,p,x) (tp*)mi_atomic_exchange_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t)x) @@ -302,18 +389,14 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub) { return (intptr_t)mi_atomic_addi(p, -sub); } -typedef _Atomic(uintptr_t) mi_atomic_once_t; -// Returns true only on the first invocation -static inline bool mi_atomic_once( mi_atomic_once_t* once ) { - if (mi_atomic_load_relaxed(once) != 0) return false; // quick test - uintptr_t expected = 0; - return mi_atomic_cas_strong_acq_rel(once, &expected, (uintptr_t)1); // try to set to 1 -} +// ---------------------------------------------------------------------- +// Guard +// ---------------------------------------------------------------------- typedef _Atomic(uintptr_t) mi_atomic_guard_t; -// Allows only one thread to execute at a time +// Allows only one thread to execute at a time (without blocking anyone) #define mi_atomic_guard(guard) \ uintptr_t _mi_guard_expected = 0; \ for(bool _mi_guard_once = true; \ @@ -321,73 +404,160 @@ typedef _Atomic(uintptr_t) mi_atomic_guard_t; (mi_atomic_store_release(guard,(uintptr_t)0), _mi_guard_once = false) ) +// ---------------------------------------------------------------------- +// Locks +// These should be light-weight in-process only locks. +// Only used for reserving arena's and to maintain the abandoned list. +// ---------------------------------------------------------------------- +#if _MSC_VER +#pragma warning(disable:26110) // unlock with holding lock +#endif + +#define mi_lock(lock) for(bool _mi_go = (mi_lock_acquire(lock),true); _mi_go; (mi_lock_release(lock), _mi_go=false) ) +#define mi_lock_maybe(lock,acquire) for(bool _mi_go = (acquire ? (mi_lock_acquire(lock),true) : true); _mi_go; _mi_go = (acquire ? (mi_lock_release(lock),false) : false) ) + + +#if defined(_WIN32) + +typedef struct mi_lock_s { + SRWLOCK mutex; // slim reader-writer lock +} mi_lock_t; + +#define MI_LOCK_INITIALIZER { SRWLOCK_INIT } + +static inline bool mi_lock_try_acquire(mi_lock_t* lock) { + return TryAcquireSRWLockExclusive(&lock->mutex); +} +static inline void mi_lock_acquire(mi_lock_t* lock) { + AcquireSRWLockExclusive(&lock->mutex); +} +static inline void mi_lock_release(mi_lock_t* lock) { + ReleaseSRWLockExclusive(&lock->mutex); +} +static inline void mi_lock_init(mi_lock_t* lock) { + InitializeSRWLock(&lock->mutex); +} +static inline void mi_lock_done(mi_lock_t* lock) { + (void)(lock); +} + +#elif defined(MI_USE_PTHREADS) + +#include // memcpy +void _mi_error_message(int err, const char* fmt, ...); + +typedef struct mi_lock_s { + pthread_mutex_t mutex; +} mi_lock_t; + +#define MI_LOCK_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + +static inline bool mi_lock_try_acquire(mi_lock_t* lock) { + return (pthread_mutex_trylock(&lock->mutex) == 0); +} +static inline void mi_lock_acquire(mi_lock_t* lock) { + const int err = pthread_mutex_lock(&lock->mutex); + if (err != 0) { + _mi_error_message(err, "internal error: lock cannot be acquired (err %i)\n", err); + } +} +static inline void mi_lock_release(mi_lock_t* lock) { + pthread_mutex_unlock(&lock->mutex); +} +static inline void mi_lock_init(mi_lock_t* lock) { + if(lock==NULL) return; + // use this instead of pthread_mutex_init since that can cause allocation on some platforms (and recursively initialize) + const pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + memcpy(&lock->mutex,&mutex,sizeof(mutex)); +} +static inline void mi_lock_done(mi_lock_t* lock) { + pthread_mutex_destroy(&lock->mutex); +} + +#elif defined(__cplusplus) -// Yield -#if defined(__cplusplus) #include -static inline void mi_atomic_yield(void) { - std::this_thread::yield(); +#include +#include + +typedef struct mi_lock_s { + std::mutex mutex; +} mi_lock_t; + +#define MI_LOCK_INITIALIZER { } + +static inline bool mi_lock_try_acquire(mi_lock_t* lock) { + return lock->mutex.try_lock(); } -#elif defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -static inline void mi_atomic_yield(void) { - YieldProcessor(); -} -#elif defined(__SSE2__) -#include -static inline void mi_atomic_yield(void) { - _mm_pause(); -} -#elif (defined(__GNUC__) || defined(__clang__)) && \ - (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__armel__) || defined(__ARMEL__) || \ - defined(__aarch64__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)) || defined(__POWERPC__) -#if defined(__x86_64__) || defined(__i386__) -static inline void mi_atomic_yield(void) { - __asm__ volatile ("pause" ::: "memory"); -} -#elif defined(__aarch64__) -static inline void mi_atomic_yield(void) { - __asm__ volatile("wfe"); -} -#elif (defined(__arm__) && __ARM_ARCH__ >= 7) -static inline void mi_atomic_yield(void) { - __asm__ volatile("yield" ::: "memory"); -} -#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__) -#ifdef __APPLE__ -static inline void mi_atomic_yield(void) { - __asm__ volatile ("or r27,r27,r27" ::: "memory"); +static inline void mi_lock_acquire(mi_lock_t* lock) { + lock->mutex.lock(); } -#else -static inline void mi_atomic_yield(void) { - __asm__ __volatile__ ("or 27,27,27" ::: "memory"); +static inline void mi_lock_release(mi_lock_t* lock) { + lock->mutex.unlock(); } -#endif -#elif defined(__armel__) || defined(__ARMEL__) -static inline void mi_atomic_yield(void) { - __asm__ volatile ("nop" ::: "memory"); +static inline void mi_lock_init(mi_lock_t* lock) { + new(&lock->mutex) std::mutex(); // in-place constructor +} +static inline void mi_lock_done(mi_lock_t* lock) { + lock->mutex.~mutex(); // in-place destructor } + +#else + +// fall back to poor man's locks. +// this should only be the case in a single-threaded environment (like __wasi__) +#include +#ifndef EFAULT +#define EFAULT (14) #endif -#elif defined(__sun) -// Fallback for other archs -#include -static inline void mi_atomic_yield(void) { - smt_pause(); +void _mi_error_message(int err, const char* fmt, ...); +void _mi_prim_thread_yield(void); + +typedef struct mi_lock_s { + _Atomic(uintptr_t) mutex; +} mi_lock_t; + +#define MI_LOCK_INITIALIZER { MI_ATOMIC_VAR_INIT(0) } + +static inline bool mi_lock_try_acquire(mi_lock_t* lock) { + uintptr_t expected = 0; + return mi_atomic_cas_strong_acq_rel(&lock->mutex, &expected, (uintptr_t)1); +} +static inline void mi_lock_acquire(mi_lock_t* lock) { + size_t ticks = 0; + for (int i = 0; i < 10000; i++) { // for at most 10000 tries? + if (mi_lock_try_acquire(lock)) return; + _mi_prim_thread_yield(); + } + _mi_error_message(EFAULT, "internal error: lock cannot be acquired (due to lack of native lock primitives)\n"); } -#elif defined(__wasi__) -#include -static inline void mi_atomic_yield(void) { - sched_yield(); +static inline void mi_lock_release(mi_lock_t* lock) { + mi_atomic_store_release(&lock->mutex, (uintptr_t)0); } -#else -#include -static inline void mi_atomic_yield(void) { - sleep(0); +static inline void mi_lock_init(mi_lock_t* lock) { + mi_lock_release(lock); +} +static inline void mi_lock_done(mi_lock_t* lock) { + (void)(lock); } + #endif +typedef struct mi_atomic_once_s { + _Atomic(uintptr_t) tid; + mi_lock_t lock; +} mi_atomic_once_t; + +// Returns `true` only on the first invocation, signifying we can execute an action once. +// If it returns `true`, the caller should call `_mi_atomic_once_release` after performing the action. +// Other threads (than the initial thread that entered) will block until `_mi_atomic_once_release` has been called. +bool _mi_atomic_once_enter(mi_atomic_once_t* once); // defined in `libc.c` +void _mi_atomic_once_release(mi_atomic_once_t* once); // defined in `libc.c` + +#define mi_atomic_do_once \ + static mi_atomic_once_t _mi_once = { MI_ATOMIC_VAR_INIT(0), MI_LOCK_INITIALIZER }; \ + for(bool _mi_exec = _mi_atomic_once_enter(&_mi_once); _mi_exec; (_mi_atomic_once_release(&_mi_once),_mi_exec=false)) + + #endif // __MIMALLOC_ATOMIC_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/bits.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/bits.h new file mode 100644 index 00000000..074d7f59 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/bits.h @@ -0,0 +1,345 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2019-2024 Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +/* ---------------------------------------------------------------------------- + Bit operation, and platform dependent definition (MI_INTPTR_SIZE etc) +---------------------------------------------------------------------------- */ + +#pragma once +#ifndef MI_BITS_H +#define MI_BITS_H + +#include // size_t +#include // int64_t etc +#include // bool +#include // LONG_MAX + +// ------------------------------------------------------ +// Size of a pointer. +// We assume that `sizeof(void*)==sizeof(intptr_t)` +// and it holds for all platforms we know of. +// +// However, the C standard only requires that: +// p == (void*)((intptr_t)p)) +// but we also need: +// i == (intptr_t)((void*)i) +// or otherwise one might define an intptr_t type that is larger than a pointer... +// ------------------------------------------------------ + +#if INTPTR_MAX > INT64_MAX +# define MI_INTPTR_SHIFT (4) // assume 128-bit (as on arm CHERI for example) +#elif INTPTR_MAX == INT64_MAX +# define MI_INTPTR_SHIFT (3) +#elif INTPTR_MAX == INT32_MAX +# define MI_INTPTR_SHIFT (2) +#else +#error platform pointers must be 32, 64, or 128 bits +#endif + +#if (INTPTR_MAX) > LONG_MAX +# define MI_PU(x) x##ULL +#else +# define MI_PU(x) x##UL +#endif + +#if SIZE_MAX == UINT64_MAX +# define MI_SIZE_SHIFT (3) +typedef int64_t mi_ssize_t; +#elif SIZE_MAX == UINT32_MAX +# define MI_SIZE_SHIFT (2) +typedef int32_t mi_ssize_t; +#else +#error platform objects must be 32 or 64 bits in size +#endif + +#if (SIZE_MAX/2) > LONG_MAX +# define MI_ZU(x) x##ULL +#else +# define MI_ZU(x) x##UL +#endif + +#define MI_INTPTR_SIZE (1< +#elif MI_ARCH_ARM64 && MI_OPT_SIMD +#include +#endif +#if defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64 || MI_ARCH_ARM32) +#include +#endif + +#if MI_ARCH_X64 && defined(__AVX2__) && !defined(__BMI2__) // msvc +#define __BMI2__ 1 +#endif +#if MI_ARCH_X64 && (defined(__AVX2__) || defined(__BMI2__)) && !defined(__BMI1__) // msvc +#define __BMI1__ 1 +#endif +#if MI_ARCH_X64 && defined(__AVX2__) && !defined(__LZCNT__) // msvc +#define __LZCNT__ 1 +#endif + +// Define big endian if needed +// #define MI_BIG_ENDIAN 1 + +// maximum virtual address bits in a user-space pointer +#if MI_DEFAULT_VIRTUAL_ADDRESS_BITS > 0 +#define MI_MAX_VABITS MI_DEFAULT_VIRTUAL_ADDRESS_BITS +#elif MI_ARCH_X64 +#define MI_MAX_VABITS (47) +#elif MI_INTPTR_SIZE > 4 +#define MI_MAX_VABITS (48) +#else +#define MI_MAX_VABITS (32) +#endif + +// use a flat page-map or a 2-level one +#ifndef MI_PAGE_MAP_FLAT +#if MI_MAX_VABITS <= 40 && !defined(__APPLE__) && MI_SECURE==0 && !MI_PAGE_META_IS_SEPARATED +#define MI_PAGE_MAP_FLAT 1 +#else +#define MI_PAGE_MAP_FLAT 0 +#endif +#endif + + +/* -------------------------------------------------------------------------------- + Builtin's +-------------------------------------------------------------------------------- */ + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#define mi_builtin(name) __builtin_##name +#define mi_has_builtin(name) __has_builtin(__builtin_##name) + +#if (LONG_MAX == INT32_MAX) +#define mi_builtin32(name) mi_builtin(name##l) +#define mi_has_builtin32(name) mi_has_builtin(name##l) +#else +#define mi_builtin32(name) mi_builtin(name) +#define mi_has_builtin32(name) mi_has_builtin(name) +#endif +#if (LONG_MAX == INT64_MAX) +#define mi_builtin64(name) mi_builtin(name##l) +#define mi_has_builtin64(name) mi_has_builtin(name##l) +#else +#define mi_builtin64(name) mi_builtin(name##ll) +#define mi_has_builtin64(name) mi_has_builtin(name##ll) +#endif + +#if (MI_SIZE_BITS == 32) +#define mi_builtinz(name) mi_builtin32(name) +#define mi_has_builtinz(name) mi_has_builtin32(name) +#define mi_msc_builtinz(name) name +#elif (MI_SIZE_BITS == 64) +#define mi_builtinz(name) mi_builtin64(name) +#define mi_has_builtinz(name) mi_has_builtin64(name) +#define mi_msc_builtinz(name) name##64 +#endif + +/* -------------------------------------------------------------------------------- + Popcount and count trailing/leading zero's +-------------------------------------------------------------------------------- */ + +size_t _mi_popcount_generic(size_t x); +extern bool _mi_cpu_has_popcnt; + +static inline size_t mi_popcount(size_t x) { + #if mi_has_builtinz(popcount) + return mi_builtinz(popcount)(x); + #elif defined(_MSC_VER) && (MI_ARCH_ARM64 || MI_ARCH_ARM32) + return mi_msc_builtinz(__popcnt)(x); + #elif defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86) + if (_mi_cpu_has_popcnt) { return mi_msc_builtinz(__popcnt)(x); } + else { return _mi_popcount_generic(x); } // see issue #1291 + #elif MI_ARCH_X64 && defined(__BMI1__) + return (size_t)_mm_popcnt_u64(x); + #else + #define MI_HAS_FAST_POPCOUNT 0 + return (x<=1 ? x : _mi_popcount_generic(x)); + #endif +} + +#ifndef MI_HAS_FAST_POPCOUNT +#define MI_HAS_FAST_POPCOUNT 1 +#endif + + + +size_t _mi_clz_generic(size_t x); +size_t _mi_ctz_generic(size_t x); + +static inline size_t mi_ctz(size_t x) { + #if defined(__GNUC__) && MI_ARCH_X64 && defined(__BMI1__) // on x64 tzcnt is defined for 0 + size_t r; + __asm ("tzcnt\t%1, %0" : "=r"(r) : "r"(x) : "cc"); + return r; + #elif defined(_MSC_VER) && MI_ARCH_X64 && defined(__BMI1__) + return _tzcnt_u64(x); + #elif defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64 || MI_ARCH_ARM32) + unsigned long idx; + return (mi_msc_builtinz(_BitScanForward)(&idx, x) ? (size_t)idx : MI_SIZE_BITS); + #elif mi_has_builtinz(ctz) + return (x!=0 ? (size_t)mi_builtinz(ctz)(x) : MI_SIZE_BITS); + #elif defined(__GNUC__) && (MI_ARCH_X64 || MI_ARCH_X86) + size_t r = MI_SIZE_BITS; // bsf leaves destination unmodified if the argument is 0 (see ) + __asm ("bsf\t%1, %0" : "+r"(r) : "r"(x) : "cc"); + return r; + #elif MI_HAS_FAST_POPCOUNT + return (x!=0 ? (mi_popcount(x^(x-1))-1) : MI_SIZE_BITS); + #else + #define MI_HAS_FAST_BITSCAN 0 + return (x!=0 ? _mi_ctz_generic(x) : MI_SIZE_BITS); + #endif +} + +static inline size_t mi_clz(size_t x) { + #if defined(__GNUC__) && MI_ARCH_X64 && defined(__LZCNT__) // on x64 lzcnt is defined for 0 + size_t r; + __asm ("lzcnt\t%1, %0" : "=r"(r) : "r"(x) : "cc"); + return r; + #elif defined(_MSC_VER) && MI_ARCH_X64 && defined(__LZCNT__) + return _lzcnt_u64(x); + #elif defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64 || MI_ARCH_ARM32) + unsigned long idx; + return (mi_msc_builtinz(_BitScanReverse)(&idx, x) ? MI_SIZE_BITS - 1 - (size_t)idx : MI_SIZE_BITS); + #elif mi_has_builtinz(clz) + return (x!=0 ? (size_t)mi_builtinz(clz)(x) : MI_SIZE_BITS); + #elif defined(__GNUC__) && (MI_ARCH_X64 || MI_ARCH_X86) + if (x==0) return MI_SIZE_BITS; + size_t r; + __asm ("bsr\t%1, %0" : "=r"(r) : "r"(x) : "cc"); + return (MI_SIZE_BITS - 1 - r); + #else + #define MI_HAS_FAST_BITSCAN 0 + return (x!=0 ? _mi_clz_generic(x) : MI_SIZE_BITS); + #endif +} + +#ifndef MI_HAS_FAST_BITSCAN +#define MI_HAS_FAST_BITSCAN 1 +#endif + +/* -------------------------------------------------------------------------------- + find trailing/leading zero (bit scan forward/reverse) +-------------------------------------------------------------------------------- */ + +// Bit scan forward: find the least significant bit that is set (i.e. count trailing zero's) +// return false if `x==0` (with `*idx` undefined) and true otherwise, +// with the `idx` is set to the bit index (`0 <= *idx < MI_BFIELD_BITS`). +static inline bool mi_bsf(size_t x, size_t* idx) { + #if defined(__GNUC__) && MI_ARCH_X64 && defined(__BMI1__) && (!defined(__clang_major__) || __clang_major__ >= 9) + // on x64 the carry flag is set on zero which gives better codegen + bool is_zero; + __asm ( "tzcnt\t%2, %1" : "=@ccc"(is_zero), "=r"(*idx) : "r"(x) : "cc" ); + return !is_zero; + #elif 0 && defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64 || MI_ARCH_ARM32) + unsigned long i; + return (mi_msc_builtinz(_BitScanForward)(&i, x) ? (*idx = (size_t)i, true) : false); + #else + return (x!=0 ? (*idx = mi_ctz(x), true) : false); + #endif +} + +// Bit scan reverse: find the most significant bit that is set +// return false if `x==0` (with `*idx` undefined) and true otherwise, +// with the `idx` is set to the bit index (`0 <= *idx < MI_BFIELD_BITS`). +static inline bool mi_bsr(size_t x, size_t* idx) { + #if 0 && defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64 || MI_ARCH_ARM32) + unsigned long i; + return (mi_msc_builtinz(_BitScanReverse)(&i, x) ? (*idx = (size_t)i, true) : false); + #else + return (x!=0 ? (*idx = MI_SIZE_BITS - 1 - mi_clz(x), true) : false); + #endif +} + + +/* -------------------------------------------------------------------------------- + rotate +-------------------------------------------------------------------------------- */ + +static inline size_t mi_rotr(size_t x, size_t r) { + #if (mi_has_builtin(rotateright64) && MI_SIZE_BITS==64) + return mi_builtin(rotateright64)(x,r); + #elif (mi_has_builtin(rotateright32) && MI_SIZE_BITS==32) + return mi_builtin(rotateright32)(x,r); + #elif defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_ARM64) + return _rotr64(x, (int)r); + #elif defined(_MSC_VER) && (MI_ARCH_X86 || MI_ARCH_ARM32) + return _lrotr(x,(int)r); + #else + // The term `(-rshift)&(BITS-1)` is written instead of `BITS - rshift` to + // avoid UB when `rshift==0`. See + const unsigned int rshift = (unsigned int)(r) & (MI_SIZE_BITS-1); + return ((x >> rshift) | (x << ((-rshift) & (MI_SIZE_BITS-1)))); + #endif +} + +static inline size_t mi_rotl(size_t x, size_t r) { + #if (mi_has_builtin(rotateleft64) && MI_SIZE_BITS==64) + return mi_builtin(rotateleft64)(x,r); + #elif (mi_has_builtin(rotateleft32) && MI_SIZE_BITS==32) + return mi_builtin(rotateleft32)(x,r); + #elif defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_ARM64) + return _rotl64(x, (int)r); + #elif defined(_MSC_VER) && (MI_ARCH_X86 || MI_ARCH_ARM32) + return _lrotl(x, (int)r); + #else + // The term `(-rshift)&(BITS-1)` is written instead of `BITS - rshift` to + // avoid UB when `rshift==0`. See + const unsigned int rshift = (unsigned int)(r) & (MI_SIZE_BITS-1); + return ((x << rshift) | (x >> ((-rshift) & (MI_SIZE_BITS-1)))); + #endif +} + +static inline uint32_t mi_rotl32(uint32_t x, uint32_t r) { + #if mi_has_builtin(rotateleft32) + return mi_builtin(rotateleft32)(x,r); + #elif defined(_MSC_VER) && (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64 || MI_ARCH_ARM32) + return _lrotl(x, (int)r); + #else + // The term `(-rshift)&(BITS-1)` is written instead of `BITS - rshift` to + // avoid UB when `rshift==0`. See + const unsigned int rshift = (unsigned int)(r) & 31; + return ((x << rshift) | (x >> ((-rshift) & 31))); + #endif +} + + +#endif // MI_BITS_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/internal.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/internal.h index 6c6e5ed0..7deaceff 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/internal.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/internal.h @@ -1,21 +1,26 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #pragma once -#ifndef MIMALLOC_INTERNAL_H -#define MIMALLOC_INTERNAL_H - +#ifndef MI_INTERNAL_H +#define MI_INTERNAL_H // -------------------------------------------------------------------------- -// This file contains the interal API's of mimalloc and various utility +// This file contains the internal API's of mimalloc and various utility // functions and macros. // -------------------------------------------------------------------------- #include "types.h" #include "track.h" +#include "bits.h" + + +// -------------------------------------------------------------------------- +// Compiler defines +// -------------------------------------------------------------------------- #if (MI_DEBUG>0) #define mi_trace_message(...) _mi_trace_message(__VA_ARGS__) @@ -23,262 +28,401 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_trace_message(...) #endif -#define MI_CACHE_LINE 64 +#define mi_decl_cache_align mi_decl_align(64) + #if defined(_MSC_VER) #pragma warning(disable:4127) // suppress constant conditional warning (due to MI_SECURE paths) #pragma warning(disable:26812) // unscoped enum warning +#define mi_decl_forceinline __forceinline #define mi_decl_noinline __declspec(noinline) #define mi_decl_thread __declspec(thread) -#define mi_decl_cache_align __declspec(align(MI_CACHE_LINE)) +#define mi_decl_noreturn __declspec(noreturn) #define mi_decl_weak +#define mi_decl_hidden +#define mi_decl_cold #elif (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) // includes clang and icc +#if !MI_TRACK_ASAN +#define mi_decl_forceinline __attribute__((always_inline)) inline +#else +#define mi_decl_forceinline inline +#endif #define mi_decl_noinline __attribute__((noinline)) #define mi_decl_thread __thread -#define mi_decl_cache_align __attribute__((aligned(MI_CACHE_LINE))) +#define mi_decl_noreturn __attribute__((noreturn)) #define mi_decl_weak __attribute__((weak)) +#define mi_decl_hidden __attribute__((visibility("hidden"))) +#if (__GNUC__ >= 4) || defined(__clang__) +#define mi_decl_cold __attribute__((cold)) +#else +#define mi_decl_cold +#endif +#elif __cplusplus >= 201103L // c++11 +#define mi_decl_forceinline inline +#define mi_decl_noinline +#define mi_decl_thread thread_local +#define mi_decl_noreturn [[noreturn]] +#define mi_decl_weak +#define mi_decl_hidden +#define mi_decl_cold #else +#define mi_decl_forceinline inline #define mi_decl_noinline #define mi_decl_thread __thread // hope for the best :-) -#define mi_decl_cache_align +#define mi_decl_noreturn #define mi_decl_weak +#define mi_decl_hidden +#define mi_decl_cold #endif -#if defined(__EMSCRIPTEN__) && !defined(__wasi__) -#define __wasi__ +#if defined(__GNUC__) || defined(__clang__) +#define mi_unlikely(x) (__builtin_expect(!!(x),false)) +#define mi_likely(x) (__builtin_expect(!!(x),true)) +#elif (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define mi_unlikely(x) (x) [[unlikely]] +#define mi_likely(x) (x) [[likely]] +#else +#define mi_unlikely(x) (x) +#define mi_likely(x) (x) +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if defined(__cplusplus) +#define mi_decl_externc extern "C" +#else +#define mi_decl_externc +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 7)) || defined(__clang__) // includes clang and icc +#define mi_decl_maybe_unused __attribute__((unused)) +#elif __cplusplus >= 201703L // c++17 +#define mi_decl_maybe_unused [[maybe_unused]] +#else +#define mi_decl_maybe_unused #endif #if defined(__cplusplus) -#define mi_decl_externc extern "C" +#define mi_decl_externc extern "C" #else #define mi_decl_externc #endif -// pthreads -#if !defined(_WIN32) && !defined(__wasi__) -#define MI_USE_PTHREADS -#include + +#if defined(__EMSCRIPTEN__) && !defined(__wasi__) +#define __wasi__ #endif + +// -------------------------------------------------------------------------- +// Internal functions +// -------------------------------------------------------------------------- + + +// "libc.c" +#include +int _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args); +int _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...); +char _mi_toupper(char c); +int _mi_strnicmp(const char* s, const char* t, size_t n); +void _mi_strlcpy(char* dest, const char* src, size_t dest_size); +void _mi_strlcat(char* dest, const char* src, size_t dest_size); +size_t _mi_strlen(const char* s); +size_t _mi_strnlen(const char* s, size_t max_len); +char* _mi_strnstr(char* s, size_t max_len, const char* pat); +bool _mi_streq(const char* s, const char* t); +int _mi_getenv(const char* name, char* result, size_t result_size); + // "options.c" -void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message); -void _mi_fprintf(mi_output_fun* out, void* arg, const char* fmt, ...); -void _mi_warning_message(const char* fmt, ...); -void _mi_verbose_message(const char* fmt, ...); -void _mi_trace_message(const char* fmt, ...); -void _mi_options_init(void); -void _mi_error_message(int err, const char* fmt, ...); +void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message); +void _mi_fprintf(mi_output_fun* out, void* arg, const char* fmt, ...); +void _mi_raw_message(const char* fmt, ...); +void _mi_message(const char* fmt, ...); +void _mi_warning_message(const char* fmt, ...); +void _mi_verbose_message(const char* fmt, ...); +void _mi_trace_message(const char* fmt, ...); +void _mi_options_init(void); +void _mi_options_post_init(void); +long _mi_option_get_fast(mi_option_t option); +void _mi_error_message(int err, const char* fmt, ...); // random.c -void _mi_random_init(mi_random_ctx_t* ctx); -void _mi_random_init_weak(mi_random_ctx_t* ctx); -void _mi_random_reinit_if_weak(mi_random_ctx_t * ctx); -void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* new_ctx); -uintptr_t _mi_random_next(mi_random_ctx_t* ctx); -uintptr_t _mi_heap_random_next(mi_heap_t* heap); -uintptr_t _mi_os_random_weak(uintptr_t extra_seed); +void _mi_random_init(mi_random_ctx_t* ctx); +void _mi_random_init_weak(mi_random_ctx_t* ctx); +void _mi_random_reinit_if_weak(mi_random_ctx_t * ctx); +void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* new_ctx); +uintptr_t _mi_random_next(mi_random_ctx_t* ctx); +uintptr_t _mi_theap_random_next(mi_theap_t* theap); +uintptr_t _mi_os_random_weak(uintptr_t extra_seed); static inline uintptr_t _mi_random_shuffle(uintptr_t x); // init.c -extern mi_decl_cache_align mi_stats_t _mi_stats_main; -extern mi_decl_cache_align const mi_page_t _mi_page_empty; -bool _mi_is_main_thread(void); -size_t _mi_current_thread_count(void); -bool _mi_preloading(void); // true while the C runtime is not initialized yet +extern mi_decl_hidden mi_decl_cache_align const mi_page_t _mi_page_empty; +void _mi_auto_process_init(void); +void mi_cdecl _mi_auto_process_done(void) mi_attr_noexcept; +bool _mi_is_redirected(void); +bool _mi_allocator_init(const char** message); +void _mi_allocator_done(void); +bool _mi_is_main_thread(void); +bool _mi_preloading(void); // true while the C runtime is not initialized yet +void _mi_thread_done(mi_theap_t* theap); + +mi_subproc_t* _mi_subproc(void); +mi_subproc_t* _mi_subproc_main(void); +mi_heap_t* _mi_subproc_heap_main(mi_subproc_t* subproc); +mi_subproc_t* _mi_subproc_from_id(mi_subproc_id_t subproc_id); + mi_threadid_t _mi_thread_id(void) mi_attr_noexcept; -mi_heap_t* _mi_heap_main_get(void); // statically allocated main backing heap -void _mi_thread_done(mi_heap_t* heap); -void _mi_thread_data_collect(void); -void _mi_tld_init(mi_tld_t* tld, mi_heap_t* bheap); +size_t _mi_thread_seq_id(void) mi_attr_noexcept; +bool _mi_is_heap_main(const mi_heap_t* heap); +bool _mi_is_theap_main(const mi_theap_t* theap); +void _mi_theap_guarded_init(mi_theap_t* theap); +void _mi_theap_options_init(mi_theap_t* theap); +mi_theap_t* _mi_theap_default_safe(void); // ensure the returned theap is initialized +mi_theap_t* _mi_theap_main_safe(void); // os.c -void _mi_os_init(void); // called from process init -void* _mi_os_alloc(size_t size, mi_memid_t* memid, mi_stats_t* stats); -void _mi_os_free(void* p, size_t size, mi_memid_t memid, mi_stats_t* stats); -void _mi_os_free_ex(void* p, size_t size, bool still_committed, mi_memid_t memid, mi_stats_t* stats); - -size_t _mi_os_page_size(void); -size_t _mi_os_good_alloc_size(size_t size); -bool _mi_os_has_overcommit(void); -bool _mi_os_has_virtual_reserve(void); - -bool _mi_os_purge(void* p, size_t size, mi_stats_t* stats); -bool _mi_os_reset(void* addr, size_t size, mi_stats_t* tld_stats); -bool _mi_os_commit(void* p, size_t size, bool* is_zero, mi_stats_t* stats); -bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); -bool _mi_os_protect(void* addr, size_t size); -bool _mi_os_unprotect(void* addr, size_t size); -bool _mi_os_purge(void* p, size_t size, mi_stats_t* stats); -bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, mi_stats_t* stats); - -void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, mi_memid_t* memid, mi_stats_t* stats); -void* _mi_os_alloc_aligned_at_offset(size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large, mi_memid_t* memid, mi_stats_t* tld_stats); - -void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size); -bool _mi_os_use_large_page(size_t size, size_t alignment); -size_t _mi_os_large_page_size(void); - -void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize, mi_memid_t* memid); +void _mi_os_init(void); // called from process init +void* _mi_os_alloc(size_t size, mi_memid_t* memid); +void* _mi_os_zalloc(size_t size, mi_memid_t* memid); +void _mi_os_free(void* p, size_t size, mi_memid_t memid); +void _mi_os_free_ex(void* p, size_t size, bool still_committed, mi_memid_t memid, mi_subproc_t* subproc ); + +size_t _mi_os_page_size(void); +size_t _mi_os_guard_page_size(void); +size_t _mi_os_good_alloc_size(size_t size); +bool _mi_os_has_overcommit(void); +bool _mi_os_has_virtual_reserve(void); +size_t _mi_os_virtual_address_bits(void); +size_t _mi_os_minimal_purge_size(void); + +bool _mi_os_reset(void* addr, size_t size); +bool _mi_os_decommit(void* addr, size_t size); +void _mi_os_reuse(void* p, size_t size); +mi_decl_nodiscard bool _mi_os_commit(void* p, size_t size, bool* is_zero); +mi_decl_nodiscard bool _mi_os_commit_ex(void* addr, size_t size, bool* is_zero, size_t stat_size); +mi_decl_nodiscard bool _mi_os_protect(void* addr, size_t size); +bool _mi_os_unprotect(void* addr, size_t size); +bool _mi_os_purge(void* p, size_t size); +bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, size_t stats_size, mi_commit_fun_t* commit_fun, void* commit_fun_arg); + +size_t _mi_os_secure_guard_page_size(void); +bool _mi_os_secure_guard_page_set_at(void* addr, mi_memid_t memid); +bool _mi_os_secure_guard_page_set_before(void* addr, mi_memid_t memid); +bool _mi_os_secure_guard_page_reset_at(void* addr, mi_memid_t memid); +bool _mi_os_secure_guard_page_reset_before(void* addr, mi_memid_t memid); + +int _mi_os_numa_node(void); +int _mi_os_numa_node_count(void); + +void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, mi_memid_t* memid); +void* _mi_os_alloc_aligned_at_offset(size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large, mi_memid_t* memid); + +void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size); +bool _mi_os_canuse_large_page(size_t size, size_t alignment); +size_t _mi_os_large_page_size(void); +void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize, mi_memid_t* memid); + +// threadlocal.c + +mi_thread_local_t _mi_thread_local_create(void); +void _mi_thread_local_free( mi_thread_local_t key ); +bool _mi_thread_local_set( mi_thread_local_t key, void* val ); +void* _mi_thread_local_get( mi_thread_local_t key ); +void _mi_thread_locals_init(void); +void _mi_thread_locals_done(void); +void _mi_thread_locals_thread_done(void); // arena.c mi_arena_id_t _mi_arena_id_none(void); -void _mi_arena_free(void* p, size_t size, size_t still_committed_size, mi_memid_t memid, mi_stats_t* stats); -void* _mi_arena_alloc(size_t size, bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld); -void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld); -bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_id_t request_arena_id); -bool _mi_arena_contains(const void* p); -void _mi_arenas_collect(bool force_purge, mi_stats_t* stats); -void _mi_arena_unsafe_destroy_all(mi_stats_t* stats); - -bool _mi_arena_segment_clear_abandoned(mi_segment_t* segment); -void _mi_arena_segment_mark_abandoned(mi_segment_t* segment); -size_t _mi_arena_segment_abandoned_count(void); - -typedef struct mi_arena_field_cursor_s { // abstract - mi_arena_id_t start; - int count; - size_t bitmap_idx; -} mi_arena_field_cursor_t; -void _mi_arena_field_cursor_init(mi_heap_t* heap, mi_arena_field_cursor_t* current); -mi_segment_t* _mi_arena_segment_clear_abandoned_next(mi_arena_field_cursor_t* previous); - -// "segment-map.c" -void _mi_segment_map_allocated_at(const mi_segment_t* segment); -void _mi_segment_map_freed_at(const mi_segment_t* segment); - -// "segment.c" -mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment, mi_segments_tld_t* tld, mi_os_tld_t* os_tld); -void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld); -void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld); -bool _mi_segment_try_reclaim_abandoned( mi_heap_t* heap, bool try_all, mi_segments_tld_t* tld); -void _mi_segment_collect(mi_segment_t* segment, bool force, mi_segments_tld_t* tld); - -#if MI_HUGE_PAGE_ABANDON -void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block); -#else -void _mi_segment_huge_page_reset(mi_segment_t* segment, mi_page_t* page, mi_block_t* block); -#endif - -uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size); // page start for any page -void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld); -void _mi_abandoned_await_readers(void); -void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* tld); -bool _mi_segment_attempt_reclaim(mi_heap_t* heap, mi_segment_t* segment); +mi_arena_t* _mi_arena_from_id(mi_arena_id_t id); +bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_t* request_arena); + +void* _mi_arenas_alloc(mi_heap_t* heap, size_t size, bool commit, bool allow_pinned, mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid); +void* _mi_arenas_alloc_aligned(mi_heap_t* heap, size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_pinned, mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid); +void _mi_arenas_free(void* p, size_t size, mi_memid_t memid); +bool _mi_arenas_contain(const void* p); +void _mi_arenas_collect(bool force_purge, bool visit_all, mi_tld_t* tld); +void _mi_arenas_unsafe_destroy_all(mi_subproc_t* subproc); + +mi_page_t* _mi_arenas_page_alloc(mi_theap_t* theap, size_t block_size, size_t page_alignment); +void _mi_arenas_page_free(mi_page_t* page, mi_theap_t* current_theapx /* can be NULL */); +void _mi_arenas_page_abandon(mi_page_t* page, mi_theap_t* current_theap); +void _mi_arenas_page_unabandon(mi_page_t* page, mi_theap_t* current_theapx /* can be NULL */); +bool _mi_arenas_page_try_reabandon_to_mapped(mi_page_t* page); + +// arena-meta.c +void* _mi_meta_zalloc( size_t size, mi_memid_t* memid ); +void _mi_meta_free(void* p, size_t size, mi_memid_t memid); +bool _mi_meta_is_meta_page(void* p); + +// "page-map.c" +bool _mi_page_map_init(void); +mi_decl_nodiscard bool _mi_page_map_register(mi_page_t* page); +void _mi_page_map_unregister(mi_page_t* page); +void _mi_page_map_unregister_range(void* start, size_t size); +mi_page_t* _mi_safe_ptr_page(const void* p); +void _mi_page_map_unsafe_destroy(mi_subproc_t* subproc); // "page.c" -void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept mi_attr_malloc; - -void _mi_page_retire(mi_page_t* page) mi_attr_noexcept; // free the page if there are no other pages with many free blocks -void _mi_page_unfull(mi_page_t* page); -void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force); // free the page -void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq); // abandon the page, to be picked up by another thread... -void _mi_heap_delayed_free_all(mi_heap_t* heap); -bool _mi_heap_delayed_free_partial(mi_heap_t* heap); -void _mi_heap_collect_retired(mi_heap_t* heap, bool force); - -void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never); -bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never); -size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append); -void _mi_deferred_free(mi_heap_t* heap, bool force); - -void _mi_page_free_collect(mi_page_t* page,bool force); -void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page); // callback from segments - -size_t _mi_bin_size(uint8_t bin); // for stats -uint8_t _mi_bin(size_t size); // for stats +void* _mi_malloc_generic(mi_theap_t* theap, size_t size, size_t zero_huge_alignment, size_t* usable) mi_attr_noexcept mi_attr_malloc; + +void _mi_page_retire(mi_page_t* page) mi_attr_noexcept; // free the page if there are no other pages with many free blocks +void _mi_page_unfull(mi_page_t* page); +void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq); // free the page +void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq); // abandon the page, to be picked up by another thread... + +size_t _mi_page_queue_append(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_queue_t* append); +void _mi_deferred_free(mi_theap_t* theap, bool force); + +void _mi_page_free_collect(mi_page_t* page, bool force); +void _mi_page_free_collect_partly(mi_page_t* page, mi_block_t* head); +mi_decl_nodiscard bool _mi_page_init(mi_theap_t* theap, mi_page_t* page); +bool _mi_page_queue_is_valid(mi_theap_t* theap, const mi_page_queue_t* pq); + +size_t _mi_page_stats_bin(const mi_page_t* page); // for stats +size_t _mi_bin_size(size_t bin); // for stats +size_t _mi_bin(size_t size); // for stats + +// "theap.c" +mi_theap_t* _mi_theap_create(mi_heap_t* heap, mi_tld_t* tld); +void _mi_theap_delete(mi_theap_t* theap, bool acquire_tld_theaps_lock); +void _mi_theap_default_set(mi_theap_t* theap); +void _mi_theap_cached_set(mi_theap_t* theap); +void _mi_theap_collect_retired(mi_theap_t* theap, bool force); +void _mi_theap_collect_abandon(mi_theap_t* theap); +bool _mi_theap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t* page, mi_block_visit_fun* visitor, void* arg); +void _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page); +bool _mi_theap_free(mi_theap_t* theap, bool acquire_heap_theaps_lock, bool acquire_tld_theaps_lock); +void _mi_theap_incref(mi_theap_t* theap); +void _mi_theap_decref(mi_theap_t* theap); +bool _mi_page_visit_blocks( mi_page_t* page, mi_block_visit_fun* visitor, void* arg ); // "heap.c" -void _mi_heap_init(mi_heap_t* heap, mi_tld_t* tld, mi_arena_id_t arena_id, bool noreclaim, uint8_t tag); -void _mi_heap_destroy_pages(mi_heap_t* heap); -void _mi_heap_collect_abandon(mi_heap_t* heap); -void _mi_heap_set_default_direct(mi_heap_t* heap); -bool _mi_heap_memid_is_suitable(mi_heap_t* heap, mi_memid_t memid); -void _mi_heap_unsafe_destroy_all(void); -mi_heap_t* _mi_heap_by_tag(mi_heap_t* heap, uint8_t tag); +void _mi_heap_area_init(mi_heap_area_t* area, mi_page_t* page); +mi_decl_cold mi_theap_t* _mi_heap_theap_get_or_init(const mi_heap_t* heap); // get (and possible create) the theap belonging to a heap +mi_decl_cold mi_theap_t* _mi_heap_theap_get_peek(const mi_heap_t* heap); // get the theap for a heap without initializing (and return NULL in that case) +void _mi_heap_move_pages(mi_heap_t* heap_from, mi_heap_t* heap_to); // in "arena.c" +void _mi_heap_destroy_pages(mi_heap_t* heap_from); // in "arena.c" +void _mi_heap_force_destroy(mi_heap_t* heap); // allow destroying the main heap // "stats.c" -void _mi_stats_done(mi_stats_t* stats); -mi_msecs_t _mi_clock_now(void); -mi_msecs_t _mi_clock_end(mi_msecs_t start); -mi_msecs_t _mi_clock_start(void); +void _mi_stats_init(void); +void _mi_stats_merge_into(mi_stats_t* to, mi_stats_t* from); + +mi_msecs_t _mi_clock_now(void); +mi_msecs_t _mi_clock_end(mi_msecs_t start); +mi_msecs_t _mi_clock_start(void); // "alloc.c" -void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept; // called from `_mi_malloc_generic` -void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` -void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` -void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept; -void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` -void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept; -mi_block_t* _mi_page_ptr_unalign(const mi_page_t* page, const void* p); -bool _mi_free_delayed_block(mi_block_t* block); -void _mi_free_generic(mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept; // for runtime integration -void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size); +void* _mi_page_malloc_zero(mi_theap_t* theap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept; // called from `_mi_theap_malloc_aligned` +void* _mi_theap_malloc_zero(mi_theap_t* theap, size_t size, bool zero, size_t* usable) mi_attr_noexcept; +void* _mi_theap_malloc_zero_ex(mi_theap_t* theap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept; // called from `_mi_theap_malloc_aligned` +void* _mi_theap_realloc_zero(mi_theap_t* theap, void* p, size_t newsize, bool zero, size_t* usable_pre, size_t* usable_post) mi_attr_noexcept; +mi_block_t* _mi_page_ptr_unalign(const mi_page_t* page, const void* p); +void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size); -// "libc.c" -#include -void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args); -void _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...); -char _mi_toupper(char c); -int _mi_strnicmp(const char* s, const char* t, size_t n); -void _mi_strlcpy(char* dest, const char* src, size_t dest_size); -void _mi_strlcat(char* dest, const char* src, size_t dest_size); -size_t _mi_strlen(const char* s); -size_t _mi_strnlen(const char* s, size_t max_len); -bool _mi_getenv(const char* name, char* result, size_t result_size); +// "free.c" +void _mi_page_unguard_all(mi_page_t* page); #if MI_DEBUG>1 -bool _mi_page_is_valid(mi_page_t* page); +bool _mi_page_is_valid(mi_page_t* page); #endif // ------------------------------------------------------ -// Branches +// Assertions // ------------------------------------------------------ -#if defined(__GNUC__) || defined(__clang__) -#define mi_unlikely(x) (__builtin_expect(!!(x),false)) -#define mi_likely(x) (__builtin_expect(!!(x),true)) -#elif (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) -#define mi_unlikely(x) (x) [[unlikely]] -#define mi_likely(x) (x) [[likely]] +#if (MI_DEBUG) +// use our own assertion to print without memory allocation +mi_decl_noreturn mi_decl_cold void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line, const char* func) mi_attr_noexcept; +#define mi_assert(expr) ((expr) ? (void)0 : _mi_assert_fail(#expr,__FILE__,__LINE__,__func__)) #else -#define mi_unlikely(x) (x) -#define mi_likely(x) (x) +#define mi_assert(x) #endif -#ifndef __has_builtin -#define __has_builtin(x) 0 +#if (MI_DEBUG>1) +#define mi_assert_internal mi_assert +#else +#define mi_assert_internal(x) +#endif + +#if (MI_DEBUG>2) +#define mi_assert_expensive mi_assert +#else +#define mi_assert_expensive(x) #endif /* ----------------------------------------------------------- - Error codes passed to `_mi_fatal_error` - All are recoverable but EFAULT is a serious error and aborts by default in secure mode. - For portability define undefined error codes using common Unix codes: - + Statistics (in `stats.c`) ----------------------------------------------------------- */ -#include -#ifndef EAGAIN // double free -#define EAGAIN (11) -#endif -#ifndef ENOMEM // out of memory -#define ENOMEM (12) -#endif -#ifndef EFAULT // corrupted free-list or meta-data -#define EFAULT (14) -#endif -#ifndef EINVAL // trying to free an invalid pointer -#define EINVAL (22) -#endif -#ifndef EOVERFLOW // count*size overflow -#define EOVERFLOW (75) -#endif + +// add to stat keeping track of the peak +void __mi_stat_increase(mi_stat_count_t* stat, size_t amount); +void __mi_stat_decrease(mi_stat_count_t* stat, size_t amount); +void __mi_stat_increase_mt(mi_stat_count_t* stat, size_t amount); +void __mi_stat_decrease_mt(mi_stat_count_t* stat, size_t amount); + +// adjust stat in special cases to compensate for double counting (and does not adjust peak values and can decrease the total) +void __mi_stat_adjust_increase(mi_stat_count_t* stat, size_t amount); +void __mi_stat_adjust_decrease(mi_stat_count_t* stat, size_t amount); +void __mi_stat_adjust_increase_mt(mi_stat_count_t* stat, size_t amount); +void __mi_stat_adjust_decrease_mt(mi_stat_count_t* stat, size_t amount); + +// counters can just be increased +void __mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount); +void __mi_stat_counter_increase_mt(mi_stat_counter_t* stat, size_t amount); + +#define mi_heap_stat_counter_increase(heap,stat,amount) __mi_stat_counter_increase_mt( &(heap)->stats.stat, amount) +#define mi_heap_stat_increase(heap,stat,amount) __mi_stat_increase_mt( &(heap)->stats.stat, amount) +#define mi_heap_stat_decrease(heap,stat,amount) __mi_stat_decrease_mt( &(heap)->stats.stat, amount) +#define mi_heap_stat_adjust_increase(heap,stat,amnt) __mi_stat_adjust_increase_mt( &(heap)->stats.stat, amnt) +#define mi_heap_stat_adjust_decrease(heap,stat,amnt) __mi_stat_adjust_decrease_mt( &(heap)->stats.stat, amnt) + +#define mi_subproc_stat_counter_increase(subproc,stat,amount) __mi_stat_counter_increase_mt( &(subproc)->stats.stat, amount) +#define mi_subproc_stat_increase(subproc,stat,amount) __mi_stat_increase_mt( &(subproc)->stats.stat, amount) +#define mi_subproc_stat_decrease(subproc,stat,amount) __mi_stat_decrease_mt( &(subproc)->stats.stat, amount) +#define mi_subproc_stat_adjust_increase(subproc,stat,amount) __mi_stat_adjust_increase_mt( &(subproc)->stats.stat, amount) +#define mi_subproc_stat_adjust_decrease(subproc,stat,amount) __mi_stat_adjust_decrease_mt( &(subproc)->stats.stat, amount) + +#define mi_os_stat_counter_increase(stat,amount) mi_subproc_stat_counter_increase(_mi_subproc(),stat,amount) +#define mi_os_stat_increase(stat,amount) mi_subproc_stat_increase(_mi_subproc(),stat,amount) +#define mi_os_stat_decrease(stat,amount) mi_subproc_stat_decrease(_mi_subproc(),stat,amount) + +#define mi_theap_stat_counter_increase(theap,stat,amount) __mi_stat_counter_increase( &(theap)->stats.stat, amount) +#define mi_theap_stat_increase(theap,stat,amount) __mi_stat_increase( &(theap)->stats.stat, amount) +#define mi_theap_stat_decrease(theap,stat,amount) __mi_stat_decrease( &(theap)->stats.stat, amount) +#define mi_theap_stat_adjust_increase(theap,stat,amnt) __mi_stat_adjust_increase( &(theap)->stats.stat, amnt) +#define mi_theap_stat_adjust_decrease(theap,stat,amnt) __mi_stat_adjust_decrease( &(theap)->stats.stat, amnt) + + +/* ----------------------------------------------------------- + Options (exposed for the debugger) +----------------------------------------------------------- */ +typedef enum mi_option_init_e { + MI_OPTION_UNINIT, // not yet initialized + MI_OPTION_DEFAULTED, // not found in the environment, use default value + MI_OPTION_INITIALIZED // found in environment or set explicitly +} mi_option_init_t; + +typedef struct mi_option_desc_s { + long value; // the value + mi_option_init_t init; // is it initialized yet? (from the environment) + mi_option_t option; // for debugging: the option index should match the option + const char* name; // option name without `mimalloc_` prefix + const char* legacy_name; // potential legacy option name +} mi_option_desc_t; + /* ----------------------------------------------------------- Inlined definitions ----------------------------------------------------------- */ #define MI_UNUSED(x) (void)(x) -#if (MI_DEBUG>0) +#if (MI_DEBUG>1) #define MI_UNUSED_RELEASE(x) #else #define MI_UNUSED_RELEASE(x) MI_UNUSED(x) @@ -292,6 +436,9 @@ bool _mi_page_is_valid(mi_page_t* page); #define MI_INIT128(x) MI_INIT64(x),MI_INIT64(x) #define MI_INIT256(x) MI_INIT128(x),MI_INIT128(x) +#define MI_INIT74(x) MI_INIT64(x),MI_INIT8(x),x(),x() +#define MI_INIT5(x) MI_INIT4(x),x() +#define MI_INIT6(x) MI_INIT4(x),x(),x() #include // initialize a local variable to zero; use memset as compilers optimize constant sized memset's @@ -303,9 +450,8 @@ static inline bool _mi_is_power_of_two(uintptr_t x) { } // Is a pointer aligned? -static inline bool _mi_is_aligned(void* p, size_t alignment) { - mi_assert_internal(alignment != 0); - return (((uintptr_t)p % alignment) == 0); +static inline bool _mi_is_aligned(const void* p, size_t alignment) { + return (alignment==0 || ((uintptr_t)p % alignment) == 0); } // Align upwards @@ -320,7 +466,11 @@ static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) { } } -// Align downwards +// Align a pointer upwards +static inline void* _mi_align_up_ptr(const void* p, size_t alignment) { + return (void*)_mi_align_up((uintptr_t)p, alignment); +} + static inline uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) { mi_assert_internal(alignment != 0); uintptr_t mask = alignment - 1; @@ -332,23 +482,25 @@ static inline uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) { } } -// Align a pointer upwards -static inline void* mi_align_up_ptr(void* p, size_t alignment) { - return (void*)_mi_align_up((uintptr_t)p, alignment); -} - -// Align a pointer downwards -static inline void* mi_align_down_ptr(void* p, size_t alignment) { +// align a pointer downwards +static inline void* _mi_align_down_ptr(const void* p, size_t alignment) { return (void*)_mi_align_down((uintptr_t)p, alignment); } - // Divide upwards: `s <= _mi_divide_up(s,d)*d < s+d`. static inline uintptr_t _mi_divide_up(uintptr_t size, size_t divider) { mi_assert_internal(divider != 0); return (divider == 0 ? size : ((size + divider - 1) / divider)); } + +// clamp an integer +static inline size_t _mi_clamp(size_t sz, size_t min, size_t max) { + if (sz < min) return min; + else if (sz > max) return max; + else return sz; +} + // Is memory zero initialized? static inline bool mi_mem_is_zero(const void* p, size_t size) { for (size_t i = 0; i < size; i++) { @@ -357,7 +509,6 @@ static inline bool mi_mem_is_zero(const void* p, size_t size) { return true; } - // Align a byte size to a size in _machine words_, // i.e. byte size == `wsize*sizeof(void*)`. static inline size_t _mi_wsize_from_size(size_t size) { @@ -382,10 +533,13 @@ static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { } #else /* __builtin_umul_overflow is unavailable */ static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { - #define MI_MUL_COULD_OVERFLOW ((size_t)1 << (4*sizeof(size_t))) // sqrt(SIZE_MAX) - *total = count * size; - // note: gcc/clang optimize this to directly check the overflow flag - return ((size >= MI_MUL_COULD_OVERFLOW || count >= MI_MUL_COULD_OVERFLOW) && size > 0 && (SIZE_MAX / size) < count); + *total = count*size; + if mi_likely(((size|count)>>(4*MI_SIZE_SIZE))==0) { // did size and count fit both in the lower half bits of a size_t? + return false; + } + else { + return (size!=0 && (SIZE_MAX / size) < count); + } } #endif @@ -395,14 +549,16 @@ static inline bool mi_count_size_overflow(size_t count, size_t size, size_t* tot *total = size; return false; } - else if mi_unlikely(mi_mul_overflow(count, size, total)) { + else if mi_likely(!mi_mul_overflow(count, size, total)) { + return false; + } + else { #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "allocation request is too large (%zu * %zu bytes)\n", count, size); #endif *total = SIZE_MAX; return true; } - else return false; } @@ -410,163 +566,193 @@ static inline bool mi_count_size_overflow(size_t count, size_t size, size_t* tot Heap functions ------------------------------------------------------------------------------------------- */ -extern const mi_heap_t _mi_heap_empty; // read-only empty heap, initial value of the thread local default heap +extern mi_decl_hidden const mi_theap_t _mi_theap_empty; // read-only empty theap, initial value of the thread local default theap (in the MI_TLS_MODEL_THREAD_LOCAL) +extern mi_decl_hidden const mi_theap_t _mi_theap_empty_wrong; // read-only empty theap used to signal that a theap for a heap could not be allocated -static inline bool mi_heap_is_backing(const mi_heap_t* heap) { - return (heap->tld->heap_backing == heap); + +static inline mi_heap_t* _mi_theap_heap(const mi_theap_t* theap) { + return mi_atomic_load_ptr_acquire(mi_heap_t,&theap->heap); } -static inline bool mi_heap_is_initialized(mi_heap_t* heap) { - mi_assert_internal(heap != NULL); - return (heap != &_mi_heap_empty); +static inline bool mi_theap_is_initialized(const mi_theap_t* theap) { + return (theap != NULL && _mi_theap_heap(theap) != NULL); } -static inline uintptr_t _mi_ptr_cookie(const void* p) { - extern mi_heap_t _mi_heap_main; - mi_assert_internal(_mi_heap_main.cookie != 0); - return ((uintptr_t)p ^ _mi_heap_main.cookie); +static inline mi_page_t* _mi_theap_get_free_small_page(mi_theap_t* theap, size_t size) { + mi_assert_internal(size <= (MI_SMALL_SIZE_MAX + MI_PADDING_SIZE)); + const size_t idx = _mi_wsize_from_size(size); + mi_assert_internal(idx < MI_PAGES_DIRECT); + return theap->pages_free_direct[idx]; } + +//static inline uintptr_t _mi_ptr_cookie(const void* p) { +// extern mi_theap_t _mi_theap_main; +// mi_assert_internal(_mi_theap_main.cookie != 0); +// return ((uintptr_t)p ^ _mi_theap_main.cookie); +//} + + /* ----------------------------------------------------------- - Pages + The page map maps addresses to `mi_page_t` pointers ----------------------------------------------------------- */ -static inline mi_page_t* _mi_heap_get_free_small_page(mi_heap_t* heap, size_t size) { - mi_assert_internal(size <= (MI_SMALL_SIZE_MAX + MI_PADDING_SIZE)); - const size_t idx = _mi_wsize_from_size(size); - mi_assert_internal(idx < MI_PAGES_DIRECT); - return heap->pages_free_direct[idx]; -} - -// Segment that contains the pointer -// Large aligned blocks may be aligned at N*MI_SEGMENT_SIZE (inside a huge segment > MI_SEGMENT_SIZE), -// and we need align "down" to the segment info which is `MI_SEGMENT_SIZE` bytes before it; -// therefore we align one byte before `p`. -// We check for NULL afterwards on 64-bit systems to improve codegen for `mi_free`. -static inline mi_segment_t* _mi_ptr_segment(const void* p) { - mi_segment_t* const segment = (mi_segment_t*)(((uintptr_t)p - 1) & ~MI_SEGMENT_MASK); - #if MI_INTPTR_SIZE <= 4 - return (p==NULL ? NULL : segment); - #else - return ((intptr_t)segment <= 0 ? NULL : segment); - #endif +#if MI_PAGE_MAP_FLAT + +// flat page-map committed on demand, using one byte per slice (64 KiB). +// single indirection and low commit, but large initial virtual reserve (4 GiB with 48 bit virtual addresses) +// used by default on <= 40 bit virtual address spaces. +extern mi_decl_hidden uint8_t* _mi_page_map; + +static inline size_t _mi_page_map_index(const void* p) { + return (size_t)((uintptr_t)p >> MI_ARENA_SLICE_SHIFT); +} + +static inline mi_page_t* _mi_ptr_page_ex(const void* p, bool* valid) { + const size_t idx = _mi_page_map_index(p); + const size_t ofs = _mi_page_map[idx]; + if (valid != NULL) { *valid = (ofs != 0); } + return (mi_page_t*)((((uintptr_t)p >> MI_ARENA_SLICE_SHIFT) + 1 - ofs) << MI_ARENA_SLICE_SHIFT); } -static inline mi_page_t* mi_slice_to_page(mi_slice_t* s) { - mi_assert_internal(s->slice_offset== 0 && s->slice_count > 0); - return (mi_page_t*)(s); +static inline mi_page_t* _mi_checked_ptr_page(const void* p) { + bool valid; + mi_page_t* const page = _mi_ptr_page_ex(p, &valid); + return (valid ? page : NULL); } -static inline mi_slice_t* mi_page_to_slice(mi_page_t* p) { - mi_assert_internal(p->slice_offset== 0 && p->slice_count > 0); - return (mi_slice_t*)(p); +static inline mi_page_t* _mi_unchecked_ptr_page(const void* p) { + return _mi_ptr_page_ex(p, NULL); } -// Segment belonging to a page -static inline mi_segment_t* _mi_page_segment(const mi_page_t* page) { - mi_assert_internal(page!=NULL); - mi_segment_t* segment = _mi_ptr_segment(page); - mi_assert_internal(segment == NULL || ((mi_slice_t*)page >= segment->slices && (mi_slice_t*)page < segment->slices + segment->slice_entries)); - return segment; +#else + +// 2-level page map: +// double indirection, but low commit and low virtual reserve. +// +// the page-map is usually 4 MiB (for 48 bit virtual addresses) and points to sub maps of 64 KiB. +// the page-map is committed on-demand (in 64 KiB parts) (and sub-maps are committed on-demand as well) +// one sub page-map = 64 KiB => covers 2^(16-3) * 2^16 = 2^29 = 512 MiB address space +// the page-map needs 48-(16+13) = 19 bits => 2^19 sub map pointers = 2^22 bytes = 4 MiB reserved size. +#define MI_PAGE_MAP_SUB_SHIFT (13) +#define MI_PAGE_MAP_SUB_COUNT (MI_ZU(1) << MI_PAGE_MAP_SUB_SHIFT) +#define MI_PAGE_MAP_SHIFT (MI_MAX_VABITS - MI_PAGE_MAP_SUB_SHIFT - MI_ARENA_SLICE_SHIFT) +#define MI_PAGE_MAP_COUNT (MI_ZU(1) << MI_PAGE_MAP_SHIFT) + +typedef mi_page_t** mi_submap_t; +extern mi_decl_hidden _Atomic(mi_submap_t)* _mi_page_map; + +static inline size_t _mi_page_map_index(const void* p, size_t* sub_idx) { + const size_t u = (size_t)((uintptr_t)p / MI_ARENA_SLICE_SIZE); + if (sub_idx != NULL) { *sub_idx = u % MI_PAGE_MAP_SUB_COUNT; } + return (u / MI_PAGE_MAP_SUB_COUNT); } -static inline mi_slice_t* mi_slice_first(const mi_slice_t* slice) { - mi_slice_t* start = (mi_slice_t*)((uint8_t*)slice - slice->slice_offset); - mi_assert_internal(start >= _mi_ptr_segment(slice)->slices); - mi_assert_internal(start->slice_offset == 0); - mi_assert_internal(start + start->slice_count > slice); - return start; +static inline mi_submap_t _mi_page_map_at(size_t idx) { + return mi_atomic_load_ptr_relaxed(mi_page_t*, &_mi_page_map[idx]); } -// Get the page containing the pointer (performance critical as it is called in mi_free) -static inline mi_page_t* _mi_segment_page_of(const mi_segment_t* segment, const void* p) { - mi_assert_internal(p > (void*)segment); - ptrdiff_t diff = (uint8_t*)p - (uint8_t*)segment; - mi_assert_internal(diff > 0 && diff <= (ptrdiff_t)MI_SEGMENT_SIZE); - size_t idx = (size_t)diff >> MI_SEGMENT_SLICE_SHIFT; - mi_assert_internal(idx <= segment->slice_entries); - mi_slice_t* slice0 = (mi_slice_t*)&segment->slices[idx]; - mi_slice_t* slice = mi_slice_first(slice0); // adjust to the block that holds the page data - mi_assert_internal(slice->slice_offset == 0); - mi_assert_internal(slice >= segment->slices && slice < segment->slices + segment->slice_entries); - return mi_slice_to_page(slice); +static inline mi_page_t* _mi_unchecked_ptr_page(const void* p) { + size_t sub_idx; + const size_t idx = _mi_page_map_index(p, &sub_idx); + return (_mi_page_map_at(idx))[sub_idx]; // NULL if p==NULL } -// Quick page start for initialized pages -static inline uint8_t* mi_page_start(const mi_page_t* page) { - mi_assert_internal(page->page_start != NULL); - mi_assert_expensive(_mi_segment_page_start(_mi_page_segment(page),page,NULL) == page->page_start); - return page->page_start; +static inline mi_page_t* _mi_checked_ptr_page(const void* p) { + size_t sub_idx; + const size_t idx = _mi_page_map_index(p, &sub_idx); + mi_submap_t const sub = _mi_page_map_at(idx); + if mi_unlikely(sub == NULL) return NULL; + return sub[sub_idx]; } -// Get the page containing the pointer -static inline mi_page_t* _mi_ptr_page(void* p) { - mi_assert_internal(p!=NULL); - return _mi_segment_page_of(_mi_ptr_segment(p), p); +#endif + + +static inline mi_page_t* _mi_ptr_page(const void* p) { + mi_assert_internal(p==NULL || mi_is_in_heap_region(p)); + #if MI_DEBUG || MI_SECURE || MI_FREE_IS_CHECKED + return _mi_checked_ptr_page(p); + #else + return _mi_unchecked_ptr_page(p); + #endif } -// Get the block size of a page (special case for huge objects) + +// Get the block size of a page static inline size_t mi_page_block_size(const mi_page_t* page) { mi_assert_internal(page->block_size > 0); return page->block_size; } -static inline bool mi_page_is_huge(const mi_page_t* page) { - mi_assert_internal((page->is_huge && _mi_page_segment(page)->kind == MI_SEGMENT_HUGE) || - (!page->is_huge && _mi_page_segment(page)->kind != MI_SEGMENT_HUGE)); - return page->is_huge; +// Page start +static inline uint8_t* mi_page_start(const mi_page_t* page) { + // multiplication must be done in `size_t`; in a 32-bit multiplication the offset wraps for pages whose blocks start 4 GiB or more after the page meta info + return (uint8_t*)page + ((size_t)page->page_woffset * MI_SIZE_SIZE); } -// Get the usable block size of a page without fixed padding. -// This may still include internal padding due to alignment and rounding up size classes. -static inline size_t mi_page_usable_block_size(const mi_page_t* page) { - return mi_page_block_size(page) - MI_PADDING_SIZE; +static inline size_t mi_page_size(const mi_page_t* page) { + return mi_page_block_size(page) * page->reserved; } -// size of a segment -static inline size_t mi_segment_size(mi_segment_t* segment) { - return segment->segment_slices * MI_SEGMENT_SLICE_SIZE; +static inline uint8_t* mi_page_area(const mi_page_t* page, size_t* size) { + if (size) { *size = mi_page_size(page); } + return mi_page_start(page); } -static inline uint8_t* mi_segment_end(mi_segment_t* segment) { - return (uint8_t*)segment + mi_segment_size(segment); +static inline size_t mi_page_info_size(void) { + return _mi_align_up(sizeof(mi_page_t), MI_MAX_ALIGN_SIZE); } -// Thread free access -static inline mi_block_t* mi_page_thread_free(const mi_page_t* page) { - return (mi_block_t*)(mi_atomic_load_relaxed(&((mi_page_t*)page)->xthread_free) & ~3); +static inline bool mi_page_contains_address(const mi_page_t* page, const void* p) { + size_t psize; + uint8_t* start = mi_page_area(page, &psize); + return (start <= (uint8_t*)p && (uint8_t*)p < start + psize); } -static inline mi_delayed_t mi_page_thread_free_flag(const mi_page_t* page) { - return (mi_delayed_t)(mi_atomic_load_relaxed(&((mi_page_t*)page)->xthread_free) & 3); +static inline bool mi_page_is_in_arena(const mi_page_t* page) { + return (page->memid.memkind == MI_MEM_ARENA); } -// Heap access -static inline mi_heap_t* mi_page_heap(const mi_page_t* page) { - return (mi_heap_t*)(mi_atomic_load_relaxed(&((mi_page_t*)page)->xheap)); +static inline bool mi_page_is_singleton(const mi_page_t* page) { + return (page->reserved == 1); } -static inline void mi_page_set_heap(mi_page_t* page, mi_heap_t* heap) { - mi_assert_internal(mi_page_thread_free_flag(page) != MI_DELAYED_FREEING); - mi_atomic_store_release(&page->xheap,(uintptr_t)heap); - if (heap != NULL) { page->heap_tag = heap->tag; } +// Get the usable block size of a page without fixed padding. +// This may still include internal padding due to alignment and rounding up size classes. +static inline size_t mi_page_usable_block_size(const mi_page_t* page) { + return mi_page_block_size(page) - MI_PADDING_SIZE; } -// Thread free flag helpers -static inline mi_block_t* mi_tf_block(mi_thread_free_t tf) { - return (mi_block_t*)(tf & ~0x03); -} -static inline mi_delayed_t mi_tf_delayed(mi_thread_free_t tf) { - return (mi_delayed_t)(tf & 0x03); +static inline bool mi_page_meta_is_separated(const mi_page_t* page) { + #if MI_PAGE_META_IS_SEPARATED + // usually separated but can still be in front for direct OS allocations (due to size or alignment) or due to MI_PAGE_META_ALIGNED_FREE_SMALL + return (page->memid.memkind == MI_MEM_ARENA && page != _mi_align_down_ptr(mi_page_start(page), MI_ARENA_SLICE_ALIGN)); + #else + MI_UNUSED(page); + return false; + #endif } -static inline mi_thread_free_t mi_tf_make(mi_block_t* block, mi_delayed_t delayed) { - return (mi_thread_free_t)((uintptr_t)block | (uintptr_t)delayed); + +static inline uint8_t* mi_page_slice_start(const mi_page_t* page) { + if (mi_page_meta_is_separated(page)) { + // page meta info is at a separate location (at `arena->pages`) + return (uint8_t*)_mi_align_down_ptr(mi_page_start(page), MI_ARENA_SLICE_ALIGN); + } + else { + // page meta info is at the start of the page slices + return (uint8_t*)page; + } } -static inline mi_thread_free_t mi_tf_set_delayed(mi_thread_free_t tf, mi_delayed_t delayed) { - return mi_tf_make(mi_tf_block(tf),delayed); + +// This gives the offset relative to the start slice of a page. +static inline size_t mi_page_slice_offset_of(const mi_page_t* page, size_t offset_relative_to_page_start) { + return (mi_page_start(page) - mi_page_slice_start(page)) + offset_relative_to_page_start; } -static inline mi_thread_free_t mi_tf_set_block(mi_thread_free_t tf, mi_block_t* block) { - return mi_tf_make(block, mi_tf_delayed(tf)); + +// Currently committed part of a page +static inline size_t mi_page_committed(const mi_page_t* page) { + return (page->slice_committed == 0 ? mi_page_size(page) : page->slice_committed - mi_page_slice_offset_of(page,0)); } // are all blocks in a page freed? @@ -576,51 +762,245 @@ static inline bool mi_page_all_free(const mi_page_t* page) { return (page->used == 0); } -// are there any available blocks? -static inline bool mi_page_has_any_available(const mi_page_t* page) { - mi_assert_internal(page != NULL && page->reserved > 0); - return (page->used < page->reserved || (mi_page_thread_free(page) != NULL)); -} - // are there immediately available blocks, i.e. blocks available on the free list. static inline bool mi_page_immediate_available(const mi_page_t* page) { mi_assert_internal(page != NULL); return (page->free != NULL); } + +// is the page not yet used up to its reserved space? +static inline bool mi_page_is_expandable(const mi_page_t* page) { + mi_assert_internal(page != NULL); + mi_assert_internal(page->capacity <= page->reserved); + return (page->capacity < page->reserved); +} + + +static inline bool mi_page_is_full(const mi_page_t* page) { + const bool full = (page->reserved == page->used); + mi_assert_internal(!full || page->free == NULL); + return full; +} + // is more than 7/8th of a page in use? -static inline bool mi_page_mostly_used(const mi_page_t* page) { +static inline bool mi_page_is_mostly_used(const mi_page_t* page) { if (page==NULL) return true; uint16_t frac = page->reserved / 8U; return (page->reserved - page->used <= frac); } -static inline mi_page_queue_t* mi_page_queue(const mi_heap_t* heap, size_t size) { - return &((mi_heap_t*)heap)->pages[_mi_bin(size)]; +// is more than (n-1)/n'th of a page in use? +static inline bool mi_page_is_used_at_frac(const mi_page_t* page, uint16_t n) { + if (page==NULL) return true; + uint16_t frac = page->reserved / n; + return (page->reserved - page->used <= frac); +} + + +static inline bool mi_page_is_huge(const mi_page_t* page) { + return (mi_page_is_singleton(page) && + (page->block_size > MI_LARGE_MAX_OBJ_SIZE || + (mi_memkind_is_os(page->memid.memkind) && page->memid.mem.os.base < (void*)page))); } +static inline mi_page_queue_t* mi_page_queue(const mi_theap_t* theap, size_t size) { + mi_page_queue_t* const pq = &((mi_theap_t*)theap)->pages[_mi_bin(size)]; + if (size <= MI_LARGE_MAX_OBJ_SIZE) { mi_assert_internal(pq->block_size <= MI_LARGE_MAX_OBJ_SIZE); } + return pq; +} //----------------------------------------------------------- -// Page flags +// Page thread id and flags //----------------------------------------------------------- + +// Thread id of thread that owns this page (with flags in the bottom 2 bits) +static inline mi_threadid_t mi_page_xthread_id(const mi_page_t* page) { + return mi_atomic_load_relaxed(&((mi_page_t*)page)->xthread_id); +} + +// Plain thread id of the thread that owns this page +static inline mi_threadid_t mi_page_thread_id(const mi_page_t* page) { + return (mi_page_xthread_id(page) & ~MI_PAGE_FLAG_MASK); +} + +static inline mi_page_flags_t mi_page_flags(const mi_page_t* page) { + return (mi_page_xthread_id(page) & MI_PAGE_FLAG_MASK); +} + +static inline bool mi_page_flags_set(mi_page_t* page, bool set, mi_page_flags_t newflag) { + mi_page_flags_t old; + if (set) { old = mi_atomic_or_relaxed(&page->xthread_id, newflag); } + else { old = mi_atomic_and_relaxed(&page->xthread_id, ~newflag); } + return ((old & newflag) == newflag); +} + static inline bool mi_page_is_in_full(const mi_page_t* page) { - return page->flags.x.in_full; + return ((mi_page_flags(page) & MI_PAGE_IN_FULL_QUEUE) != 0); } static inline void mi_page_set_in_full(mi_page_t* page, bool in_full) { - page->flags.x.in_full = in_full; + const bool was_in_full = mi_page_flags_set(page, in_full, MI_PAGE_IN_FULL_QUEUE); + if (was_in_full != in_full) { + // optimize: maintain pages_full_size to avoid visiting the full queue (issue #1220) + mi_theap_t* const theap = page->theap; + mi_assert_internal(theap!=NULL); + if (theap != NULL) { + const size_t size = page->capacity * mi_page_block_size(page); + if (in_full) { theap->pages_full_size += size; } + else { mi_assert_internal(size <= theap->pages_full_size); theap->pages_full_size -= size; } + } + } +} + +static inline bool mi_page_has_interior_pointers(const mi_page_t* page) { + return ((mi_page_flags(page) & MI_PAGE_HAS_INTERIOR_POINTERS) != 0); +} + +static inline void mi_page_set_has_interior_pointers(mi_page_t* page, bool has_aligned) { + mi_page_flags_set(page, has_aligned, MI_PAGE_HAS_INTERIOR_POINTERS); +} + +static inline void mi_page_set_theap(mi_page_t* page, mi_theap_t* theap) { + // mi_assert_internal(!mi_page_is_in_full(page)); // can happen when destroying pages on theap_destroy + page->theap = theap; + const mi_threadid_t tid = (theap == NULL ? MI_THREADID_ABANDONED : theap->tld->thread_id); + mi_assert_internal((tid & MI_PAGE_FLAG_MASK) == 0); + + // we need to use an atomic cas since a concurrent thread may still set the MI_PAGE_HAS_INTERIOR_POINTERS flag (see `alloc_aligned.c`). + mi_threadid_t xtid_old = mi_page_xthread_id(page); + mi_threadid_t xtid; + do { + xtid = tid | (xtid_old & MI_PAGE_FLAG_MASK); + } while (!mi_atomic_cas_weak_release(&page->xthread_id, &xtid_old, xtid)); +} + +static inline bool mi_page_is_abandoned(const mi_page_t* page) { + // note: the xtheap field of an abandoned theap is set to the subproc (for fast reclaim-on-free) + return (mi_page_thread_id(page) <= MI_THREADID_ABANDONED_MAPPED); +} + +static inline bool mi_page_is_abandoned_mapped(const mi_page_t* page) { + return (mi_page_thread_id(page) == MI_THREADID_ABANDONED_MAPPED); +} + +static inline void mi_page_set_abandoned_mapped(mi_page_t* page) { + mi_assert_internal(mi_page_is_abandoned(page)); + mi_atomic_or_relaxed(&page->xthread_id, (mi_threadid_t)MI_THREADID_ABANDONED_MAPPED); } -static inline bool mi_page_has_aligned(const mi_page_t* page) { - return page->flags.x.has_aligned; +static inline void mi_page_clear_abandoned_mapped(mi_page_t* page) { + mi_assert_internal(mi_page_is_abandoned_mapped(page)); + mi_atomic_and_relaxed(&page->xthread_id, (mi_threadid_t)MI_PAGE_FLAG_MASK); +} + + +static inline mi_theap_t* mi_page_theap(const mi_page_t* page) { + mi_assert_internal(!mi_page_is_abandoned(page)); + mi_assert_internal(page->theap != NULL); + return page->theap; } -static inline void mi_page_set_has_aligned(mi_page_t* page, bool has_aligned) { - page->flags.x.has_aligned = has_aligned; +static inline mi_tld_t* mi_page_tld(const mi_page_t* page) { + mi_assert_internal(!mi_page_is_abandoned(page)); + mi_assert_internal(page->theap != NULL); + return page->theap->tld; +} + + +static inline mi_heap_t* mi_page_heap(const mi_page_t* page) { + mi_heap_t* heap = page->heap; + // we use NULL for the main heap to make `_mi_page_get_associated_theap` fast in `free.c:mi_abandoned_page_try_reclaim`. + if mi_likely(heap==NULL) heap = mi_heap_main(); + mi_assert_internal(heap != NULL); + return heap; +} + +//----------------------------------------------------------- +// Thread free list and ownership +//----------------------------------------------------------- + +// Thread free flag helpers +static inline mi_block_t* mi_tf_block(mi_thread_free_t tf) { + return (mi_block_t*)(tf & ~1); +} +static inline bool mi_tf_is_owned(mi_thread_free_t tf) { + return ((tf & 1) == 1); +} +static inline mi_thread_free_t mi_tf_create(mi_block_t* block, bool owned) { + return (mi_thread_free_t)((uintptr_t)block | (owned ? 1 : 0)); +} + +// Thread free access +static inline mi_block_t* mi_page_thread_free(const mi_page_t* page) { + return mi_tf_block(mi_atomic_load_relaxed(&((mi_page_t*)page)->xthread_free)); +} + +// are there any available blocks? +static inline bool mi_page_has_any_available(const mi_page_t* page) { + mi_assert_internal(page != NULL && page->reserved > 0); + return (page->used < page->reserved || (mi_page_thread_free(page) != NULL)); +} + +// Owned? +static inline bool mi_page_is_owned(const mi_page_t* page) { + return mi_tf_is_owned(mi_atomic_load_relaxed(&((mi_page_t*)page)->xthread_free)); +} + +// get ownership; returns true if the page was not owned before. +static inline bool mi_page_claim_ownership(mi_page_t* page) { + const uintptr_t old = mi_atomic_or_acq_rel(&page->xthread_free, (uintptr_t)1); + return ((old&1)==0); } +/* ------------------------------------------------------------------- + Guarded objects +------------------------------------------------------------------- */ +#if MI_GUARDED +// we always align guarded pointers in a block at an offset +// the block `next` field is then used as a tag to distinguish regular offset aligned blocks from guarded ones +#define MI_BLOCK_TAG_ALIGNED ((mi_encoded_t)(0)) +#define MI_BLOCK_TAG_GUARDED (~MI_BLOCK_TAG_ALIGNED) +#endif + +static inline bool mi_block_ptr_is_guarded(const mi_block_t* block, const void* p) { +#if MI_GUARDED + const ptrdiff_t offset = (uint8_t*)p - (uint8_t*)block; + return (offset >= (ptrdiff_t)(sizeof(mi_block_t)) && block->next == MI_BLOCK_TAG_GUARDED); +#else + MI_UNUSED(block); MI_UNUSED(p); + return false; +#endif +} + +#if MI_GUARDED +static inline bool mi_theap_malloc_use_guarded(mi_theap_t* theap, size_t size) { + // this code is written to result in fast assembly as it is on the hot path for allocation + const size_t count = theap->guarded_sample_count - 1; // if the rate was 0, this will underflow and count for a long time.. + if mi_likely(count != 0) { + // no sample + theap->guarded_sample_count = count; + return false; + } + else if (size >= theap->guarded_size_min && size <= theap->guarded_size_max) { + // use guarded allocation + theap->guarded_sample_count = theap->guarded_sample_rate; // reset + return (theap->guarded_sample_rate != 0); + } + else { + // failed size criteria, rewind count (but don't write to an empty theap) + if (theap->guarded_sample_rate != 0) { theap->guarded_sample_count = 1; } + return false; + } +} + +mi_decl_restrict void* _mi_theap_malloc_guarded(mi_theap_t* theap, size_t size, bool zero) mi_attr_noexcept; + +#endif + + /* ------------------------------------------------------------------- Encoding/Decoding the free list next pointers @@ -646,27 +1026,10 @@ We also pass a separate `null` value to be used as `NULL` or otherwise `(k2<<> (MI_INTPTR_BITS - shift)))); -} -static inline uintptr_t mi_rotr(uintptr_t x, uintptr_t shift) { - shift %= MI_INTPTR_BITS; - return (shift==0 ? x : ((x >> shift) | (x << (MI_INTPTR_BITS - shift)))); + mi_page_t* page = _mi_ptr_page(p); + return mi_page_contains_address(page,q); + // return (_mi_ptr_page(p) == _mi_ptr_page(q)); } static inline void* mi_ptr_decode(const void* null, const mi_encoded_t x, const uintptr_t* keys) { @@ -679,10 +1042,20 @@ static inline mi_encoded_t mi_ptr_encode(const void* null, const void* p, const return mi_rotl(x ^ keys[1], keys[0]) + keys[0]; } +static inline uint32_t mi_ptr_encode_canary(const void* null, const void* p, const uintptr_t* keys) { + const uint32_t x = (uint32_t)(mi_ptr_encode(null,p,keys)); + // make the lowest byte 0 to prevent spurious read overflows which could be a security issue (issue #951) + #if MI_BIG_ENDIAN + return (x & 0x00FFFFFF); + #else + return (x & 0xFFFFFF00); + #endif +} + static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, const uintptr_t* keys ) { mi_track_mem_defined(block,sizeof(mi_block_t)); mi_block_t* next; - #ifdef MI_ENCODE_FREELIST + #if MI_ENCODE_FREELIST next = (mi_block_t*)mi_ptr_decode(null, block->next, keys); #else MI_UNUSED(keys); MI_UNUSED(null); @@ -694,7 +1067,7 @@ static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* bl static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, const uintptr_t* keys) { mi_track_mem_undefined(block,sizeof(mi_block_t)); - #ifdef MI_ENCODE_FREELIST + #if MI_ENCODE_FREELIST block->next = mi_ptr_encode(null, next, keys); #else MI_UNUSED(keys); MI_UNUSED(null); @@ -704,7 +1077,7 @@ static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const } static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* block) { - #ifdef MI_ENCODE_FREELIST + #if MI_ENCODE_FREELIST mi_block_t* next = mi_block_nextx(page,block,page->keys); // check for free list corruption: is `next` at least in the same page? // TODO: check if `next` is `page->block_size` aligned? @@ -720,7 +1093,7 @@ static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* } static inline void mi_block_set_next(const mi_page_t* page, mi_block_t* block, const mi_block_t* next) { - #ifdef MI_ENCODE_FREELIST + #if MI_ENCODE_FREELIST mi_block_set_nextx(page,block,next, page->keys); #else MI_UNUSED(page); @@ -728,50 +1101,20 @@ static inline void mi_block_set_next(const mi_page_t* page, mi_block_t* block, c #endif } +/* ----------------------------------------------------------- + arena blocks +----------------------------------------------------------- */ -// ------------------------------------------------------------------- -// commit mask -// ------------------------------------------------------------------- - -static inline void mi_commit_mask_create_empty(mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - cm->mask[i] = 0; - } -} - -static inline void mi_commit_mask_create_full(mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - cm->mask[i] = ~((size_t)0); - } -} - -static inline bool mi_commit_mask_is_empty(const mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - if (cm->mask[i] != 0) return false; - } - return true; +// Blocks needed for a given byte size +static inline size_t mi_slice_count_of_size(size_t size) { + return _mi_divide_up(size, MI_ARENA_SLICE_SIZE); } -static inline bool mi_commit_mask_is_full(const mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - if (cm->mask[i] != ~((size_t)0)) return false; - } - return true; +// Byte size of a number of blocks +static inline size_t mi_size_of_slices(size_t bcount) { + return (bcount * MI_ARENA_SLICE_SIZE); } -// defined in `segment.c`: -size_t _mi_commit_mask_committed_size(const mi_commit_mask_t* cm, size_t total); -size_t _mi_commit_mask_next_run(const mi_commit_mask_t* cm, size_t* idx); - -#define mi_commit_mask_foreach(cm,idx,count) \ - idx = 0; \ - while ((count = _mi_commit_mask_next_run(cm,&idx)) > 0) { - -#define mi_commit_mask_foreach_end() \ - idx += count; \ - } - - /* ----------------------------------------------------------- memory id's @@ -788,14 +1131,27 @@ static inline mi_memid_t _mi_memid_none(void) { return _mi_memid_create(MI_MEM_NONE); } -static inline mi_memid_t _mi_memid_create_os(bool committed, bool is_zero, bool is_large) { +static inline mi_memid_t _mi_memid_create_os(void* base, size_t size, bool committed, bool is_zero, bool is_large) { mi_memid_t memid = _mi_memid_create(MI_MEM_OS); + memid.mem.os.base = base; + memid.mem.os.size = size; memid.initially_committed = committed; memid.initially_zero = is_zero; memid.is_pinned = is_large; return memid; } +static inline mi_memid_t _mi_memid_create_meta(void* mpage, size_t block_idx, size_t block_count) { + mi_memid_t memid = _mi_memid_create(MI_MEM_META); + memid.mem.meta.meta_page = mpage; + memid.mem.meta.block_index = (uint32_t)block_idx; + memid.mem.meta.block_count = (uint32_t)block_count; + memid.initially_committed = true; + memid.initially_zero = true; + memid.is_pinned = true; + return memid; +} + // ------------------------------------------------------------------- // Fast "random" shuffle @@ -803,7 +1159,7 @@ static inline mi_memid_t _mi_memid_create_os(bool committed, bool is_zero, bool static inline uintptr_t _mi_random_shuffle(uintptr_t x) { if (x==0) { x = 17; } // ensure we don't get stuck in generating zeros -#if (MI_INTPTR_SIZE==8) +#if (MI_INTPTR_SIZE>=8) // by Sebastiano Vigna, see: x ^= x >> 30; x *= 0xbf58476d1ce4e5b9UL; @@ -821,165 +1177,65 @@ static inline uintptr_t _mi_random_shuffle(uintptr_t x) { return x; } -// ------------------------------------------------------------------- -// Optimize numa node access for the common case (= one node) -// ------------------------------------------------------------------- - -int _mi_os_numa_node_get(mi_os_tld_t* tld); -size_t _mi_os_numa_node_count_get(void); - -extern _Atomic(size_t) _mi_numa_node_count; -static inline int _mi_os_numa_node(mi_os_tld_t* tld) { - if mi_likely(mi_atomic_load_relaxed(&_mi_numa_node_count) == 1) { return 0; } - else return _mi_os_numa_node_get(tld); -} -static inline size_t _mi_os_numa_node_count(void) { - const size_t count = mi_atomic_load_relaxed(&_mi_numa_node_count); - if mi_likely(count > 0) { return count; } - else return _mi_os_numa_node_count_get(); -} +// --------------------------------------------------------------------------------- +// Provide our own `_mi_memcpy/set` for potential performance optimizations. +// +// For now, only on x64/x86 we optimize to `rep movsb/stosb`. +// Generally, we check for "fast short rep movsb/stosb" (FSRM/FSRS) or "fast enhanced rep movsb" (ERMS) support +// (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017). See also issue #201 and pr #253. +// Todo: we see improvements on win32 but less with glibc; we might want to only enable this on windows. +// --------------------------------------------------------------------------------- +#if !MI_TRACK_ENABLED && (MI_ARCH_X64 || MI_ARCH_X86) && (defined(_WIN32) || defined(__GNUC__)) -// ----------------------------------------------------------------------- -// Count bits: trailing or leading zeros (with MI_INTPTR_BITS on all zero) -// ----------------------------------------------------------------------- - -#if defined(__GNUC__) - -#include // LONG_MAX -#define MI_HAVE_FAST_BITSCAN -static inline size_t mi_clz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; -#if (INTPTR_MAX == LONG_MAX) - return __builtin_clzl(x); -#else - return __builtin_clzll(x); -#endif -} -static inline size_t mi_ctz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; -#if (INTPTR_MAX == LONG_MAX) - return __builtin_ctzl(x); -#else - return __builtin_ctzll(x); -#endif -} - -#elif defined(_MSC_VER) +extern mi_decl_hidden size_t _mi_cpu_movsb_max; // in init.c +extern mi_decl_hidden size_t _mi_cpu_stosb_max; -#include // LONG_MAX -#include // BitScanReverse64 -#define MI_HAVE_FAST_BITSCAN -static inline size_t mi_clz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; - unsigned long idx; -#if (INTPTR_MAX == LONG_MAX) - _BitScanReverse(&idx, x); -#else - _BitScanReverse64(&idx, x); -#endif - return ((MI_INTPTR_BITS - 1) - idx); -} -static inline size_t mi_ctz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; - unsigned long idx; -#if (INTPTR_MAX == LONG_MAX) - _BitScanForward(&idx, x); -#else - _BitScanForward64(&idx, x); -#endif - return idx; -} - -#else -static inline size_t mi_ctz32(uint32_t x) { - // de Bruijn multiplication, see - static const unsigned char debruijn[32] = { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - if (x==0) return 32; - return debruijn[((x & -(int32_t)x) * 0x077CB531UL) >> 27]; -} -static inline size_t mi_clz32(uint32_t x) { - // de Bruijn multiplication, see - static const uint8_t debruijn[32] = { - 31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1, - 23, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0 - }; - if (x==0) return 32; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - return debruijn[(uint32_t)(x * 0x07C4ACDDUL) >> 27]; -} - -static inline size_t mi_clz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; -#if (MI_INTPTR_BITS <= 32) - return mi_clz32((uint32_t)x); -#else - size_t count = mi_clz32((uint32_t)(x >> 32)); - if (count < 32) return count; - return (32 + mi_clz32((uint32_t)x)); -#endif -} -static inline size_t mi_ctz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; -#if (MI_INTPTR_BITS <= 32) - return mi_ctz32((uint32_t)x); -#else - size_t count = mi_ctz32((uint32_t)x); - if (count < 32) return count; - return (32 + mi_ctz32((uint32_t)(x>>32))); -#endif +static inline void mi_rep_movsb(void* dst, const void* src, size_t n) { + #if defined(__GNUC__) + __asm volatile("rep movsb" : "+D"(dst), "+c"(n), "+S"(src) : : "memory"); + #else + __movsb((unsigned char*)dst, (const unsigned char*)src, n); + #endif } -#endif - -// "bit scan reverse": Return index of the highest bit (or MI_INTPTR_BITS if `x` is zero) -static inline size_t mi_bsr(uintptr_t x) { - return (x==0 ? MI_INTPTR_BITS : MI_INTPTR_BITS - 1 - mi_clz(x)); +static inline void mi_rep_stosb(void* dst, uint8_t val, size_t n) { + #if defined(__GNUC__) + __asm volatile("rep stosb" : "+D"(dst), "+c"(n) : "a"(val) : "memory"); + #else + __stosb((unsigned char*)dst, val, n); + #endif } - -// --------------------------------------------------------------------------------- -// Provide our own `_mi_memcpy` for potential performance optimizations. -// -// For now, only on Windows with msvc/clang-cl we optimize to `rep movsb` if -// we happen to run on x86/x64 cpu's that have "fast short rep movsb" (FSRM) support -// (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017). See also issue #201 and pr #253. -// --------------------------------------------------------------------------------- - -#if !MI_TRACK_ENABLED && defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) -#include -extern bool _mi_cpu_has_fsrm; static inline void _mi_memcpy(void* dst, const void* src, size_t n) { - if (_mi_cpu_has_fsrm) { - __movsb((unsigned char*)dst, (const unsigned char*)src, n); + if mi_likely(n <= _mi_cpu_movsb_max) { // has fsrm && n <= 127 (todo: and maybe has erms?) + mi_rep_movsb(dst, src, n); } else { memcpy(dst, src, n); } } -static inline void _mi_memzero(void* dst, size_t n) { - if (_mi_cpu_has_fsrm) { - __stosb((unsigned char*)dst, 0, n); + +static inline void _mi_memset(void* dst, int val, size_t n) { + if mi_likely(n <= _mi_cpu_stosb_max) { // has fsrs && n <= 127 + mi_rep_stosb(dst, (uint8_t)val, n); } else { - memset(dst, 0, n); + memset(dst, val, n); } } + #else + static inline void _mi_memcpy(void* dst, const void* src, size_t n) { memcpy(dst, src, n); } -static inline void _mi_memzero(void* dst, size_t n) { - memset(dst, 0, n); + +static inline void _mi_memset(void* dst, int val, size_t n) { + memset(dst, val, n); } + #endif // ------------------------------------------------------------------------------- @@ -988,6 +1244,7 @@ static inline void _mi_memzero(void* dst, size_t n) { // ------------------------------------------------------------------------------- #if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + // On GCC/CLang we provide a hint that the pointers are word aligned. static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { mi_assert_internal(((uintptr_t)dst % MI_INTPTR_SIZE == 0) && ((uintptr_t)src % MI_INTPTR_SIZE == 0)); @@ -996,23 +1253,35 @@ static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { _mi_memcpy(adst, asrc, n); } -static inline void _mi_memzero_aligned(void* dst, size_t n) { +static inline void _mi_memset_aligned(void* dst, int val, size_t n) { mi_assert_internal((uintptr_t)dst % MI_INTPTR_SIZE == 0); void* adst = __builtin_assume_aligned(dst, MI_INTPTR_SIZE); - _mi_memzero(adst, n); + _mi_memset(adst, val, n); } + #else + // Default fallback on `_mi_memcpy` static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { mi_assert_internal(((uintptr_t)dst % MI_INTPTR_SIZE == 0) && ((uintptr_t)src % MI_INTPTR_SIZE == 0)); _mi_memcpy(dst, src, n); } -static inline void _mi_memzero_aligned(void* dst, size_t n) { +static inline void _mi_memset_aligned(void* dst, int val, size_t n) { mi_assert_internal((uintptr_t)dst % MI_INTPTR_SIZE == 0); - _mi_memzero(dst, n); + _mi_memset(dst, val, n); } + #endif +static inline void _mi_memzero(void* dst, size_t n) { + _mi_memset(dst, 0, n); +} -#endif +static inline void _mi_memzero_aligned(void* dst, size_t n) { + _mi_memset_aligned(dst, 0, n); +} + + + +#endif // MI_INTERNAL_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/prim.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/prim.h index 3f4574dd..a58fb273 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/prim.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/prim.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,7 +7,7 @@ terms of the MIT license. A copy of the license can be found in the file #pragma once #ifndef MIMALLOC_PRIM_H #define MIMALLOC_PRIM_H - +#include "internal.h" // mi_decl_hidden // -------------------------------------------------------------------------- // This file specifies the primitive portability API. @@ -22,12 +22,15 @@ terms of the MIT license. A copy of the license can be found in the file // OS memory configuration typedef struct mi_os_mem_config_s { - size_t page_size; // default to 4KiB - size_t large_page_size; // 0 if not supported, usually 2MiB (4MiB on Windows) - size_t alloc_granularity; // smallest allocation size (usually 4KiB, on Windows 64KiB) - bool has_overcommit; // can we reserve more memory than can be actually committed? - bool has_partial_free; // can allocated blocks be freed partially? (true for mmap, false for VirtualAlloc) - bool has_virtual_reserve; // supports virtual address space reservation? (if true we can reserve virtual address space without using commit or physical memory) + size_t page_size; // default to 4KiB + size_t large_page_size; // 0 if not supported, usually 2MiB (4MiB on Windows) + size_t alloc_granularity; // smallest allocation size (usually 4KiB, on Windows 64KiB) + size_t physical_memory_in_kib; // physical memory size in KiB + size_t virtual_address_bits; // usually 48 or 56 bits on 64-bit systems. (used to determine secure randomization) + bool has_overcommit; // can we reserve more memory than can be actually committed? + bool has_partial_free; // can allocated blocks be freed partially? (true for mmap, false for VirtualAlloc) + bool has_virtual_reserve; // supports virtual address space reservation? (if true we can reserve virtual address space without using commit or physical memory) + bool has_transparent_huge_pages; // true if transparent huge pages are enabled (on Linux) } mi_os_mem_config_t; // Initialize @@ -41,9 +44,10 @@ int _mi_prim_free(void* addr, size_t size ); // If `commit` is false, the virtual memory range only needs to be reserved (with no access) // which will later be committed explicitly using `_mi_prim_commit`. // `is_zero` is set to true if the memory was zero initialized (as on most OS's) +// The `hint_addr` address is either `NULL` or a preferred allocation address but can be ignored. // pre: !commit => !allow_large // try_alignment >= _mi_os_page_size() and a power of 2 -int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr); +int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr); // Commit memory. Returns error code or 0 on success. // For example, on Linux this would make the memory PROT_READ|PROT_WRITE. @@ -56,10 +60,15 @@ int _mi_prim_commit(void* addr, size_t size, bool* is_zero); // pre: needs_recommit != NULL int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit); -// Reset memory. The range keeps being accessible but the content might be reset. +// Reset memory. The range keeps being accessible but the content might be reset to zero at any moment. // Returns error code or 0 on success. int _mi_prim_reset(void* addr, size_t size); +// Reuse memory. This is called for memory that is already committed but +// may have been reset (`_mi_prim_reset`) or decommitted (`_mi_prim_decommit`) where `needs_recommit` was false. +// Returns error code or 0 on success. On most platforms this is a no-op. +int _mi_prim_reuse(void* addr, size_t size); + // Protect memory. Returns error code or 0 on success. int _mi_prim_protect(void* addr, size_t size, bool protect); @@ -98,7 +107,9 @@ void _mi_prim_out_stderr( const char* msg ); // Get an environment variable. (only for options) // name != NULL, result != NULL, result_size >= 64 -bool _mi_prim_getenv(const char* name, char* result, size_t result_size); +// Return 1 for success, 0 if not found, +// and -1 on error (for example, if `getenv` cannot be called yet during preloading). +int _mi_prim_getenv(const char* name, char* result, size_t result_size); // Fill a buffer with strong randomness; return `false` on error or if @@ -111,18 +122,21 @@ void _mi_prim_thread_init_auto_done(void); // Called on process exit and may take action to clean up resources associated with the thread auto done. void _mi_prim_thread_done_auto_done(void); -// Called when the default heap for a thread changes -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap); +// Called when the default theap for a thread changes +void _mi_prim_thread_associate_default_theap(mi_theap_t* theap); + +// Is this thread part of a thread pool? +bool _mi_prim_thread_is_in_threadpool(void); +// Yield to other threads. Should be similar to `sleep(0)`. +// Is called only in rare situations and does not have to be lightning fast. +void _mi_prim_thread_yield(void); //------------------------------------------------------------------- -// Thread id: `_mi_prim_thread_id()` -// -// Getting the thread id should be performant as it is called in the -// fast path of `_mi_free` and we specialize for various platforms as -// inlined definitions. Regular code should call `init.c:_mi_thread_id()`. -// We only require _mi_prim_thread_id() to return a unique id -// for each thread (unequal to zero). +// Access to TLS (thread local storage) slots. +// We need fast access to both a unique thread id (in `free.c:mi_free`) and +// to a thread-local theap pointer (in `alloc.c:mi_malloc`). +// To achieve this we use specialized code for various platforms. //------------------------------------------------------------------- // On some libc + platform combinations we can directly access a thread-local storage (TLS) slot. @@ -132,21 +146,28 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap); // // Note: we would like to prefer `__builtin_thread_pointer()` nowadays instead of using assembly, // but unfortunately we can not detect support reliably (see issue #883) -// We also use it on Apple OS as we use a TLS slot for the default heap there. -#if defined(__GNUC__) && ( \ - (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))) \ +// We also use it on Apple OS as we use a TLS slot for the default theap there. +#if (defined(_WIN32)) || \ + (defined(__GNUC__) && ( \ + (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) || defined(__riscv))) \ || (defined(__APPLE__) && (defined(__x86_64__) || defined(__aarch64__) || defined(__POWERPC__))) \ - || (defined(__BIONIC__) && (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))) \ + || (defined(__BIONIC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__))) \ || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ || (defined(__OpenBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ - ) - -#define MI_HAS_TLS_SLOT + )) static inline void* mi_prim_tls_slot(size_t slot) mi_attr_noexcept { void* res; const size_t ofs = (slot*sizeof(void*)); - #if defined(__i386__) + #if defined(_WIN32) + #if (_M_X64 || _M_AMD64) && !defined(_M_ARM64EC) + res = (void*)__readgsqword((unsigned long)ofs); // direct load at offset from gs + #elif _M_IX86 && !defined(_M_ARM64EC) + res = (void*)__readfsdword((unsigned long)ofs); // direct load at offset from fs + #else + res = ((void**)NtCurrentTeb())[slot]; MI_UNUSED(ofs); + #endif + #elif defined(__i386__) __asm__("movl %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86 32-bit always uses GS #elif defined(__APPLE__) && defined(__x86_64__) __asm__("movq %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 macOSX uses GS @@ -166,17 +187,31 @@ static inline void* mi_prim_tls_slot(size_t slot) mi_attr_noexcept { __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); #endif res = tcb[slot]; + #elif defined(__riscv) + void** tcb; MI_UNUSED(ofs); + __asm__ volatile ("mv %0, tp" : "=r" (tcb)); + res = tcb[slot]; #elif defined(__APPLE__) && defined(__POWERPC__) // ppc, issue #781 MI_UNUSED(ofs); res = pthread_getspecific(slot); + #else + #define MI_HAS_TLS_SLOT 0 + MI_UNUSED(ofs); + res = NULL; #endif return res; } +#ifndef MI_HAS_TLS_SLOT +#define MI_HAS_TLS_SLOT 1 +#endif + // setting a tls slot is only used on macOS for now static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexcept { const size_t ofs = (slot*sizeof(void*)); - #if defined(__i386__) + #if defined(_WIN32) + ((void**)NtCurrentTeb())[slot] = value; MI_UNUSED(ofs); + #elif defined(__i386__) __asm__("movl %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // 32-bit always uses GS #elif defined(__APPLE__) && defined(__x86_64__) __asm__("movq %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 macOS uses GS @@ -196,64 +231,89 @@ static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexce __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); #endif tcb[slot] = value; + #elif defined(__riscv) + void** tcb; MI_UNUSED(ofs); + __asm__ volatile ("mv %0, tp" : "=r" (tcb)); + tcb[slot] = value; #elif defined(__APPLE__) && defined(__POWERPC__) // ppc, issue #781 MI_UNUSED(ofs); pthread_setspecific(slot, value); + #else + MI_UNUSED(ofs); MI_UNUSED(value); #endif } #endif + +// defined in `init.c`; do not use these directly +extern mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_main; // theap belonging to the main heap +extern mi_decl_hidden bool _mi_process_is_initialized; // has mi_process_init been called? + + +//------------------------------------------------------------------- +// Get a fast unique thread id. +// +// Getting the thread id should be performant as it is called in the +// fast path of `_mi_free` and we specialize for various platforms as +// inlined definitions. Regular code should call `init.c:_mi_thread_id()`. +// We only require _mi_prim_thread_id() to return a unique id +// for each thread (unequal to zero) with the bottom 2 bits clear. +//------------------------------------------------------------------- + // Do we have __builtin_thread_pointer? This would be the preferred way to get a unique thread id // but unfortunately, it seems we cannot test for this reliably at this time (see issue #883) // Nevertheless, it seems needed on older graviton platforms (see issue #851). // For now, we only enable this for specific platforms. -#if !defined(__APPLE__) /* on apple (M1) the wrong register is read (tpidr_el0 instead of tpidrro_el0) so fall back to TLS slot assembly ()*/ \ - && !defined(MI_LIBC_MUSL) \ - && (!defined(__clang_major__) || __clang_major__ >= 14) /* older clang versions emit bad code; fall back to using the TLS slot () */ - #if (defined(__GNUC__) && (__GNUC__ >= 7) && defined(__aarch64__)) /* aarch64 for older gcc versions (issue #851) */ \ - || (defined(__GNUC__) && (__GNUC__ >= 11) && defined(__x86_64__)) \ - || (defined(__clang_major__) && (__clang_major__ >= 14) && (defined(__aarch64__) || defined(__x86_64__))) - #define MI_USE_BUILTIN_THREAD_POINTER 1 +#if !defined(MI_USE_BUILTIN_THREAD_POINTER) /* allow user override */ + #if !defined(__APPLE__) /* on apple (M1) the wrong register is read (tpidr_el0 instead of tpidrro_el0) so fall back to TLS slot assembly ()*/ \ + && !defined(__CYGWIN__) \ + && !defined(MI_LIBC_MUSL) \ + && (!defined(__clang_major__) || __clang_major__ >= 14) /* older clang versions emit bad code; fall back to using the TLS slot () */ + #if (defined(__GNUC__) && (__GNUC__ >= 7) && defined(__aarch64__)) /* aarch64 for older gcc versions (issue #851) */ \ + || (defined(__GNUC__) && (__GNUC__ >= 7) && defined(__riscv)) \ + || (defined(__GNUC__) && (__GNUC__ >= 11) && defined(__x86_64__)) \ + || (defined(__clang_major__) && (__clang_major__ >= 14) && (defined(__aarch64__) || defined(__x86_64__))) + #define MI_USE_BUILTIN_THREAD_POINTER 1 + #endif #endif #endif +static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept; - -// defined in `init.c`; do not use these directly -extern mi_decl_thread mi_heap_t* _mi_heap_default; // default heap to allocate from -extern bool _mi_process_is_initialized; // has mi_process_init been called? - -static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept; +static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { + const mi_threadid_t tid = __mi_prim_thread_id(); + mi_assert_internal(tid > 1); + mi_assert_internal((tid & MI_PAGE_FLAG_MASK) == 0); // bottom 2 bits are clear? + return tid; +} // Get a unique id for the current thread. #if defined(MI_PRIM_THREAD_ID) static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { - return MI_PRIM_THREAD_ID(); // used for example by CPython for a free threaded build (see python/cpython#115488) + const mi_threadid_t tid = MI_PRIM_THREAD_ID(); // used for example by CPython for a free threaded build (see python/cpython#115488) + mi_assert_internal( (tid & 0x03) == 0 ); // mimalloc reserves the bottom 2 bits + return tid; } #elif defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { +static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { // Windows: works on Intel and ARM in both 32- and 64-bit return (uintptr_t)NtCurrentTeb(); } #elif MI_USE_BUILTIN_THREAD_POINTER -static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { +static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { // Works on most Unix based platforms with recent compilers return (uintptr_t)__builtin_thread_pointer(); } -#elif defined(MI_HAS_TLS_SLOT) +#elif MI_HAS_TLS_SLOT -static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { +static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { #if defined(__BIONIC__) // issue #384, #495: on the Bionic libc (Android), slot 1 is the thread id // see: https://github.com/aosp-mirror/platform_bionic/blob/c44b1d0676ded732df4b3b21c5f798eacae93228/libc/platform/bionic/tls_defines.h#L86 @@ -269,8 +329,8 @@ static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { #else // otherwise use portable C, taking the address of a thread local variable (this is still very fast on most platforms). -static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { - return (uintptr_t)&_mi_heap_default; +static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { + return (uintptr_t)&__mi_theap_main; } #endif @@ -278,96 +338,225 @@ static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { /* ---------------------------------------------------------------------------------------- -The thread local default heap: `_mi_prim_get_default_heap()` -This is inlined here as it is on the fast path for allocation functions. +Get the thread local default theap: `_mi_theap_default()` (and the cached heap `_mi_theap_cached`). -On most platforms (Windows, Linux, FreeBSD, NetBSD, etc), this just returns a -__thread local variable (`_mi_heap_default`). With the initial-exec TLS model this ensures -that the storage will always be available (allocated on the thread stacks). - -On some platforms though we cannot use that when overriding `malloc` since the underlying -TLS implementation (or the loader) will call itself `malloc` on a first access and recurse. -We try to circumvent this in an efficient way: -- macOSX : we use an unused TLS slot from the OS allocated slots (MI_TLS_SLOT). On OSX, the - loader itself calls `malloc` even before the modules are initialized. -- OpenBSD: we use an unused slot from the pthread block (MI_TLS_PTHREAD_SLOT_OFS). -- DragonFly: defaults are working but seem slow compared to freeBSD (see PR #323) +This is inlined here as it is on the fast path for allocation functions. +We have 4 models: + +- MI_TLS_MODEL_THREAD_LOCAL: use regular thread local (default on Linux, FreeBSD, etc) + On most platforms (Linux, FreeBSD, NetBSD, etc), this just returns a + thread local variable (`__mi_theap_default`). With the initial-exec TLS model this ensures + that the storage will always be available and properly initialized (with an empty theap). + + On some platforms the underlying TLS implementation (or the loader) will call itself `malloc` + on a first access to a thread local and recurse in the MI_TLS_MODEL_THREAD_LOCAL. + A way around this is to define MI_TLS_RECURSE_GUARD which adds an extra check if the process + is initialized before accessing the thread-local. This is a check in the fast path though + so this should be avoided. + +- MI_TLS_MODEL_FIXED_SLOT: use a fixed slot in the TLS block (default on macOS) + This reserves an unused and fixed TLS slot. This is fast and avoids the problem + where the underlying TLS implementation (or the loader) will call itself `malloc` + on a first access to a thread local (and recurse in the MI_TLS_MODEL_THREAD_LOCAL). + This goes wrong though if the OS or a library uses the same fixed slot. + +- MI_TLS_MODEL_DYNAMIC_WIN32: use a dynamically allocated slot with TlsAlloc. (default on Windows) + Windows has somewhat slow thread locals so by default we use TlsAlloc'd slots which + can be more efficient. First tries to use one of the "direct" first 64 slots which + are the fastest, but falls back to using "expansion" slots when needed (up to 1088 slots). + (If the allocated slot happens to always be under 64 for a particular program, + one might use cmake with `-DMI_WIN_DIRECT_TLS=ON` to skip the expansion slot test in the fast path.) + +- MI_TLS_MODEL_DYNAMIC_PTHREADS: use `pthread_getspecific`. (default on OpenBSD, maybe good for Android as well?) + Use pthread local storage. Somewhat slow but can work well depending on the platform. + +Each model should define `MI_THEAP_INITASNULL` to signify that the initial value +returned from `_mi_theap_default()` can be `NULL` (instead of the address of the empty heap). +This incurs an extra check in the fast path (but can often be combined in an existing check). ------------------------------------------------------------------------------------------- */ -static inline mi_heap_t* mi_prim_get_default_heap(void); - -#if defined(MI_MALLOC_OVERRIDE) -#if defined(__APPLE__) // macOS - #define MI_TLS_SLOT 89 // seems unused? - // other possible unused ones are 9, 29, __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4 (94), __PTK_FRAMEWORK_GC_KEY9 (112) and __PTK_FRAMEWORK_OLDGC_KEY9 (89) - // see -#elif defined(__OpenBSD__) - // use end bytes of a name; goes wrong if anyone uses names > 23 characters (ptrhread specifies 16) - // see - #define MI_TLS_PTHREAD_SLOT_OFS (6*sizeof(int) + 4*sizeof(void*) + 24) - // #elif defined(__DragonFly__) - // #warning "mimalloc is not working correctly on DragonFly yet." - // #define MI_TLS_PTHREAD_SLOT_OFS (4 + 1*sizeof(void*)) // offset `uniqueid` (also used by gdb?) -#elif defined(__ANDROID__) - // See issue #381 - #define MI_TLS_PTHREAD -#endif +static inline mi_theap_t* _mi_theap_default(void); +static inline mi_theap_t* _mi_theap_cached(void); + +// Default TLS model +#if !defined(MI_TLS_MODEL_THREAD_LOCAL) && !defined(MI_TLS_MODEL_DYNAMIC_PTHREADS) + #if defined(_WIN32) + #define MI_TLS_MODEL_DYNAMIC_WIN32 1 + #elif defined(__APPLE__) && MI_HAS_TLS_SLOT && !defined(__POWERPC__) // macOS on arm64 or x64 + // #define MI_TLS_MODEL_DYNAMIC_PTHREADS 1 // also works but a tad slower + #define MI_TLS_MODEL_FIXED_SLOT 1 + // we use the last 2 7-bit slots which seem unused. + // @apple: it would be great to get 2 official slots for custom allocators :-) + #define MI_TLS_MODEL_FIXED_SLOT_DEFAULT 126 + #define MI_TLS_MODEL_FIXED_SLOT_CACHED 127 + // see for assigned slots + #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__ANDROID__) + #define MI_TLS_MODEL_DYNAMIC_PTHREADS 1 + // #define MI_TLS_MODEL_DYNAMIC_PTHREADS_DEFAULT_ENTRY_IS_NULL 1 + #else + #define MI_TLS_MODEL_THREAD_LOCAL 1 + #endif #endif +#if !defined(MI_TLS_RECURSE_GUARD) && MI_TLS_MODEL_THREAD_LOCAL && defined(__APPLE__) +#define MI_TLS_RECURSE_GUARD 1 // macOS can allocate on thread-local initialization +#endif -#if defined(MI_TLS_SLOT) -# if !defined(MI_HAS_TLS_SLOT) -# error "trying to use a TLS slot for the default heap, but the mi_prim_tls_slot primitives are not defined" -# endif +// Declared this way to optimize register spills and branches +mi_decl_cold mi_decl_noinline mi_theap_t* _mi_theap_empty_get(void); -static inline mi_heap_t* mi_prim_get_default_heap(void) { - mi_heap_t* heap = (mi_heap_t*)mi_prim_tls_slot(MI_TLS_SLOT); - if mi_unlikely(heap == NULL) { - #ifdef __GNUC__ - __asm(""); // prevent conditional load of the address of _mi_heap_empty - #endif - heap = (mi_heap_t*)&_mi_heap_empty; - } - return heap; +static inline mi_theap_t* __mi_theap_empty(void) { + #if __GNUC__ + __asm(""); // prevent conditional load + return (mi_theap_t*)&_mi_theap_empty; + #else + return _mi_theap_empty_get(); + #endif } -#elif defined(MI_TLS_PTHREAD_SLOT_OFS) +#if MI_TLS_MODEL_THREAD_LOCAL +// Thread local with an initial value (default on Linux). Very efficient. -static inline mi_heap_t** mi_prim_tls_pthread_heap_slot(void) { - pthread_t self = pthread_self(); - #if defined(__DragonFly__) - if (self==NULL) return NULL; +extern mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_default; // default theap to allocate from +extern mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_cached; // theap from the last used heap + +static inline mi_theap_t* _mi_theap_default(void) { + #if defined(MI_TLS_RECURSE_GUARD) + if (mi_unlikely(!_mi_process_is_initialized)) return _mi_theap_empty_get(); #endif - return (mi_heap_t**)((uint8_t*)self + MI_TLS_PTHREAD_SLOT_OFS); + return __mi_theap_default; } -static inline mi_heap_t* mi_prim_get_default_heap(void) { - mi_heap_t** pheap = mi_prim_tls_pthread_heap_slot(); - if mi_unlikely(pheap == NULL) return _mi_heap_main_get(); - mi_heap_t* heap = *pheap; - if mi_unlikely(heap == NULL) return (mi_heap_t*)&_mi_heap_empty; - return heap; +static inline mi_theap_t* _mi_theap_cached(void) { + return __mi_theap_cached; } -#elif defined(MI_TLS_PTHREAD) +#elif MI_TLS_MODEL_FIXED_SLOT +// Fixed TLS slot (default on macOS). +#define MI_THEAP_INITASNULL 1 -extern pthread_key_t _mi_heap_default_key; -static inline mi_heap_t* mi_prim_get_default_heap(void) { - mi_heap_t* heap = (mi_unlikely(_mi_heap_default_key == (pthread_key_t)(-1)) ? _mi_heap_main_get() : (mi_heap_t*)pthread_getspecific(_mi_heap_default_key)); - return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap); +static inline mi_theap_t* _mi_theap_default(void) { + return (mi_theap_t*)mi_prim_tls_slot(MI_TLS_MODEL_FIXED_SLOT_DEFAULT); } -#else // default using a thread local variable; used on most platforms. +static inline mi_theap_t* _mi_theap_cached(void) { + return (mi_theap_t*)mi_prim_tls_slot(MI_TLS_MODEL_FIXED_SLOT_CACHED); +} -static inline mi_heap_t* mi_prim_get_default_heap(void) { - #if defined(MI_TLS_RECURSE_GUARD) - if (mi_unlikely(!_mi_process_is_initialized)) return _mi_heap_main_get(); +#elif MI_TLS_MODEL_DYNAMIC_WIN32 +// Dynamic TLS slot (default on Windows) +#define MI_THEAP_INITASNULL 1 + +// We try to use direct slots (64), but can also use the expansion slots (upto 1024 extra available) +// See for the offsets. +#if MI_SIZE_SIZE==4 +#define MI_TLS_EXPANSION_SLOT (0x0F94 / MI_SIZE_SIZE) +#else +#define MI_TLS_EXPANSION_SLOT (0x1780 / MI_SIZE_SIZE) +#endif + +extern mi_decl_hidden size_t _mi_theap_default_slot; +extern mi_decl_hidden size_t _mi_theap_cached_slot; +extern mi_decl_hidden size_t _mi_theap_default_expansion_slot; +extern mi_decl_hidden size_t _mi_theap_cached_expansion_slot; + +static inline mi_theap_t* _mi_theap_default(void) { + const size_t slot = _mi_theap_default_slot; + mi_theap_t* theap = (mi_theap_t*)mi_prim_tls_slot(slot); + #if !MI_WIN_DIRECT_TLS + if mi_unlikely(slot==MI_TLS_EXPANSION_SLOT) { // in TlsExpansionSlots ? + mi_theap_t** const eslots = (mi_theap_t**)theap; // theap is the expansion slot entry + if mi_likely(eslots!=NULL) { // is it initialized? (on this thread) + theap = eslots[_mi_theap_default_expansion_slot]; + } + } + #endif + return theap; +} + +static inline mi_theap_t* _mi_theap_cached(void) { + const size_t slot = _mi_theap_cached_slot; + mi_theap_t* theap = (mi_theap_t*)mi_prim_tls_slot(slot); + #if !MI_WIN_DIRECT_TLS + if mi_unlikely(slot==MI_TLS_EXPANSION_SLOT) { // in TlsExpansionSlots ? + mi_theap_t** const eslots = (mi_theap_t**)theap; // theap is the expansion slot entry + if mi_likely(eslots!=NULL) { // is it initialized? (on this thread) + theap = eslots[_mi_theap_cached_expansion_slot]; + } + } #endif - return _mi_heap_default; + return theap; } -#endif // mi_prim_get_default_heap() +#elif MI_TLS_MODEL_DYNAMIC_PTHREADS +// Dynamic pthread slot on less common platforms. This is not too bad. (default on OpenBSD) +#define MI_THEAP_INITASNULL 1 +extern mi_decl_hidden pthread_key_t _mi_theap_default_key; +extern mi_decl_hidden pthread_key_t _mi_theap_cached_key; +static inline mi_theap_t* _mi_theap_default(void) { + #if !MI_TLS_MODEL_DYNAMIC_PTHREADS_DEFAULT_ENTRY_IS_NULL + // we can skip this check if using the initial key will return NULL from pthread_getspecific + if mi_unlikely(_mi_theap_default_key==0) { return NULL; } + #endif + return (mi_theap_t*)pthread_getspecific(_mi_theap_default_key); +} + +static inline mi_theap_t* _mi_theap_cached(void) { + #if !MI_TLS_MODEL_DYNAMIC_PTHREADS_DEFAULT_ENTRY_IS_NULL + // we can skip this check if using the initial key will return NULL from pthread_getspecific + if mi_unlikely(_mi_theap_cached_key==0) { return NULL; } + #endif + return (mi_theap_t*)pthread_getspecific(_mi_theap_cached_key); +} + +#else +#error "no TLS model is defined for this platform?" +#endif + + +// Check if a thread is initialized (without using a thread-local if using fixed slots) +static inline bool _mi_thread_is_initialized(void) { + return (mi_theap_is_initialized(_mi_theap_default())); +} + +// Get (and possible create) the theap belonging to a heap +// We cache the last accessed theap in `_mi_theap_cached` for better performance. +static inline mi_theap_t* _mi_heap_theap(const mi_heap_t* heap) { + mi_theap_t* theap = _mi_theap_cached(); + #if MI_THEAP_INITASNULL + if mi_likely(theap!=NULL && _mi_theap_heap(theap)==heap) return theap; + #else + if mi_likely(_mi_theap_heap(theap)==heap) return theap; + #endif + return _mi_heap_theap_get_or_init(heap); +} + +// Get the theap belonging to a heap without creating it if it is not yet initialized. +static inline mi_theap_t* _mi_heap_theap_peek(const mi_heap_t* heap) { + mi_theap_t* theap = _mi_theap_cached(); + #if MI_THEAP_INITASNULL + if mi_unlikely(theap==NULL || _mi_theap_heap(theap)!=heap) + #else + if mi_unlikely(_mi_theap_heap(theap)!=heap) + #endif + { + theap = _mi_heap_theap_get_peek(heap); // don't update the cache on a query (?) + } + mi_assert(theap==NULL || _mi_theap_heap(theap)==heap); + return theap; +} + +// Find the associated theap or NULL if it does not exist (during shutdown) +// Should be fast as it is called in `free.c:mi_free_try_collect`. +static inline mi_theap_t* _mi_page_associated_theap_peek(mi_page_t* page) { + mi_heap_t* const heap = page->heap; + mi_theap_t* theap; + if mi_likely(heap==NULL) { theap = __mi_theap_main; } // note: on macOS accessing the thread_local can cause allocation during thread shutdown (and reinitialize the thread)! + else { theap = _mi_heap_theap_peek(heap); } + mi_assert_internal(theap==NULL || _mi_thread_id()==theap->tld->thread_id); + return theap; +} -#endif // MIMALLOC_PRIM_H +#endif // MI_PRIM_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/track.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/track.h index a659d940..1f402129 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/track.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/track.h @@ -5,8 +5,8 @@ terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #pragma once -#ifndef MIMALLOC_TRACK_H -#define MIMALLOC_TRACK_H +#ifndef MI_TRACK_H +#define MI_TRACK_H /* ------------------------------------------------------------------------------------------------------ Track memory ranges with macros for tools like Valgrind address sanitizer, or other memory checkers. @@ -34,7 +34,7 @@ The corresponding `mi_track_free` still uses the block start pointer and origina The `mi_track_resize` is currently unused but could be called on reallocations within a block. `mi_track_init` is called at program start. -The following macros are for tools like asan and valgrind to track whether memory is +The following macros are for tools like asan and valgrind to track whether memory is defined, undefined, or not accessible at all: #define mi_track_mem_defined(p,size) @@ -47,7 +47,7 @@ defined, undefined, or not accessible at all: // valgrind tool #define MI_TRACK_ENABLED 1 -#define MI_TRACK_HEAP_DESTROY 1 // track free of individual blocks on heap_destroy +#define MI_TRACK_HEAP_DESTROY 1 // track free of individual blocks on theap_destroy #define MI_TRACK_TOOL "valgrind" #include @@ -82,13 +82,10 @@ defined, undefined, or not accessible at all: #define MI_TRACK_HEAP_DESTROY 1 #define MI_TRACK_TOOL "ETW" -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include #include "../src/prim/windows/etw.h" -#define mi_track_init() EventRegistermicrosoft_windows_mimalloc(); +#define mi_track_init() EventRegistermicrosoft_windows_mimalloc() +#define mi_track_done() EventUnregistermicrosoft_windows_mimalloc() #define mi_track_malloc_size(p,reqsize,size,zero) EventWriteETW_MI_ALLOC((UINT64)(p), size) #define mi_track_free_size(p,size) EventWriteETW_MI_FREE((UINT64)(p), size) @@ -96,7 +93,7 @@ defined, undefined, or not accessible at all: // no tracking #define MI_TRACK_ENABLED 0 -#define MI_TRACK_HEAP_DESTROY 0 +#define MI_TRACK_HEAP_DESTROY 0 #define MI_TRACK_TOOL "none" #define mi_track_malloc_size(p,reqsize,size,zero) @@ -108,7 +105,7 @@ defined, undefined, or not accessible at all: // Utility definitions #ifndef mi_track_resize -#define mi_track_resize(p,oldsize,newsize) mi_track_free_size(p,oldsize); mi_track_malloc(p,newsize,false) +#define mi_track_resize(p,oldsize,newsize) do{ mi_track_free_size(p,oldsize); mi_track_malloc(p,newsize,false); } while(0) #endif #ifndef mi_track_align @@ -119,6 +116,10 @@ defined, undefined, or not accessible at all: #define mi_track_init() #endif +#ifndef mi_track_done +#define mi_track_done() +#endif + #ifndef mi_track_mem_defined #define mi_track_mem_defined(p,size) #endif @@ -146,4 +147,4 @@ defined, undefined, or not accessible at all: } #endif -#endif +#endif // MI_TRACK_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/types.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/types.h index 2fdde904..d67f0a1a 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/types.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/include/mimalloc/types.h @@ -1,34 +1,36 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2024, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #pragma once -#ifndef MIMALLOC_TYPES_H -#define MIMALLOC_TYPES_H +#ifndef MI_TYPES_H +#define MI_TYPES_H // -------------------------------------------------------------------------- // This file contains the main type definitions for mimalloc: -// mi_heap_t : all data for a thread-local heap, contains -// lists of all managed heap pages. -// mi_segment_t : a larger chunk of memory (32GiB) from where pages -// are allocated. A segment is divided in slices (64KiB) from -// which pages are allocated. -// mi_page_t : a "mimalloc" page (usually 64KiB or 512KiB) from -// where objects are allocated. +// mi_heap_t : all data for a heap; usually there is just one main default heap. +// mi_theap_t : a thread local heap belonging to a specific heap: +// maintains lists of thread-local heap pages that have free space. +// mi_page_t : a mimalloc page (usually 64KiB or 512KiB) from +// where objects of a single size are allocated. // Note: we write "OS page" for OS memory pages while // using plain "page" for mimalloc pages (`mi_page_t`). +// mi_arena_t : a large memory area where pages are allocated (process shared) +// mi_tld_t : thread local data +// mi_subproc_t : all heaps belong to a sub-process (usually just the main one) // -------------------------------------------------------------------------- +#include #include // ptrdiff_t #include // uintptr_t, uint16_t, etc -#include "atomic.h" // _Atomic - -#ifdef _MSC_VER -#pragma warning(disable:4214) // bitfield is not int -#endif +#include // bool +#include // SIZE_MAX etc. +#include // error codes +#include "bits.h" // size defines (MI_INTPTR_SIZE etc), bit operations +#include "atomic.h" // _Atomic primitives // Minimal alignment necessary. On most platforms 16 bytes are needed // due to SSE registers for example. This must be at least `sizeof(void*)` @@ -36,6 +38,7 @@ terms of the MIT license. A copy of the license can be found in the file #define MI_MAX_ALIGN_SIZE 16 // sizeof(max_align_t) #endif + // ------------------------------------------------------ // Variants // ------------------------------------------------------ @@ -52,28 +55,43 @@ terms of the MIT license. A copy of the license can be found in the file // #define MI_STAT 1 // Define MI_SECURE to enable security mitigations -// #define MI_SECURE 1 // guard page around metadata -// #define MI_SECURE 2 // guard page around each mimalloc page +// #define MI_SECURE 1 // guard pages around meta data, randomize arena allocation addresses (like ASLR), abort on detected meta data corruption +// #define MI_SECURE 2 // randomize relative allocation addresses (within mimalloc pages) // #define MI_SECURE 3 // encode free lists (detect corrupted free list (buffer overflow), and invalid pointer free) -// #define MI_SECURE 4 // checks for double free. (may be more expensive) +// #define MI_SECURE 4 // checks for double free (may be more expensive) (`-DMI_SECURE=ON`) +// #define MI_SECURE 5 // guard page at the end of each mimalloc page (expensive!) (`-DMI_SECURE_FULL=ON`) #if !defined(MI_SECURE) #define MI_SECURE 0 #endif -// Define MI_DEBUG for debug mode -// #define MI_DEBUG 1 // basic assertion checks and statistics, check double free, corrupted free list, and invalid pointer free. -// #define MI_DEBUG 2 // + internal assertion checks +// Define MI_DEBUG for assertion and invariant checking +// #define MI_DEBUG 1 // basic assertion checks and statistics, check double free, corrupted free list, and invalid pointer free. (cmake -DMI_DEBUG=ON) +// #define MI_DEBUG 2 // + internal assertion checks (cmake -DMI_DEBUG_INTERNAL=ON) // #define MI_DEBUG 3 // + extensive internal invariant checking (cmake -DMI_DEBUG_FULL=ON) #if !defined(MI_DEBUG) -#if !defined(NDEBUG) || defined(_DEBUG) +#if defined(MI_BUILD_RELEASE) || defined(NDEBUG) +#define MI_DEBUG 0 +#else #define MI_DEBUG 2 +#endif +#endif + +// Statistics (0=only essential, 1=normal, 2=more fine-grained (expensive) tracking) +#ifndef MI_STAT +#if (MI_DEBUG>0) +#define MI_STAT 2 #else -#define MI_DEBUG 0 +#define MI_STAT 0 +#endif #endif + +// Enable guard pages behind objects of a certain size (set by the MIMALLOC_GUARDED_MIN/MAX/SAMPLE_RATE options) +#if !defined(MI_GUARDED) && MI_DEBUG && !defined(NDEBUG) && !MI_PAGE_META_ALIGNED_FREE_SMALL +#define MI_GUARDED 1 #endif -// Reserve extra padding at the end of each block to be more resilient against heap block overflows. +// Reserve extra padding at the end of each block to be more resilient against theap block overflows. // The padding can detect buffer overflow on free. #if !defined(MI_PADDING) && (MI_SECURE>=3 || MI_DEBUG>=1 || (MI_TRACK_VALGRIND || MI_TRACK_ASAN || MI_TRACK_ETW)) #define MI_PADDING 1 @@ -91,134 +109,207 @@ terms of the MIT license. A copy of the license can be found in the file #define MI_ENCODE_FREELIST 1 #endif +// Enable large pages for objects between 64KiB and 512KiB. +// This should perhaps be disabled by default as for many workloads the block sizes above 64 KiB +// are quite random which can lead to too many partially used large pages (but see issue #1104). +#ifndef MI_ENABLE_LARGE_PAGES +#define MI_ENABLE_LARGE_PAGES 1 +#endif -// We used to abandon huge pages in order to eagerly deallocate it if freed from another thread. -// Unfortunately, that makes it not possible to visit them during a heap walk or include them in a -// `mi_heap_destroy`. We therefore instead reset/decommit the huge blocks nowadays if freed from -// another thread so the memory becomes "virtually" available (and eventually gets properly freed by -// the owning thread). -// #define MI_HUGE_PAGE_ABANDON 1 +// Place page meta info at the start of the page area or keep it separate? +// Separate keeps the page info at the arena start (default) which is more secure +// and reduces wasted space due to alignment and block sizes. +// (but also reserves more memory up front (about 2MiB per GiB)) +#if !defined(MI_PAGE_META_IS_SEPARATED) +#if MI_PAGE_MAP_FLAT +#define MI_PAGE_META_IS_SEPARATED 0 +#else +#define MI_PAGE_META_IS_SEPARATED 1 +#endif +#endif +// We can choose to only put page info of small pages at the start of the page area. +// This can be used to have a slightly faster `mi_free_small` function for specialized +// cases (like language runtime systems). +#if !defined(MI_PAGE_META_ALIGNED_FREE_SMALL) +#define MI_PAGE_META_ALIGNED_FREE_SMALL 0 +#endif -// ------------------------------------------------------ -// Platform specific values -// ------------------------------------------------------ +// Configuration checks +#if !MI_PAGE_META_IS_SEPARATED && MI_SECURE +#error "secure mode should use separated page infos" +#endif +#if MI_PAGE_META_ALIGNED_FREE_SMALL && MI_SECURE +#error "secure mode cannot use MI_PAGE_META_ALIGNED_FREE_SMALL" +#endif +#if MI_PAGE_META_IS_SEPARATED && MI_PAGE_MAP_FLAT +#error "cannot have a flat page map with separated page infos" +#endif +#if MI_DEBUG && NDEBUG +#warning "mimalloc assertions enabled in a release build" +#endif -// ------------------------------------------------------ -// Size of a pointer. -// We assume that `sizeof(void*)==sizeof(intptr_t)` -// and it holds for all platforms we know of. -// -// However, the C standard only requires that: -// p == (void*)((intptr_t)p)) -// but we also need: -// i == (intptr_t)((void*)i) -// or otherwise one might define an intptr_t type that is larger than a pointer... -// ------------------------------------------------------ -#if INTPTR_MAX > INT64_MAX -# define MI_INTPTR_SHIFT (4) // assume 128-bit (as on arm CHERI for example) -#elif INTPTR_MAX == INT64_MAX -# define MI_INTPTR_SHIFT (3) -#elif INTPTR_MAX == INT32_MAX -# define MI_INTPTR_SHIFT (2) -#else -#error platform pointers must be 32, 64, or 128 bits +// -------------------------------------------------------------- +// Sizes of internal data-structures +// (comments specify sizes on 64-bit, usually 32-bit is halved) +// -------------------------------------------------------------- + +// Main size parameter; determines max arena sizes and max arena object sizes etc. +#ifndef MI_ARENA_SLICE_SHIFT + #ifdef MI_SMALL_PAGE_SHIFT // backward compatibility + #define MI_ARENA_SLICE_SHIFT MI_SMALL_PAGE_SHIFT + #elif MI_SECURE>=5 && __APPLE__ && MI_ARCH_ARM64 + #define MI_ARENA_SLICE_SHIFT (17) // 128 KiB to not waste too much due to 16 KiB guard pages + #else + #define MI_ARENA_SLICE_SHIFT (13 + MI_SIZE_SHIFT) // 64 KiB (32 KiB on 32-bit) + #endif +#endif +#if MI_ARENA_SLICE_SHIFT < 12 +#error Arena slices should be at least 4KiB #endif -#if SIZE_MAX == UINT64_MAX -# define MI_SIZE_SHIFT (3) -typedef int64_t mi_ssize_t; -#elif SIZE_MAX == UINT32_MAX -# define MI_SIZE_SHIFT (2) -typedef int32_t mi_ssize_t; -#else -#error platform objects must be 32 or 64 bits +#ifndef MI_BCHUNK_BITS_SHIFT + #if MI_ARENA_SLICE_SHIFT <= 13 // <= 8KiB + #define MI_BCHUNK_BITS_SHIFT (7) // 128 bits + #elif MI_ARENA_SLICE_SHIFT < 16 // <= 32KiB + #define MI_BCHUNK_BITS_SHIFT (8) // 256 bits + #else + #define MI_BCHUNK_BITS_SHIFT (6 + MI_SIZE_SHIFT) // 512 bits (or 256 on 32-bit) + #endif #endif -#if (SIZE_MAX/2) > LONG_MAX -# define MI_ZU(x) x##ULL -# define MI_ZI(x) x##LL -#else -# define MI_ZU(x) x##UL -# define MI_ZI(x) x##L +#define MI_BCHUNK_BITS (1 << MI_BCHUNK_BITS_SHIFT) // sub-bitmaps in arena's are "bchunks" of 512 bits +#define MI_ARENA_SLICE_SIZE (MI_ZU(1) << MI_ARENA_SLICE_SHIFT) // arena's allocate in slices of 64 KiB +#define MI_ARENA_SLICE_ALIGN (MI_ARENA_SLICE_SIZE) + +#define MI_ARENA_MIN_OBJ_SLICES (1) +#define MI_ARENA_MAX_CHUNK_OBJ_SLICES (MI_BCHUNK_BITS) // 32 MiB (or 8 MiB on 32-bit) + +#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_MIN_OBJ_SLICES * MI_ARENA_SLICE_SIZE) +#define MI_ARENA_MAX_CHUNK_OBJ_SIZE (MI_ARENA_MAX_CHUNK_OBJ_SLICES * MI_ARENA_SLICE_SIZE) + +#if MI_ARENA_MAX_CHUNK_OBJ_SIZE < MI_SIZE_SIZE*1024 +#error maximum object size may be too small to hold local thread data #endif -#define MI_INTPTR_SIZE (1<) +#define MI_MAX_ALLOC_SIZE PTRDIFF_MAX + +// Minimal commit for a page on-demand commit (should be >= OS page size) +#define MI_PAGE_MIN_COMMIT_SIZE MI_ARENA_SLICE_SIZE // ------------------------------------------------------ -// Main internal data-structures +// Arena's are large reserved areas of memory allocated from +// the OS that are managed by mimalloc to efficiently +// allocate MI_ARENA_SLICE_SIZE slices of memory for the +// mimalloc pages. // ------------------------------------------------------ -// Main tuning parameters for segment and page sizes -// Sizes for 64-bit (usually divide by two for 32-bit) -#ifndef MI_SEGMENT_SLICE_SHIFT -#define MI_SEGMENT_SLICE_SHIFT (13 + MI_INTPTR_SHIFT) // 64KiB (32KiB on 32-bit) -#endif +// A large memory arena where pages are allocated in. +typedef struct mi_arena_s mi_arena_t; // defined below -#ifndef MI_SEGMENT_SHIFT -#if MI_INTPTR_SIZE > 4 -#define MI_SEGMENT_SHIFT ( 9 + MI_SEGMENT_SLICE_SHIFT) // 32MiB -#else -#define MI_SEGMENT_SHIFT ( 7 + MI_SEGMENT_SLICE_SHIFT) // 4MiB on 32-bit -#endif -#endif -#ifndef MI_SMALL_PAGE_SHIFT -#define MI_SMALL_PAGE_SHIFT (MI_SEGMENT_SLICE_SHIFT) // 64KiB -#endif -#ifndef MI_MEDIUM_PAGE_SHIFT -#define MI_MEDIUM_PAGE_SHIFT ( 3 + MI_SMALL_PAGE_SHIFT) // 512KiB -#endif +// ------------------------------------------------------ +// Heaps contain allocated blocks. Heaps are self-contained +// but share the (sub-process) memory in the arena's. +// ------------------------------------------------------ -// Derived constants -#define MI_SEGMENT_SIZE (MI_ZU(1)<= 655360) -#error "mimalloc internal: define more bins" -#endif +// --------------------------------------------------------------- +// a memory id tracks the provenance of arena/OS allocated memory +// --------------------------------------------------------------- -// Maximum block size for which blocks are guaranteed to be block size aligned. (see `segment.c:_mi_segment_page_start`) -#define MI_MAX_ALIGN_GUARANTEE (MI_MEDIUM_OBJ_SIZE_MAX) +// Memory can reside in arena's, direct OS allocated, meta-data pages, or statically allocated. +// The memid keeps track of this. +typedef enum mi_memkind_e { + MI_MEM_NONE, // not allocated + MI_MEM_EXTERNAL, // not owned by mimalloc but provided externally (via `mi_manage_os_memory` for example) + MI_MEM_STATIC, // allocated in a static area and should not be freed (the initial main theap data for example (`init.c`)) + MI_MEM_META, // allocated with the meta data allocator (`arena-meta.c`) + MI_MEM_OS, // allocated from the OS + MI_MEM_OS_HUGE, // allocated as huge OS pages (usually 1GiB, pinned to physical memory) + MI_MEM_OS_REMAP, // allocated in a remapable area (i.e. using `mremap`) + MI_MEM_ARENA, // allocated from an arena (the usual case) (`arena.c`) + MI_MEM_HEAP_MAIN // allocated in the main heap (for theaps) +} mi_memkind_t; -// Alignments over MI_BLOCK_ALIGNMENT_MAX are allocated in dedicated huge page segments -#define MI_BLOCK_ALIGNMENT_MAX (MI_SEGMENT_SIZE >> 1) +static inline bool mi_memkind_is_os(mi_memkind_t memkind) { + return (memkind >= MI_MEM_OS && memkind <= MI_MEM_OS_REMAP); +} -// Maximum slice count (255) for which we can find the page for interior pointers -#define MI_MAX_SLICE_OFFSET_COUNT ((MI_BLOCK_ALIGNMENT_MAX / MI_SEGMENT_SLICE_SIZE) - 1) +static inline bool mi_memkind_needs_no_free(mi_memkind_t memkind) { + return (memkind <= MI_MEM_STATIC); +} -// we never allocate more than PTRDIFF_MAX (see also ) -// on 64-bit+ systems we also limit the maximum allocation size such that the slice count fits in 32-bits. (issue #877) -#if (PTRDIFF_MAX > INT32_MAX) && (PTRDIFF_MAX >= (MI_SEGMENT_SLIZE_SIZE * UINT32_MAX)) -#define MI_MAX_ALLOC_SIZE (MI_SEGMENT_SLICE_SIZE * (UINT32_MAX-1)) -#else -#define MI_MAX_ALLOC_SIZE PTRDIFF_MAX -#endif + +typedef struct mi_memid_os_info { + void* base; // actual base address of the block (used for offset aligned allocations) + size_t size; // allocated full size + // size_t alignment; // alignment at allocation +} mi_memid_os_info_t; + +typedef struct mi_memid_arena_info { + mi_arena_t* arena; // arena that contains this memory + uint32_t slice_index; // slice index in the arena + uint32_t slice_count; // allocated slices +} mi_memid_arena_info_t; + +typedef struct mi_memid_meta_info { + void* meta_page; // meta-page that contains the block + uint32_t block_index; // block index in the meta-data page + uint32_t block_count; // allocated blocks +} mi_memid_meta_info_t; + +typedef struct mi_memid_s { + union { + mi_memid_os_info_t os; // only used for MI_MEM_OS + mi_memid_arena_info_t arena; // only used for MI_MEM_ARENA + mi_memid_meta_info_t meta; // only used for MI_MEM_META + } mem; + mi_memkind_t memkind; + bool is_pinned; // `true` if we cannot decommit/reset/protect in this memory (e.g. when allocated using large (2Mib) or huge (1GiB) OS pages) + bool initially_committed;// `true` if the memory was originally allocated as committed + bool initially_zero; // `true` if the memory was originally zero initialized +} mi_memid_t; + + +static inline bool mi_memid_is_os(mi_memid_t memid) { + return mi_memkind_is_os(memid.memkind); +} + +static inline bool mi_memid_needs_no_free(mi_memid_t memid) { + return mi_memkind_needs_no_free(memid.memkind); +} + +static inline mi_arena_t* mi_memid_arena(mi_memid_t memid) { + return (memid.memkind == MI_MEM_ARENA ? memid.mem.arena.arena : NULL); +} // ------------------------------------------------------ @@ -238,38 +329,26 @@ typedef struct mi_block_s { } mi_block_t; -// The delayed flags are used for efficient multi-threaded free-ing -typedef enum mi_delayed_e { - MI_USE_DELAYED_FREE = 0, // push on the owning heap thread delayed list - MI_DELAYED_FREEING = 1, // temporary: another thread is accessing the owning heap - MI_NO_DELAYED_FREE = 2, // optimize: push on page local thread free queue if another block is already in the heap thread delayed free list - MI_NEVER_DELAYED_FREE = 3 // sticky: used for abondoned pages without a owning heap; this only resets on page reclaim -} mi_delayed_t; - - -// The `in_full` and `has_aligned` page flags are put in a union to efficiently -// test if both are false (`full_aligned == 0`) in the `mi_free` routine. -#if !MI_TSAN -typedef union mi_page_flags_s { - uint8_t full_aligned; - struct { - uint8_t in_full : 1; - uint8_t has_aligned : 1; - } x; -} mi_page_flags_t; -#else -// under thread sanitizer, use a byte for each flag to suppress warning, issue #130 -typedef union mi_page_flags_s { - uint16_t full_aligned; - struct { - uint8_t in_full; - uint8_t has_aligned; - } x; -} mi_page_flags_t; -#endif +// The page flags are put in the bottom 2 bits of the thread_id (for a fast test in `mi_free`) +// If `has_interior_pointers` is true if the page has pointers at an offset in a block (so we have to unalign to the block start before free-ing) +// `in_full_queue` is true if the page is full and resides in the full queue (so we move it to a regular queue on free-ing) +#define MI_PAGE_IN_FULL_QUEUE MI_ZU(0x01) +#define MI_PAGE_HAS_INTERIOR_POINTERS MI_ZU(0x02) +#define MI_PAGE_FLAG_MASK MI_ZU(0x03) +typedef size_t mi_page_flags_t; + +// There are two special threadid's: 0 for pages that are abandoned (and not in a theap queue), +// and 4 for abandoned & mapped threads -- abandoned-mapped pages are abandoned but also mapped +// in an arena (in `mi_heap_t.arena_pages.pages_abandoned`) so these can be quickly found for reuse. +// Abandoning partially used pages allows for sharing of this memory between threads (in particular if threads are blocked) +#define MI_THREADID_ABANDONED MI_ZU(0) +#define MI_THREADID_ABANDONED_MAPPED (MI_PAGE_FLAG_MASK + 1) // Thread free list. -// We use the bottom 2 bits of the pointer for mi_delayed_t flags +// Points to a list of blocks that are freed by other threads. +// The least-bit is set if the page is owned by the current thread. (`mi_page_is_owned`). +// Ownership is required before we can read any non-atomic fields in the page. +// This way we can push a block on the thread free list and try to claim ownership atomically in `free.c:mi_free_block_mt`. typedef uintptr_t mi_thread_free_t; // A page contains blocks of one specific size (`block_size`). @@ -280,226 +359,132 @@ typedef uintptr_t mi_thread_free_t; // The `local_free` and `thread_free` lists are migrated to the `free` list // when it is exhausted. The separate `local_free` list is necessary to // implement a monotonic heartbeat. The `thread_free` list is needed for -// avoiding atomic operations in the common case. +// avoiding atomic operations when allocating from the owning thread. // // `used - |thread_free|` == actual blocks that are in use (alive) // `used - |thread_free| + |free| + |local_free| == capacity` // -// We don't count `freed` (as |free|) but use `used` to reduce +// We don't count "freed" (as |free|) but use only the `used` field to reduce // the number of memory accesses in the `mi_page_all_free` function(s). +// Use `_mi_page_free_collect` to collect the thread_free list and update the `used` count. // // Notes: -// - Access is optimized for `free.c:mi_free` and `alloc.c:mi_page_alloc` +// - Non-atomic fields can only be accessed if having _ownership_ (low bit of `xthread_free` is 1). +// Combining the `thread_free` list with an ownership bit allows a concurrent `free` to atomically +// free an object and (re)claim ownership if the page was abandoned. +// - If a page is not part of a theap it is called "abandoned" (`theap==NULL`) -- in +// that case the `xthreadid` is 0 or 4 (4 is for abandoned pages that +// are in the `pages_abandoned` lists of an arena, these are called "mapped" abandoned pages). +// - page flags are in the bottom 3 bits of `xthread_id` for the fast path in `mi_free`. +// - The layout is optimized for `free.c:mi_free` and `alloc.c:mi_page_alloc` // - Using `uint16_t` does not seem to slow things down -// - The size is 12 words on 64-bit which helps the page index calculations -// (and 14 words on 32-bit, and encoded free lists add 2 words) -// - `xthread_free` uses the bottom bits as a delayed-free flags to optimize -// concurrent frees where only the first concurrent free adds to the owning -// heap `thread_delayed_free` list (see `free.c:mi_free_block_mt`). -// The invariant is that no-delayed-free is only set if there is -// at least one block that will be added, or as already been added, to -// the owning heap `thread_delayed_free` list. This guarantees that pages -// will be freed correctly even if only other threads free blocks. -typedef struct mi_page_s { - // "owned" by the segment - uint32_t slice_count; // slices in this page (0 if not a page) - uint32_t slice_offset; // distance from the actual page data slice (0 if a page) - uint8_t is_committed:1; // `true` if the page virtual memory is committed - uint8_t is_zero_init:1; // `true` if the page was initially zero initialized - uint8_t is_huge:1; // `true` if the page is in a huge segment (`segment->kind == MI_SEGMENT_HUGE`) - // padding - // layout like this to optimize access in `mi_malloc` and `mi_free` - uint16_t capacity; // number of blocks committed, must be the first field, see `segment.c:page_clear` - uint16_t reserved; // number of blocks reserved in memory - mi_page_flags_t flags; // `in_full` and `has_aligned` flags (8 bits) - uint8_t free_is_zero:1; // `true` if the blocks in the free list are zero initialized - uint8_t retire_expire:7; // expiration count for retired blocks - - mi_block_t* free; // list of available free blocks (`malloc` allocates from this list) - mi_block_t* local_free; // list of deferred free blocks by this thread (migrates to `free`) - uint16_t used; // number of blocks in use (including blocks in `thread_free`) - uint8_t block_size_shift; // if not zero, then `(1 << block_size_shift) == block_size` (only used for fast path in `free.c:_mi_page_ptr_unalign`) - uint8_t heap_tag; // tag of the owning heap, used for separated heaps by object type - // padding - size_t block_size; // size available in each block (always `>0`) - uint8_t* page_start; // start of the page area containing the blocks - #if (MI_ENCODE_FREELIST || MI_PADDING) - uintptr_t keys[2]; // two random keys to encode the free lists (see `_mi_block_next`) or padding canary - #endif +typedef struct mi_page_s { + _Atomic(mi_threadid_t) xthread_id; // thread this page belongs to. (= `theap->thread_id (or 0 or 4 if abandoned) | page_flags`) - _Atomic(mi_thread_free_t) xthread_free; // list of deferred free blocks freed by other threads - _Atomic(uintptr_t) xheap; + mi_block_t* free; // list of available free blocks (`malloc` allocates from this list) + uint16_t used; // number of blocks in use (including blocks in `thread_free`) + uint16_t capacity; // number of blocks committed + uint16_t reserved; // number of blocks reserved in memory + uint8_t retire_expire; // expiration count for retired blocks + bool free_is_zero; // `true` if the blocks in the free list are zero initialized - struct mi_page_s* next; // next page owned by this thread with the same `block_size` - struct mi_page_s* prev; // previous page owned by this thread with the same `block_size` - - // 64-bit 11 words, 32-bit 13 words, (+2 for secure) - void* padding[1]; -} mi_page_t; + mi_block_t* local_free; // list of deferred free blocks by this thread (migrates to `free`) + _Atomic(mi_thread_free_t) xthread_free; // list of deferred free blocks freed by other threads (= `mi_block_t* | (1 if owned)`) + size_t block_size; // const: size available in each block (always `>0`) + uint32_t page_woffset; // const: offset relative to the page (in machine words) to the start of the blocks + uint32_t slice_committed; // committed size relative to the first arena slice of the page data (or 0 if the page is fully committed already) + #if (MI_ENCODE_FREELIST || MI_PADDING) + uintptr_t keys[2]; // const: two random keys to encode the free lists (see `_mi_block_next`) or padding canary + #endif -// ------------------------------------------------------ -// Mimalloc segments contain mimalloc pages -// ------------------------------------------------------ + mi_theap_t* theap; // the theap owning this page (may not be valid or NULL for abandoned pages) + mi_heap_t* heap; // const: the heap owning this page -typedef enum mi_page_kind_e { - MI_PAGE_SMALL, // small blocks go into 64KiB pages inside a segment - MI_PAGE_MEDIUM, // medium blocks go into 512KiB pages inside a segment - MI_PAGE_LARGE, // larger blocks go into a single page spanning a whole segment - MI_PAGE_HUGE // a huge page is a single page in a segment of variable size - // used for blocks `> MI_LARGE_OBJ_SIZE_MAX` or an aligment `> MI_BLOCK_ALIGNMENT_MAX`. -} mi_page_kind_t; + struct mi_page_s* next; // next page owned by the theap with the same `block_size` + struct mi_page_s* prev; // previous page owned by the theap with the same `block_size` + mi_memid_t memid; // const: provenance of the page memory +} mi_page_t; -typedef enum mi_segment_kind_e { - MI_SEGMENT_NORMAL, // MI_SEGMENT_SIZE size with pages inside. - MI_SEGMENT_HUGE, // segment with just one huge page inside. -} mi_segment_kind_t; // ------------------------------------------------------ -// A segment holds a commit mask where a bit is set if -// the corresponding MI_COMMIT_SIZE area is committed. -// The MI_COMMIT_SIZE must be a multiple of the slice -// size. If it is equal we have the most fine grained -// decommit (but setting it higher can be more efficient). -// The MI_MINIMAL_COMMIT_SIZE is the minimal amount that will -// be committed in one go which can be set higher than -// MI_COMMIT_SIZE for efficiency (while the decommit mask -// is still tracked in fine-grained MI_COMMIT_SIZE chunks) +// Object sizes // ------------------------------------------------------ -#define MI_MINIMAL_COMMIT_SIZE (1*MI_SEGMENT_SLICE_SIZE) -#define MI_COMMIT_SIZE (MI_SEGMENT_SLICE_SIZE) // 64KiB -#define MI_COMMIT_MASK_BITS (MI_SEGMENT_SIZE / MI_COMMIT_SIZE) -#define MI_COMMIT_MASK_FIELD_BITS MI_SIZE_BITS -#define MI_COMMIT_MASK_FIELD_COUNT (MI_COMMIT_MASK_BITS / MI_COMMIT_MASK_FIELD_BITS) - -#if (MI_COMMIT_MASK_BITS != (MI_COMMIT_MASK_FIELD_COUNT * MI_COMMIT_MASK_FIELD_BITS)) -#error "the segment size must be exactly divisible by the (commit size * size_t bits)" +#define MI_PAGE_ALIGN MI_ARENA_SLICE_ALIGN // pages must be aligned on this for the page map. +#define MI_PAGE_MIN_START_BLOCK_ALIGN MI_MAX_ALIGN_SIZE // minimal block alignment for the first block in a page (16b) +#define MI_PAGE_MAX_START_BLOCK_ALIGN2 (4*MI_KiB) // maximal block alignment for "power of 2"-sized blocks (such that we guarantee natural alignment) +#define MI_PAGE_OSPAGE_BLOCK_ALIGN2 (4*MI_KiB) // also aligns any multiple of this size to avoid TLB misses. +#define MI_PAGE_MAX_OVERALLOC_ALIGN MI_ARENA_SLICE_SIZE // (64 KiB) limit for which we overallocate in arena pages, beyond this use OS allocation + +// The max object sizes are intended to not waste more than ~ 12.5% internally over the page sizes. +#define MI_SMALL_MAX_OBJ_SIZE ((MI_SMALL_PAGE_SIZE-MI_PAGE_OSPAGE_BLOCK_ALIGN2)/6) // = 10 KiB +#if MI_ENABLE_LARGE_PAGES +#define MI_MEDIUM_MAX_OBJ_SIZE ((MI_MEDIUM_PAGE_SIZE-MI_PAGE_OSPAGE_BLOCK_ALIGN2)/6) // ~ 84 KiB +#define MI_LARGE_MAX_OBJ_SIZE (MI_LARGE_PAGE_SIZE/8) // <= 512 KiB. note: this must be a nice power of 2 or we get rounding issues with `_mi_bin` +#else +#define MI_MEDIUM_MAX_OBJ_SIZE (MI_MEDIUM_PAGE_SIZE/8) // <= 64 KiB +#define MI_LARGE_MAX_OBJ_SIZE MI_MEDIUM_MAX_OBJ_SIZE // note: this must be a nice power of 2 or we get rounding issues with `_mi_bin` #endif +#define MI_LARGE_MAX_OBJ_WSIZE (MI_LARGE_MAX_OBJ_SIZE/MI_SIZE_SIZE) -typedef struct mi_commit_mask_s { - size_t mask[MI_COMMIT_MASK_FIELD_COUNT]; -} mi_commit_mask_t; - -typedef mi_page_t mi_slice_t; -typedef int64_t mi_msecs_t; - - -// --------------------------------------------------------------- -// a memory id tracks the provenance of arena/OS allocated memory -// --------------------------------------------------------------- - -// Memory can reside in arena's, direct OS allocated, or statically allocated. The memid keeps track of this. -typedef enum mi_memkind_e { - MI_MEM_NONE, // not allocated - MI_MEM_EXTERNAL, // not owned by mimalloc but provided externally (via `mi_manage_os_memory` for example) - MI_MEM_STATIC, // allocated in a static area and should not be freed (for arena meta data for example) - MI_MEM_OS, // allocated from the OS - MI_MEM_OS_HUGE, // allocated as huge OS pages (usually 1GiB, pinned to physical memory) - MI_MEM_OS_REMAP, // allocated in a remapable area (i.e. using `mremap`) - MI_MEM_ARENA // allocated from an arena (the usual case) -} mi_memkind_t; - -static inline bool mi_memkind_is_os(mi_memkind_t memkind) { - return (memkind >= MI_MEM_OS && memkind <= MI_MEM_OS_REMAP); -} - -typedef struct mi_memid_os_info { - void* base; // actual base address of the block (used for offset aligned allocations) - size_t alignment; // alignment at allocation -} mi_memid_os_info_t; +#if (MI_LARGE_MAX_OBJ_WSIZE >= 655360) +#error "mimalloc internal: define more bins" +#endif -typedef struct mi_memid_arena_info { - size_t block_index; // index in the arena - mi_arena_id_t id; // arena id (>= 1) - bool is_exclusive; // this arena can only be used for specific arena allocations -} mi_memid_arena_info_t; +// static invariant: MI_MAX_SINGLETON_BIN >= _mi_bin(MI_LARGE_MAX_OBJ_SIZE) (See init.c for the size bins) +#if (MI_LARGE_MAX_OBJ_WSIZE <= 8192) // 64 KiB +#define MI_MAX_SINGLETON_BIN (48) +#elif (MI_LARGE_MAX_OBJ_WSIZE <= 32768) // 256KiB +#define MI_MAX_SINGLETON_BIN (56) +#elif (MI_LARGE_MAX_OBJ_WSIZE <= 65536) // 512KiB +#define MI_MAX_SINGLETON_BIN (60) +#else +#define MI_MAX_SINGLETON_BIN MI_BIN_HUGE +#endif -typedef struct mi_memid_s { - union { - mi_memid_os_info_t os; // only used for MI_MEM_OS - mi_memid_arena_info_t arena; // only used for MI_MEM_ARENA - } mem; - bool is_pinned; // `true` if we cannot decommit/reset/protect in this memory (e.g. when allocated using large (2Mib) or huge (1GiB) OS pages) - bool initially_committed;// `true` if the memory was originally allocated as committed - bool initially_zero; // `true` if the memory was originally zero initialized - mi_memkind_t memkind; -} mi_memid_t; +// ------------------------------------------------------ +// Page kinds +// ------------------------------------------------------ +typedef enum mi_page_kind_e { + MI_PAGE_SMALL, // small blocks go into 64KiB pages + MI_PAGE_MEDIUM, // medium blocks go into 512KiB pages + MI_PAGE_LARGE, // larger blocks go into 4MiB pages (if `MI_ENABLE_LARGE_PAGES==1`) + MI_PAGE_SINGLETON // page containing a single block. + // used for blocks `> MI_LARGE_MAX_OBJ_SIZE` or an alignment `> MI_PAGE_MAX_OVERALLOC_ALIGN`. +} mi_page_kind_t; -// ----------------------------------------------------------------------------------------- -// Segments are large allocated memory blocks (8mb on 64 bit) from arenas or the OS. -// -// Inside segments we allocated fixed size mimalloc pages (`mi_page_t`) that contain blocks. -// The start of a segment is this structure with a fixed number of slice entries (`slices`) -// usually followed by a guard OS page and the actual allocation area with pages. -// While a page is not allocated, we view it's data as a `mi_slice_t` (instead of a `mi_page_t`). -// Of any free area, the first slice has the info and `slice_offset == 0`; for any subsequent -// slices part of the area, the `slice_offset` is the byte offset back to the first slice -// (so we can quickly find the page info on a free, `internal.h:_mi_segment_page_of`). -// For slices, the `block_size` field is repurposed to signify if a slice is used (`1`) or not (`0`). -// Small and medium pages use a fixed amount of slices to reduce slice fragmentation, while -// large and huge pages span a variable amount of slices. -typedef struct mi_segment_s { - // constant fields - mi_memid_t memid; // memory id for arena/OS allocation - bool allow_decommit; // can we decommmit the memory - bool allow_purge; // can we purge the memory (reset or decommit) - size_t segment_size; - - // segment fields - mi_msecs_t purge_expire; // purge slices in the `purge_mask` after this time - mi_commit_mask_t purge_mask; // slices that can be purged - mi_commit_mask_t commit_mask; // slices that are currently committed - - // from here is zero initialized - struct mi_segment_s* next; // the list of freed segments in the cache (must be first field, see `segment.c:mi_segment_init`) - bool was_reclaimed; // true if it was reclaimed (used to limit on-free reclamation) - - size_t abandoned; // abandoned pages (i.e. the original owning thread stopped) (`abandoned <= used`) - size_t abandoned_visits; // count how often this segment is visited during abondoned reclamation (to force reclaim if it takes too long) - size_t used; // count of pages in use - uintptr_t cookie; // verify addresses in debug mode: `mi_ptr_cookie(segment) == segment->cookie` - - size_t segment_slices; // for huge segments this may be different from `MI_SLICES_PER_SEGMENT` - size_t segment_info_slices; // initial count of slices that we are using for segment info and possible guard pages. - - // layout like this to optimize access in `mi_free` - mi_segment_kind_t kind; - size_t slice_entries; // entries in the `slices` array, at most `MI_SLICES_PER_SEGMENT` - _Atomic(mi_threadid_t) thread_id; // unique id of the thread owning this segment - - mi_slice_t slices[MI_SLICES_PER_SEGMENT+1]; // one extra final entry for huge blocks with large alignment -} mi_segment_t; // ------------------------------------------------------ -// Heaps -// Provide first-class heaps to allocate from. -// A heap just owns a set of pages for allocation and +// A "theap" is a thread local heap which owns pages. +// (making them thread-local avoids atomic operations) +// +// All theaps belong to a (non-thread-local) heap. +// A theap just owns a set of pages for allocation and // can only be allocate/reallocate from the thread that created it. // Freeing blocks can be done from any thread though. -// Per thread, the segments are shared among its heaps. -// Per thread, there is always a default heap that is -// used for allocation; it is initialized to statically -// point to an empty heap to avoid initialization checks -// in the fast path. +// +// Per thread, there is always a default theap that belongs +// to the default heap. It is initialized to statically +// point initially to an empty theap to avoid initialization +// checks in the fast path. // ------------------------------------------------------ // Thread local data -typedef struct mi_tld_s mi_tld_t; +typedef struct mi_tld_s mi_tld_t; // defined below // Pages of a certain block size are held in a queue. typedef struct mi_page_queue_s { mi_page_t* first; mi_page_t* last; + size_t count; size_t block_size; } mi_page_queue_t; -#define MI_BIN_FULL (MI_BIN_HUGE+1) - // Random context typedef struct mi_random_cxt_s { uint32_t input[16]; @@ -510,7 +495,7 @@ typedef struct mi_random_cxt_s { // In debug mode there is a padding structure at the end of the blocks to check for buffer overflows -#if (MI_PADDING) +#if MI_PADDING typedef struct mi_padding_s { uint32_t canary; // encoded block value to check validity of the padding (in case of overflow) uint32_t delta; // padding bytes before the block. (mi_usable_size(p) - delta == exact allocated bytes) @@ -525,181 +510,238 @@ typedef struct mi_padding_s { #define MI_PAGES_DIRECT (MI_SMALL_WSIZE_MAX + MI_PADDING_WSIZE + 1) -// A heap owns a set of pages. -struct mi_heap_s { - mi_tld_t* tld; - _Atomic(mi_block_t*) thread_delayed_free; - mi_threadid_t thread_id; // thread this heap belongs too - mi_arena_id_t arena_id; // arena id if the heap belongs to a specific arena (or 0) +// A thread-local heap ("theap") owns a set of thread-local pages. +struct mi_theap_s { + mi_tld_t* tld; // thread-local data + _Atomic(mi_heap_t*) heap; // the heap this theap belongs to. + _Atomic(size_t) refcount; // reference count + _Atomic(size_t) freed; // ensure atomic free-ing + unsigned long long heartbeat; // monotonic heartbeat count uintptr_t cookie; // random cookie to verify pointers (see `_mi_ptr_cookie`) - uintptr_t keys[2]; // two random keys used to encode the `thread_delayed_free` list mi_random_ctx_t random; // random number context used for secure allocation size_t page_count; // total number of pages in the `pages` queues. size_t page_retired_min; // smallest retired index (retired pages are fully free, but still in the page queues) size_t page_retired_max; // largest retired index into the `pages` array. - mi_heap_t* next; // list of heaps per thread - bool no_reclaim; // `true` if this heap should not reclaim abandoned pages - uint8_t tag; // custom tag, can be used for separating heaps based on the object types + size_t pages_full_size; // optimization: total size of blocks in the pages of the full queue (issue #1220) + long generic_count; // how often is `_mi_malloc_generic` called? + long generic_collect_count; // how often is `_mi_malloc_generic` called without collecting? + + mi_theap_t* tnext; // list of theaps in this thread + mi_theap_t* tprev; + mi_theap_t* hnext; // list of theaps of the owning `heap` + mi_theap_t* hprev; + + long page_full_retain; // how many full pages can be retained per queue (before abandoning them) + bool allow_page_reclaim; // `true` if this theap can reclaim abandoned pages + bool allow_page_abandon; // `true` if this theap can abandon pages to reduce memory footprint + #if MI_GUARDED + size_t guarded_size_min; // minimal size for guarded objects + size_t guarded_size_max; // maximal size for guarded objects + size_t guarded_sample_rate; // sample rate (set to 0 to disable guarded pages) + size_t guarded_sample_count; // current sample count (counting down to 0) + #endif mi_page_t* pages_free_direct[MI_PAGES_DIRECT]; // optimize: array where every entry points a page with possibly free blocks in the corresponding queue for that size. - mi_page_queue_t pages[MI_BIN_FULL + 1]; // queue of pages for each size class (or "bin") + mi_page_queue_t pages[MI_BIN_COUNT]; // queue of pages for each size class (or "bin") + mi_memid_t memid; // provenance of the theap struct itself (meta or os) + mi_stats_t stats; // thread-local statistics }; + // ------------------------------------------------------ -// Debug +// Heaps contain allocated blocks. Heaps are self-contained +// but share the (sub-process) memory in the arena's. // ------------------------------------------------------ -#if !defined(MI_DEBUG_UNINIT) -#define MI_DEBUG_UNINIT (0xD0) -#endif -#if !defined(MI_DEBUG_FREED) -#define MI_DEBUG_FREED (0xDF) -#endif -#if !defined(MI_DEBUG_PADDING) -#define MI_DEBUG_PADDING (0xDE) -#endif +// Keep track of all owned and abandoned pages in the arena's +struct mi_arena_pages_s; +typedef struct mi_arena_pages_s mi_arena_pages_t; -#if (MI_DEBUG) -// use our own assertion to print without memory allocation -void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line, const char* func ); -#define mi_assert(expr) ((expr) ? (void)0 : _mi_assert_fail(#expr,__FILE__,__LINE__,__func__)) -#else -#define mi_assert(x) -#endif +#define MI_MAX_ARENAS (160) // Limited for now (and takes up .bss).. but arena's scale up exponentially (see `mi_arena_reserve`) + // 160 arenas is enough for ~2 TiB memory -#if (MI_DEBUG>1) -#define mi_assert_internal mi_assert -#else -#define mi_assert_internal(x) -#endif +// A dynamic thread-local variable; 0 for an invalid thread-local +typedef size_t mi_thread_local_t; -#if (MI_DEBUG>2) -#define mi_assert_expensive mi_assert -#else -#define mi_assert_expensive(x) -#endif +typedef struct mi_heap_s { + mi_subproc_t* subproc; // a heap belongs to a subprocess + size_t heap_seq; // unique sequence number for heaps in this subprocess + mi_heap_t* next; // list of heaps in this subprocess + mi_heap_t* prev; + mi_thread_local_t theap; // dynamic thread local for the thread-local theaps of this heap -// ------------------------------------------------------ -// Statistics -// ------------------------------------------------------ + mi_arena_t* exclusive_arena; // if the heap should only allocate from a specific arena (or NULL) + int numa_node; // if >=0, prefer this numa node for allocations -#ifndef MI_STAT -#if (MI_DEBUG>0) -#define MI_STAT 2 -#else -#define MI_STAT 0 -#endif -#endif + mi_theap_t* theaps; // list of all thread-local theaps belonging to this heap (using the `hnext`/`hprev` fields) + mi_lock_t theaps_lock; // lock for the theaps list operations -typedef struct mi_stat_count_s { - int64_t allocated; - int64_t freed; - int64_t peak; - int64_t current; -} mi_stat_count_t; - -typedef struct mi_stat_counter_s { - int64_t total; - int64_t count; -} mi_stat_counter_t; - -typedef struct mi_stats_s { - mi_stat_count_t segments; - mi_stat_count_t pages; - mi_stat_count_t reserved; - mi_stat_count_t committed; - mi_stat_count_t reset; - mi_stat_count_t purged; - mi_stat_count_t page_committed; - mi_stat_count_t segments_abandoned; - mi_stat_count_t pages_abandoned; - mi_stat_count_t threads; - mi_stat_count_t normal; - mi_stat_count_t huge; - mi_stat_count_t large; - mi_stat_count_t malloc; - mi_stat_count_t segments_cache; - mi_stat_counter_t pages_extended; - mi_stat_counter_t mmap_calls; - mi_stat_counter_t commit_calls; - mi_stat_counter_t reset_calls; - mi_stat_counter_t purge_calls; - mi_stat_counter_t page_no_retire; - mi_stat_counter_t searches; - mi_stat_counter_t normal_count; - mi_stat_counter_t huge_count; - mi_stat_counter_t large_count; - mi_stat_counter_t arena_count; - mi_stat_counter_t arena_crossover_count; - mi_stat_counter_t arena_rollback_count; -#if MI_STAT>1 - mi_stat_count_t normal_bins[MI_BIN_HUGE+1]; -#endif -} mi_stats_t; - - -void _mi_stat_increase(mi_stat_count_t* stat, size_t amount); -void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount); -void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount); - -#if (MI_STAT) -#define mi_stat_increase(stat,amount) _mi_stat_increase( &(stat), amount) -#define mi_stat_decrease(stat,amount) _mi_stat_decrease( &(stat), amount) -#define mi_stat_counter_increase(stat,amount) _mi_stat_counter_increase( &(stat), amount) -#else -#define mi_stat_increase(stat,amount) (void)0 -#define mi_stat_decrease(stat,amount) (void)0 -#define mi_stat_counter_increase(stat,amount) (void)0 -#endif + _Atomic(size_t) abandoned_count[MI_BIN_COUNT]; // total count of abandoned pages in this heap + mi_page_t* os_abandoned_pages; // list of pages that are OS allocated and not in an arena + mi_lock_t os_abandoned_pages_lock; // lock for the os abandoned pages list (this lock protects list operations) + + _Atomic(mi_arena_pages_t*) arena_pages[MI_MAX_ARENAS]; // track owned and abandoned pages in the arenas (entries can be NULL) + mi_lock_t arena_pages_lock; // lock to update the arena_pages array + + mi_stats_t stats; // statistics for this heap; periodically updated by merging from each theap +} mi_heap_t; + + +// ------------------------------------------------------ +// Sub processes do not reclaim or visit pages from other sub processes. +// These are essentially the static variables of a process, and +// usually there is only one subprocess. This can be used for example +// by CPython to have separate interpreters within one process. +// Each thread can only belong to one subprocess +// (and needs to call `mi_subproc_add_current_thread` before any allocations). +// ------------------------------------------------------ -#define mi_heap_stat_counter_increase(heap,stat,amount) mi_stat_counter_increase( (heap)->tld->stats.stat, amount) -#define mi_heap_stat_increase(heap,stat,amount) mi_stat_increase( (heap)->tld->stats.stat, amount) -#define mi_heap_stat_decrease(heap,stat,amount) mi_stat_decrease( (heap)->tld->stats.stat, amount) +struct mi_subproc_s { + size_t subproc_seq; // unique id for sub-processes + mi_subproc_t* next; // list of all sub-processes + mi_subproc_t* prev; + + _Atomic(size_t) arena_count; // current count of arena's + _Atomic(mi_arena_t*) arenas[MI_MAX_ARENAS]; // arena's of this sub-process + mi_lock_t arena_reserve_lock; // lock to ensure arena's get reserved one at a time + mi_decl_align(8) // needed on some 32-bit platforms + _Atomic(int64_t) purge_expire; // expiration is set if any arenas can be purged + + _Atomic(mi_heap_t*) heap_main; // main heap for this sub process + mi_heap_t* heaps; // heaps belonging to this sub-process + mi_lock_t heaps_lock; + + _Atomic(size_t) thread_count; // current threads associated with this sub-process + _Atomic(size_t) thread_total_count; // total created threads associated with this sub-process + _Atomic(size_t) heap_count; // current heaps in this sub-process (== |heaps|) + _Atomic(size_t) heap_total_count; // total created heaps in this sub-process + + mi_memid_t memid; // provenance of this memory block (meta or static) + mi_decl_align(8) // needed on some 32-bit platforms + mi_stats_t stats; // subprocess statistics; updated for arena/OS stats like committed, + // and otherwise merged with heap stats when those are deleted +}; // ------------------------------------------------------ // Thread Local data // ------------------------------------------------------ -// A "span" is is an available range of slices. The span queues keep -// track of slice spans of at most the given `slice_count` (but more than the previous size class). -typedef struct mi_span_queue_s { - mi_slice_t* first; - mi_slice_t* last; - size_t slice_count; -} mi_span_queue_t; - -#define MI_SEGMENT_BIN_MAX (35) // 35 == mi_segment_bin(MI_SLICES_PER_SEGMENT) - -// OS thread local data -typedef struct mi_os_tld_s { - size_t region_idx; // start point for next allocation - mi_stats_t* stats; // points to tld stats -} mi_os_tld_t; - - -// Segments thread local data -typedef struct mi_segments_tld_s { - mi_span_queue_t spans[MI_SEGMENT_BIN_MAX+1]; // free slice spans inside segments - size_t count; // current number of segments; - size_t peak_count; // peak number of segments - size_t current_size; // current size of all segments - size_t peak_size; // peak size of all segments - size_t reclaim_count;// number of reclaimed (abandoned) segments - mi_stats_t* stats; // points to tld stats - mi_os_tld_t* os; // points to os stats -} mi_segments_tld_t; +// Milliseconds as in `int64_t` to avoid overflows +typedef int64_t mi_msecs_t; // Thread local data struct mi_tld_s { - unsigned long long heartbeat; // monotonic heartbeat count - bool recurse; // true if deferred was called; used to prevent infinite recursion. - mi_heap_t* heap_backing; // backing heap of this thread (cannot be deleted) - mi_heap_t* heaps; // list of heaps in this thread (so we can abandon all when the thread terminates) - mi_segments_tld_t segments; // segment tld - mi_os_tld_t os; // os tld - mi_stats_t stats; // statistics + mi_threadid_t thread_id; // thread id of this thread + size_t thread_seq; // thread sequence id (linear count of created threads) + int numa_node; // thread preferred numa node + mi_subproc_t* subproc; // sub-process this thread belongs to. + mi_theap_t* theaps; // list of theaps in this thread (so we can abandon all when the thread terminates) + mi_lock_t theaps_lock; // lock as the theaps list is sometimes accessed from another thread (on `mi_heap_free`) + bool recurse; // true if deferred was called; used to prevent infinite recursion. + bool is_in_threadpool; // true if this thread is part of a threadpool (and can run arbitrary tasks) + mi_memid_t memid; // provenance of the tld memory itself (meta or OS) }; + +/* ---------------------------------------------------------------------------- + Arenas are fixed area's of OS memory from which we can allocate + large blocks (>= MI_ARENA_MIN_BLOCK_SIZE). + In contrast to the rest of mimalloc, the arenas are shared between + threads and need to be accessed using atomic operations (using atomic `mi_bitmap_t`'s). + + Arenas are also used to for huge OS page (1GiB) reservations or for reserving + OS memory upfront which can be improve performance or is sometimes needed + on embedded devices. We can also employ this with WASI or `sbrk` systems + to reserve large arenas upfront and be able to reuse the memory more effectively. +-----------------------------------------------------------------------------*/ + +#define MI_ARENA_BIN_COUNT (MI_MAX_SINGLETON_BIN+1) +#define MI_ARENA_MIN_SIZE (MI_BCHUNK_BITS * MI_ARENA_SLICE_SIZE) // 32 MiB (or 8 MiB on 32-bit) +#define MI_ARENA_MAX_SIZE (MI_BITMAP_MAX_BIT_COUNT * MI_ARENA_SLICE_SIZE) + +typedef struct mi_bitmap_s mi_bitmap_t; // atomic bitmap (defined in `src/bitmap.h`) +typedef struct mi_bbitmap_s mi_bbitmap_t; // atomic binned bitmap (defined in `src/bitmap.h`) + +typedef struct mi_arena_pages_s { + mi_bitmap_t* pages; // all registered pages (abandoned and owned) + mi_bitmap_t* pages_abandoned[MI_ARENA_BIN_COUNT]; // abandoned pages per size bin (a set bit means the start of the page) + // followed by the bitmaps (whose siz`es depend on the arena size) +} mi_arena_pages_t; + + +// A memory arena +typedef struct mi_arena_s { + mi_memid_t memid; // provenance of the memory area + mi_subproc_t* subproc; // subprocess this arena belongs to (`this 'element-of' this->subproc->arenas`) + size_t arena_idx; // index in the arenas array + + size_t slice_count; // total size of the area in arena slices (of `MI_ARENA_SLICE_SIZE`) + size_t info_slices; // initial slices reserved for the arena bitmaps + int numa_node; // associated NUMA node + bool is_exclusive; // only allow allocations if specifically for this arena + mi_decl_align(8) // needed on some 32-bit platforms + _Atomic(mi_msecs_t) purge_expire; // expiration time when slices can be purged from `slices_purge`. + mi_commit_fun_t* commit_fun; // custom commit/decommit memory + void* commit_fun_arg; // user argument for a custom commit function + + size_t total_size; // for (user given) memory more than MI_ARENA_MAX_SIZE, we use N arena's to cover it. The first (parent) has the total size (and the other sub-arena's 0). + mi_arena_t* parent; // if this is a sub arena, this points to the first one in the memory area. + + mi_bbitmap_t* slices_free; // is the slice free? (a binned bitmap with size classes) + mi_bitmap_t* slices_committed; // is the slice committed? (i.e. accessible) + mi_bitmap_t* slices_dirty; // is the slice potentially non-zero? + mi_bitmap_t* slices_purge; // slices that can be purged + mi_page_t* pages_meta; // pre-allocated `slice_count` page meta info -- only used if `MI_PAGE_META_IS_SEPARATED!=0` + mi_arena_pages_t pages_main; // arena page bitmaps for the main heap are allocated up front as well + + // followed by the bitmaps (whose sizes depend on the arena size) + // note: when adding bitmaps revise `mi_arena_info_slices_needed` +} mi_arena_t; + + + +/* ----------------------------------------------------------- + Error codes passed to `_mi_fatal_error` + All are recoverable but EFAULT is a serious error and aborts by default in secure mode. + For portability define undefined error codes using common Unix codes: + +----------------------------------------------------------- */ + +#ifndef EAGAIN // double free +#define EAGAIN (11) +#endif +#ifndef ENOMEM // out of memory +#define ENOMEM (12) +#endif +#ifndef EFAULT // corrupted free-list or meta-data +#define EFAULT (14) +#endif +#ifndef EINVAL // trying to free an invalid pointer +#define EINVAL (22) +#endif +#ifndef EOVERFLOW // count*size overflow +#define EOVERFLOW (75) +#endif +#ifndef ENOENT // environment variable not found +#define ENOENT (2) #endif + + +/* ----------------------------------------------------------- + Debug constants +----------------------------------------------------------- */ + +#if !defined(MI_DEBUG_UNINIT) +#define MI_DEBUG_UNINIT (0xD0) +#endif +#if !defined(MI_DEBUG_FREED) +#define MI_DEBUG_FREED (0xDF) +#endif +#if !defined(MI_DEBUG_PADDING) +#define MI_DEBUG_PADDING (0xDE) +#endif + + +#endif // MI_TYPES_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/mimalloc.pc.in b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/mimalloc.pc.in index 36da2038..80922256 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/mimalloc.pc.in +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/mimalloc.pc.in @@ -1,11 +1,11 @@ prefix=@CMAKE_INSTALL_PREFIX@ -libdir=@libdir_for_pc_file@ -includedir=@includedir_for_pc_file@ +libdir=@mi_pc_libdir@ +includedir=@mi_pc_includedir@ Name: @PROJECT_NAME@ Description: A compact general purpose allocator with excellent performance Version: @PACKAGE_VERSION@ URL: https://github.com/microsoft/mimalloc/ -Libs: -L${libdir} -lmimalloc -Libs.private: @pc_libraries@ +Libs: -L${libdir} -l@mi_libname@ +Libs.private: @mi_pc_libraries@ Cflags: -I${includedir} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/readme.md b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/readme.md index a0296b43..374b6d48 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/readme.md +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/readme.md @@ -1,7 +1,10 @@ - - -[](https://dev.azure.com/Daan0324/mimalloc/_build?definitionId=1&_a=summary) +
+ v3: + v2: + v1: +   v3: +
# mimalloc @@ -12,17 +15,19 @@ is a general purpose allocator with excellent [performance](#performance) charac Initially developed by Daan Leijen for the runtime systems of the [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. -Latest release tag: `v2.1.7` (2024-05-21). -Latest v1 tag: `v1.8.7` (2024-05-21). +Latest release : `v3.4.1` (2026-07-14) recommended. +Latest v2 release: `v2.4.1` (2026-07-14) stable. +Latest v1 release: `v1.9.11` (2026-07-14) legacy. mimalloc is a drop-in replacement for `malloc` and can be used in other programs without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as: ``` > LD_PRELOAD=/usr/lib/libmimalloc.so myprogram ``` -It also includes a robust way to override the default allocator in [Windows](#override_on_windows). Notable aspects of the design include: +It also includes a way to dynamically override the default allocator in [Windows](#override_on_windows). +Notable aspects of the design include: -- __small and consistent__: the library is about 8k LOC using simple and +- __small and consistent__: the library is about 10k LOC using simple and consistent data structures. This makes it very suitable to integrate and adapt in other projects. For runtime systems it provides hooks for a monotonic _heartbeat_ and deferred freeing (for @@ -55,6 +60,7 @@ It also includes a robust way to override the default allocator in [Windows](#ov over our benchmarks. - __first-class heaps__: efficiently create and use multiple heaps to allocate across different regions. A heap can be destroyed at once instead of deallocating each object separately. + New: v3 has true first-class heaps where one can allocate in a heap from any thread. - __bounded__: it does not suffer from _blowup_ \[1\], has bounded worst-case allocation times (_wcat_) (upto OS primitives), bounded space overhead (~0.2% meta-data, with low internal fragmentation), and has no internal points of contention using only atomic operations. @@ -64,72 +70,81 @@ It also includes a robust way to override the default allocator in [Windows](#ov of benchmarks. There is also good huge OS page support for larger server programs. The [documentation](https://microsoft.github.io/mimalloc) gives a full overview of the API. -You can read more on the design of _mimalloc_ in the [technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action) which also has detailed benchmark results. +You can read more on the design of mimalloc in the [technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action) which also has detailed benchmark results. Enjoy! -### Branches - -* `master`: latest stable release (based on `dev-slice`). -* `dev`: development branch for mimalloc v1. Use this branch for submitting PR's. -* `dev-slice`: development branch for mimalloc v2. This branch is downstream of `dev` (and is essentially equal to `dev` except for -`src/segment.c`) - -### Releases +### Versions -Note: the `v2.x` version has a different algorithm for managing internal mimalloc pages (as slices) that tends to use reduce -memory usage - and fragmentation compared to mimalloc `v1.x` (especially for large workloads). Should otherwise have similar performance - (see [below](#performance)); please report if you observe any significant performance regression. +There are three maintained versions of mimalloc. These are mostly equal except for how the OS memory is handled. +New development is mostly on v3, while v1 and v2 are maintained with security and bug fixes. -* 2024-05-21, `v1.8.7`, `v2.1.7`: Fix build issues on less common platforms. Started upstreaming patches - from the CPython [integration](https://github.com/python/cpython/issues/113141#issuecomment-2119255217). Upstream `vcpkg` patches. -* 2024-05-13, `v1.8.6`, `v2.1.6`: Fix build errors on various (older) platforms. Refactored aligned allocation. -* 2024-04-22, `v1.8.4`, `v2.1.4`: Fixes various bugs and build issues. Add `MI_LIBC_MUSL` cmake flag for musl builds. - Free-ing code is refactored into a separate module (`free.c`). Mimalloc page info is simplified with the block size - directly available (and new `block_size_shift` to improve aligned block free-ing). - New approach to collection of abandoned segments: When - a thread terminates the segments it owns are abandoned (containing still live objects) and these can be - reclaimed by other threads. We no longer use a list of abandoned segments but this is now done using bitmaps in arena's - which is more concurrent (and more aggressive). Abandoned memory can now also be reclaimed if a thread frees an object in - an abandoned page (which can be disabled using `mi_option_abandoned_reclaim_on_free`). The option `mi_option_max_segment_reclaim` - gives a maximum percentage of abandoned segments that can be reclaimed per try (=10%). - -* 2023-04-24, `v1.8.2`, `v2.1.2`: Fixes build issues on freeBSD, musl, and C17 (UE 5.1.1). Reduce code size/complexity - by removing regions and segment-cache's and only use arenas with improved memory purging -- this may improve memory - usage as well for larger services. Renamed options for consistency. Improved Valgrind and ASAN checking. - -* 2023-04-03, `v1.8.1`, `v2.1.1`: Fixes build issues on some platforms. - -* 2023-03-29, `v1.8.0`, `v2.1.0`: Improved support dynamic overriding on Windows 11. Improved tracing precision - with [asan](#asan) and [Valgrind](#valgrind), and added Windows event tracing [ETW](#ETW) (contributed by Xinglong He). Created an OS - abstraction layer to make it easier to port and separate platform dependent code (in `src/prim`). Fixed C++ STL compilation on older Microsoft C++ compilers, and various small bug fixes. - -* 2022-12-23, `v1.7.9`, `v2.0.9`: Supports building with [asan](#asan) and improved [Valgrind](#valgrind) support. - Support arbitrary large alignments (in particular for `std::pmr` pools). - Added C++ STL allocators attached to a specific heap (thanks @vmarkovtsev). - Heap walks now visit all object (including huge objects). Support Windows nano server containers (by Johannes Schindelin,@dscho). - Various small bug fixes. - -* 2022-11-03, `v1.7.7`, `v2.0.7`: Initial support for [Valgrind](#valgrind) for leak testing and heap block overflow - detection. Initial - support for attaching heaps to a speficic memory area (only in v2). Fix `realloc` behavior for zero size blocks, remove restriction to integral multiple of the alignment in `alloc_align`, improved aligned allocation performance, reduced contention with many threads on few processors (thank you @dposluns!), vs2022 support, support `pkg-config`, . +- __v3__: recommended: simplifies the lock-free design of previous versions and improves sharing of + memory between threads. On certain large workloads this version may use + (much) less memory. Also supports true first-class heaps (that can allocate from any thread) + and has more efficient heap-walking (for the CPython GC for example). + (release tags: `v3.x`, development branch `dev3`). +- __v2__: stable mimalloc version. Uses thread-local segments to reduce fragmentation. (release tags: `v2.x`, development branch `dev2` and `main`) +- __v1__: legacy version: initial design of mimalloc (release tags: `v1.9.x`, development branch `dev`). Send PR's against this version if possible. -* 2022-04-14, `v1.7.6`, `v2.0.6`: fix fallback path for aligned OS allocation on Windows, improve Windows aligned allocation - even when compiling with older SDK's, fix dynamic overriding on macOS Monterey, fix MSVC C++ dynamic overriding, fix - warnings under Clang 14, improve performance if many OS threads are created and destroyed, fix statistics for large object - allocations, using MIMALLOC_VERBOSE=1 has no maximum on the number of error messages, various small fixes. - -* 2022-02-14, `v1.7.5`, `v2.0.5` (alpha): fix malloc override on - Windows 11, fix compilation with musl, potentially reduced - committed memory, add `bin/minject` for Windows, - improved wasm support, faster aligned allocation, - various small fixes. +### Releases +* 2026-07-14, `v1.9.11`, `v2.4.1`, `v3.4.1`: various bug and security fixes through LLM audit (by @Zoxc). + Fix issue with using OS memory instead of arenas for > 4GiB memory usage (v3), fix concurrency bug in concurrent + heap destroy (v3), detect riscV virtual address bits at runtime, add riscV TLS support, reduce spinlock waits (v2), + use TLS slots 126/127 on macOS (v3), and other small fixes. All metadata is now separated from heap objects in v3. +* 2026-04-29, `v1.9.10`, `v2.3.2`, `v3.3.2`: various bug and security fixes through LLM audit (by @Zoxc). + Only increase minimal purge size automatically if allow_thp is set to 2. Enable large OS alignment + on all platforms (fixing OS large pages on Windows). Fix accounting of committed memory on Linux/macOS. + Update MSVC atomics implementation when using C mode. Upstream Emscripten fixes. Proper atomic do-once + implementation. +* 2026-04-20, `v1.9.9`, `v2.3.1`, `v3.3.1`: various bug and security fixes. Special thanks to + @jinpzhanAMD, @res2k, and @GoldJohnKing for their help in improving Windows finalization, and + @Zoxc for his help in finding various issues. +* 2026-04-15, `v1.9.8`, `v2.3.0`, `v3.3.0`: initial support for github (binary) releases, + fix visiting of full pages during collection (performance), + fix THP alignment (performance), fix arm64 cross-compilation on Windows, enable guard pages in debug mode, + always use uncommitted areas between arenas (security), enable static overloading of `malloc` etc. on Windows with the + static CRT (by @Noxybot), fix TLS slot leak on Windows (v3), enable clean DLL load/unload with statically linked + mimalloc (v3), fix race in `mi_heap_destroy` (v3), by default put page meta info separate from allocated + objects (v3,security), fix C++ overrides for emscripten. Various bugs found by DeepTest include: + fix offset for `mi_heap_realloc_aligned`, fix `mi_(w)dupenv_s` buffer size, fix potential overflow in size options, + and error codes for `mi_reallocarr(ay)`. +* 2026-02-03, `v3.2.8` (rc3): Fix thread reinitialize issue on macOS. Fix SIMD codegen bug on older + GCC versions. Extend Windows TLS slot limit from 64 to 1088. Report commit statistics more precise. + Fixes issue in free-page search in arenas. +* 2026-01-15, `v1.9.7`, `v2.2.7`, `v3.2.7` (rc2): Fix zero initializing blocks that were OS allocated. + For v3 various bug and performance fixes. Fix Debian 32-bit compilation. +* 2026-01-08, `v1.9.6`, `v2.2.6`, `v3.2.6` (rc1): Important bug fixes. Many improvements to v3 including + true first-class heaps where one can allocate in heap from any thread, and track statistics per heap as well. + Added `MIMALLOC_ALLOW_THP` option. This is by default enabled except on Android. When THP is detected on v3, + mimalloc will set the `MIMALLOC_MINIMAL_PURGE_SIZE` to 2MiB to avoid breaking up potential THP huge pages. + v3 uses faster TLS access on Windows, and has improved performance for `mi_calloc` and aligned allocations. + Fixed rare race condition on older v3, fixed potential buffer overflow in debug statistics, add API for returning + allocated sizes on allocation and free. +* 2025-06-09, `v1.9.4`, `v2.2.4`, `v3.1.4` (beta) : Some important bug fixes, including a case where OS memory + was not always fully released. Improved v3 performance, build on XBox, fix build on Android, support interpose + for older macOS versions, use MADV_FREE_REUSABLE on macOS, always check commit success, better support for Windows + fixed TLS offset, etc. +* 2025-03-28, `v1.9.3`, `v2.2.3`, `v3.0.3` (beta) : Various small bug and build fixes, including: + fix arm32 pre v7 builds, fix mingw build, get runtime statistics, improve statistic commit counts, + fix execution on non BMI1 x64 systems. +* 2025-03-06, `v1.9.2`, `v2.2.2`, `v3.0.2-beta`: Various small bug and build fixes. + Add `mi_options_print`, `mi_arenas_print`, and the experimental `mi_stat_get` and `mi_stat_get_json`. + Add `mi_thread_set_in_threadpool` and `mi_heap_set_numa_affinity` (v3 only). Add vcpkg portfile. + Upgrade mimalloc-redirect to v1.3.2. `MI_OPT_ARCH` is off by default now but still assumes armv8.1-a on arm64 + for fast atomic operations. Add QNX support. +* 2025-01-03, `v1.8.9`, `v2.1.9`, `v3.0.1-alpha`: Interim release. Support Windows arm64. New [guarded](#guarded) build that can place OS + guard pages behind objects to catch buffer overflows as they occur. + Many small fixes: build on Windows arm64, cygwin, riscV, and dragonfly; fix Windows static library initialization to account for + thread local destructors (in Rust/C++); macOS tag change; macOS TLS slot fix; improve stats; + consistent `mimalloc.dll` on Windows (instead of `mimalloc-override.dll`); fix mimalloc-redirect on Win11 H2; + add 0-byte to canary; upstream CPython fixes; reduce .bss size; allow fixed TLS slot on Windows for improved performance. * [Older release notes](#older-release-notes) Special thanks to: +* Sergiy Kuryata for his contributions on reducing memory commit -- especially on Windows with the Windows thread pool (now implemented in v3). * [David Carlier](https://devnexen.blogspot.com/) (@devnexen) for his many contributions, and making mimalloc work better on many less common operating systems, like Haiku, Dragonfly, etc. * Mary Feofanova (@mary3000), Evgeniy Moiseenko, and Manuel Pöter (@mpoeter) for making mimalloc TSAN checkable, and finding @@ -152,7 +167,7 @@ mimalloc is used in various large scale low-latency services and programs, for e - + # Building @@ -160,13 +175,13 @@ mimalloc is used in various large scale low-latency services and programs, for e ## Windows Open `ide/vs2022/mimalloc.sln` in Visual Studio 2022 and build. -The `mimalloc` project builds a static library (in `out/msvc-x64`), while the -`mimalloc-override` project builds a DLL for overriding malloc +The `mimalloc-lib` project builds a static library (in `out/msvc-x64`), while the +`mimalloc-override-dll` project builds a DLL for overriding malloc in the entire program. -## macOS, Linux, BSD, etc. +## Linux, macOS, BSD, etc. -We use [`cmake`](https://cmake.org)1 as the build system: +We use [`cmake`](https://cmake.org) as the build system: ``` > mkdir -p out/release @@ -189,32 +204,58 @@ maintains detailed statistics as: > cmake -DCMAKE_BUILD_TYPE=Debug ../.. > make ``` + This will name the shared library as `libmimalloc-debug.so`. -Finally, you can build a _secure_ version that uses guard pages, encrypted -free lists, etc., as: +Finally, you can build a _secure_ version that uses guard pages, encrypted free lists, etc., as: + ``` > mkdir -p out/secure > cd out/secure > cmake -DMI_SECURE=ON ../.. > make ``` + This will name the shared library as `libmimalloc-secure.so`. -Use `ccmake`2 instead of `cmake` -to see and customize all the available build options. +Use `cmake ../.. -LH` to see all the available build options. -Notes: -1. Install CMake: `sudo apt-get install cmake` -2. Install CCMake: `sudo apt-get install cmake-curses-gui` +The examples use the default compiler. If you like to use another, use: +``` +> CC=clang CXX=clang++ cmake ../.. +``` + +## Cmake with Visual Studio + +You can also use cmake on Windows. Open a Visual Studio 2022 development prompt +and invoke `cmake` with the right [generator](https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2017%202022.html) +and architecture, like: + +``` +> cmake ..\.. -G "Visual Studio 17 2022" -A x64 -DMI_OVERRIDE=ON +``` -## Single source +The cmake build type is specified when actually building, for example: + +``` +> cmake --build . --config=Release +``` + +You can also install the [LLVM toolset](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-170#install-1) +on Windows to build with the `clang-cl` compiler directly: + +``` +> cmake ../.. -G "Visual Studio 17 2022" -T ClangCl +``` + + +## Single Source You can also directly build the single `src/static.c` file as part of your project without needing `cmake` at all. Make sure to also add the mimalloc `include` directory to the include path. -# Using the library +# Using the Library The preferred usage is including ``, linking with the shared- or static library, and using the `mi_malloc` API exclusively for allocation. For example, @@ -226,7 +267,7 @@ mimalloc uses only safe OS calls (`mmap` and `VirtualAlloc`) and can co-exist with other allocators linked to the same program. If you use `cmake`, you can simply use: ``` -find_package(mimalloc 1.4 REQUIRED) +find_package(mimalloc 1.8 REQUIRED) ``` in your `CMakeLists.txt` to find a locally installed mimalloc. Then use either: ``` @@ -240,7 +281,7 @@ to link with the static library. See `test\CMakeLists.txt` for an example. For best performance in C++ programs, it is also recommended to override the global `new` and `delete` operators. For convenience, mimalloc provides -[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) which does this for you -- just include it in a single(!) source file in your project. +[`mimalloc-new-delete.h`](include/mimalloc-new-delete.h) which does this for you -- just include it in a single(!) source file in your project. In C++, mimalloc also provides the `mi_stl_allocator` struct which implements the `std::allocator` interface. @@ -251,33 +292,52 @@ and statistics (`MIMALLOC_SHOW_STATS=1`) (in the debug version): 175451865205073170563711388363 = 374456281610909315237213 * 468551 -heap stats: peak total freed unit -normal 2: 16.4 kb 17.5 mb 17.5 mb 16 b ok -normal 3: 16.3 kb 15.2 mb 15.2 mb 24 b ok -normal 4: 64 b 4.6 kb 4.6 kb 32 b ok -normal 5: 80 b 118.4 kb 118.4 kb 40 b ok -normal 6: 48 b 48 b 48 b 48 b ok -normal 17: 960 b 960 b 960 b 320 b ok - -heap stats: peak total freed unit - normal: 33.9 kb 32.8 mb 32.8 mb 1 b ok - huge: 0 b 0 b 0 b 1 b ok - total: 33.9 kb 32.8 mb 32.8 mb 1 b ok -malloc requested: 32.8 mb - - committed: 58.2 kb 58.2 kb 58.2 kb 1 b ok - reserved: 2.0 mb 2.0 mb 2.0 mb 1 b ok - reset: 0 b 0 b 0 b 1 b ok - segments: 1 1 1 --abandoned: 0 - pages: 6 6 6 --abandoned: 0 - mmaps: 3 - mmap fast: 0 - mmap slow: 1 - threads: 0 - elapsed: 2.022s - process: user: 1.781s, system: 0.016s, faults: 756, reclaims: 0, rss: 2.7 mb +subproc 0 + blocks peak total current block total# + bin S 4: 75.3 KiB 55.2 MiB 0 32 B 1.8 M ok + bin S 6: 31.0 KiB 180.4 KiB 0 48 B 3.8 K ok + bin S 8: 64 B 64 B 0 64 B 1 ok + bin S 9: 160 B 160 B 0 80 B 2 ok + bin S 17: 1.2 KiB 1.2 KiB 0 320 B 4 ok + bin S 21: 640 B 3.1 KiB 0 640 B 5 ok + bin S 33: 5.0 KiB 5.0 KiB 0 5.0 KiB 1 ok + + binned : 84.2 Ki 41.5 Mi 0 ok + huge : 0 0 0 ok + total : 84.2 KiB 41.5 MiB 0 + malloc req: 29.7 MiB + + pages peak total current block total# + touched : 152.8 KiB 152.8 KiB 152.8 KiB + pages : 8 14 0 ok + abandoned : 1 249 0 ok + reclaima : 0 + reclaimf : 249 + reabandon : 0 + waits : 0 + extended : 38 + retire : 35 + searches : 0.7 avg + + arenas peak total current block total# + reserved : 1.0 GiB 1.0 GiB 1.0 GiB + committed : 4.8 MiB 4.8 MiB 4.4 MiB + reset : 0 + purged : 385.5 Ki + arenas : 1 + rollback : 0 + mmaps : 3 + commits : 0 + resets : 1 + purges : 2 + guarded : 0 + heaps : 1 1 1 + + process peak total current block total# + threads : 1 1 1 + numa nodes: 1 + elapsed : 0.553 s + process : user: 0.557 s, system: 0.013 s, faults: 29, peak rss: 2.1 MiB, peak commit: 4.8 MiB ``` The above model of using the `mi_` prefixed API is not always possible @@ -290,53 +350,51 @@ completely and redirect all calls to the _mimalloc_ library instead . You can set further options either programmatically (using [`mi_option_set`](https://microsoft.github.io/mimalloc/group__options.html)), or via environment variables: - `MIMALLOC_SHOW_STATS=1`: show statistics when the program terminates. -- `MIMALLOC_VERBOSE=1`: show verbose messages. +- `MIMALLOC_VERBOSE=1`: show verbose messages (including statistics). - `MIMALLOC_SHOW_ERRORS=1`: show error and warning messages. Advanced options: -- `MIMALLOC_ARENA_EAGER_COMMIT=2`: turns on eager commit for the large arenas (usually 1GiB) from which mimalloc - allocates segments and pages. Set this to 2 (default) to - only enable this on overcommit systems (e.g. Linux). Set this to 1 to enable explicitly on other systems - as well (like Windows or macOS) which may improve performance (as the whole arena is committed at once). - Note that eager commit only increases the commit but not the actual the peak resident set +- `MIMALLOC_ARENA_EAGER_COMMIT=2`: turns on eager commit for the large arenas (usually 1GiB) from which mimalloc + allocates segments and pages. Set this to 2 (default) to + only enable this on overcommit systems (e.g. Linux). Set this to 1 to enable explicitly on other systems + as well (like Windows or macOS) which may improve performance (as the whole arena is committed at once). + Note that eager commit only increases the commit but not the actual the peak resident set (rss) so it is generally ok to enable this. -- `MIMALLOC_PURGE_DELAY=N`: the delay in `N` milli-seconds (by default `10`) after which mimalloc will purge - OS pages that are not in use. This signals to the OS that the underlying physical memory can be reused which +- `MIMALLOC_PURGE_DELAY=N`: the delay in `N` milli-seconds (by default `1000` in v3) after which mimalloc will purge + OS pages that are not in use. This signals to the OS that the underlying physical memory can be reused which can reduce memory fragmentation especially in long running (server) programs. Setting `N` to `0` purges immediately when - a page becomes unused which can improve memory usage but also decreases performance. Setting `N` to a higher - value like `100` can improve performance (sometimes by a lot) at the cost of potentially using more memory at times. + a page becomes unused which can improve memory usage but also decreases performance. Setting it to `-1` disables purging completely. - `MIMALLOC_PURGE_DECOMMITS=1`: By default "purging" memory means unused memory is decommitted (`MEM_DECOMMIT` on Windows, `MADV_DONTNEED` (which decresease rss immediately) on `mmap` systems). Set this to 0 to instead "reset" unused memory on a purge (`MEM_RESET` on Windows, generally `MADV_FREE` (which does not decrease rss immediately) on `mmap` systems). - Mimalloc generally does not "free" OS memory but only "purges" OS memory, in other words, it tries to keep virtual + Mimalloc generally does not "free" OS memory but only "purges" OS memory, in other words, it tries to keep virtual address ranges and decommits within those ranges (to make the underlying physical memory available to other processes). Further options for large workloads and services: +- `MIMALLOC_ALLOW_THP=1`: By default always allow transparent huge pages (THP) on Linux systems. On Android only this is + by default off. When set to `0`, THP is disabled for the process that mimalloc runs in. If enabled, mimalloc also sets + the `MIMALLOC_MINIMAL_PURGE_SIZE` in v3 to 2MiB to avoid potentially breaking up transparent huge pages when purging memory. - `MIMALLOC_USE_NUMA_NODES=N`: pretend there are at most `N` NUMA nodes. If not set, the actual NUMA nodes are detected at runtime. Setting `N` to 1 may avoid problems in some virtual environments. Also, setting it to a lower number than the actual NUMA nodes is fine and will only cause threads to potentially allocate more memory across actual NUMA nodes (but this can happen in any case as NUMA local allocation is always a best effort but not guaranteed). -- `MIMALLOC_ALLOW_LARGE_OS_PAGES=1`: use large OS pages (2 or 4MiB) when available; for some workloads this can significantly - improve performance. When this option is disabled, it also disables transparent huge pages (THP) for the process - (on Linux and Android). Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs +- `MIMALLOC_ALLOW_LARGE_OS_PAGES=0`: Set to 1 to use large OS pages (2 or 4MiB) when available; for some workloads this can + significantly improve performance. However, large OS pages cannot be purged or shared with other processes so may lead + to increased memory usage in some cases. + Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs to explicitly give permissions for large OS pages (as on [Windows][windows-huge] and [Linux][linux-huge]). However, sometimes the OS is very slow to reserve contiguous physical memory for large OS pages so use with care on systems that - can have fragmented memory (for that reason, we generally recommend to use `MIMALLOC_RESERVE_HUGE_OS_PAGES` instead whenever possible). + can have fragmented memory (for that reason, we generally recommend to use `MIMALLOC_RESERVE_HUGE_OS_PAGES` instead whenever possible). - `MIMALLOC_RESERVE_HUGE_OS_PAGES=N`: where `N` is the number of 1GiB _huge_ OS pages. This reserves the huge pages at startup and sometimes this can give a large (latency) performance improvement on big workloads. - Usually it is better to not use `MIMALLOC_ALLOW_LARGE_OS_PAGES=1` in combination with this setting. Just like large + Usually it is better to not use `MIMALLOC_ALLOW_LARGE_OS_PAGES=1` in combination with this setting. Just like large OS pages, use with care as reserving contiguous physical memory can take a long time when memory is fragmented (but reserving the huge pages is done at startup only once). Note that we usually need to explicitly give permission for huge OS pages (as on [Windows][windows-huge] and [Linux][linux-huge])). - With huge OS pages, it may be beneficial to set the setting - `MIMALLOC_EAGER_COMMIT_DELAY=N` (`N` is 1 by default) to delay the initial `N` segments (of 4MiB) - of a thread to not allocate in the huge OS pages; this prevents threads that are short lived - and allocate just a little to take up space in the huge OS page area (which cannot be purged as huge OS pages are pinned - to physical memory). The huge pages are usually allocated evenly among NUMA nodes. We can use `MIMALLOC_RESERVE_HUGE_OS_PAGES_AT=N` where `N` is the numa node (starting at 0) to allocate all the huge pages at a specific numa node instead. @@ -353,8 +411,7 @@ OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the _mimalloc_ can be build in secure mode by using the `-DMI_SECURE=ON` flags in `cmake`. This build enables various mitigations to make mimalloc more robust against exploits. In particular: -- All internal mimalloc pages are surrounded by guard pages and the heap metadata is behind a guard page as well (so a buffer overflow - exploit cannot reach into the metadata). +- All internal mimalloc page meta-data is surrounded by guard pages (so a buffer overflow exploit cannot reach into the metadata). - All free list pointers are [encoded](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396) with per-page keys which is used both to prevent overwrites with a known pointer, as well as to detect heap corruption. @@ -362,18 +419,48 @@ to make mimalloc more robust against exploits. In particular: - The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc from the OS are also address randomized. +- If enabling `-DMI_SECURE_FULL=ON` there will also be guard pages at the end of each (64KiB) mimalloc page (thus interleaving + valid block data with inaccessible gaps). This setting is not recommended in general as it is more expensive and can lead to + reaching the maximum VMA limit on Linux systems if the heap gets too large. As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees. ## Debug Mode -When _mimalloc_ is built using debug mode, various checks are done at runtime to catch development errors. +When _mimalloc_ is built using debug mode, (`-DCMAKE_BUILD_TYPE=Debug`), +various checks are done at runtime to catch development errors. - Statistics are maintained in detail for each object size. They can be shown using `MIMALLOC_SHOW_STATS=1` at runtime. - All objects have padding at the end to detect (byte precise) heap block overflows. - Double free's, and freeing invalid heap pointers are detected. - Corrupted free-lists and some forms of use-after-free are detected. +## Guarded Mode + +_mimalloc_ can be build in guarded mode using the `-DMI_GUARDED=ON` flags in `cmake`. +This is `ON` by default when building a debug version of mimalloc. +Guarded mode enables placing OS guard pages behind certain object allocations to catch buffer overflows as they occur. +This can be invaluable to catch buffer-overflow bugs in large programs. However, it also means that any object +allocated with a guard page takes at least 8 KiB memory for the guard page and its alignment. As such, allocating +a guard page for every allocation may be too expensive both in terms of memory, and in terms of performance with +many system calls. Therefore, there are various environment variables (and options) to tune this: + +- `MIMALLOC_GUARDED_SAMPLE_RATE=N`: Set the sample rate to `N` (by default 0). This mode places a guard page + behind every `N` suitable object allocations (per thread). Since the performance in guarded mode without placing + guard pages is close to release mode, this can be used to enable guard pages even in production to catch latent + buffer overflow bugs. Set the sample rate to `1` to guard every object, and to `0` to place no guard pages at all. + +- `MIMALLOC_GUARDED_SAMPLE_SEED=N`: Start sampling at `N` (by default random). Can be used to reproduce a buffer + overflow if needed. + +- `MIMALLOC_GUARDED_MIN=N`, `MIMALLOC_GUARDED_MAX=N`: Minimal and maximal _rounded_ object sizes for which a guard + page is considered (`0` and `1GiB` respectively). If you suspect a buffer overflow occurs with an object of size + 141, set the minimum and maximum to `148` and the sample rate to `1` to have all of those guarded. + +- `MIMALLOC_GUARDED_PRECISE=1`: If we have an object of size 13, we would usually place it an aligned 16 bytes in + front of the guard page. Using `MIMALLOC_GUARDED_PRECISE` places it exactly 13 bytes before a page so that even + a 1 byte overflow is detected. This violates the C/C++ minimal alignment guarantees though so use with care. + # Overriding Standard Malloc @@ -417,41 +504,44 @@ the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-i ### Dynamic Override on Windows -Dynamically overriding on mimalloc on Windows -is robust and has the particular advantage to be able to redirect all malloc/free calls that go through -the (dynamic) C runtime allocator, including those from other DLL's or libraries. -As it intercepts all allocation calls on a low level, it can be used reliably -on large programs that include other 3rd party components. -There are four requirements to make the overriding work robustly: +We use a separate redirection DLL to override mimalloc on Windows +such that we redirect all malloc/free calls that go through the (dynamic) C runtime allocator, +including those from other DLL's or libraries. As it intercepts all allocation calls on a low level, +it can be used on large programs that include other 3rd party components. +There are four requirements to make the overriding work well: 1. Use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch). -2. Link your program explicitly with `mimalloc-override.dll` library. - To ensure the `mimalloc-override.dll` is loaded at run-time it is easiest to insert some - call to the mimalloc API in the `main` function, like `mi_version()` - (or use the `/INCLUDE:mi_version` switch on the linker). See the `mimalloc-override-test` project - for an example on how to use this. -3. The [`mimalloc-redirect.dll`](bin) (or `mimalloc-redirect32.dll`) must be put - in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency of that DLL). - The redirection DLL ensures that all calls to the C runtime malloc API get redirected to - mimalloc functions (which reside in `mimalloc-override.dll`). -4. Ensure the `mimalloc-override.dll` comes as early as possible in the import + +2. Link your program explicitly with the `mimalloc.dll.lib` export library for the `mimalloc.dll`. + (which must be compiled with `-DMI_OVERRIDE=ON`, which is the default though). + To ensure the `mimalloc.dll` is actually loaded at run-time it is easiest + to insert some call to the mimalloc API in the `main` function, like `mi_version()` + (or use the `/include:mi_version` switch on the linker command, or + similarly, `#pragma comment(linker, "/include:mi_version")` in some source file). + See the `mimalloc-test-override` project for an example on how to use this. + +3. The `mimalloc-redirect.dll` must be put in the same directory as the main + `mimalloc.dll` at runtime (as it is a dependency of that DLL). + The redirection DLL ensures that all calls to the C runtime malloc API get + redirected to mimalloc functions (which reside in `mimalloc.dll`). + +4. Ensure the `mimalloc.dll` comes as early as possible in the import list of the final executable (so it can intercept all potential allocations). + You can use `minject -l ` to check this if needed. -For best performance on Windows with C++, it -is also recommended to also override the `new`/`delete` operations (by including -[`mimalloc-new-delete.h`](include/mimalloc-new-delete.h) +For best performance on Windows with C++, it is also recommended to also override +the `new`/`delete` operations (by including [`mimalloc-new-delete.h`](include/mimalloc-new-delete.h) a single(!) source file in your project). The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic -overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully redirected. +overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully +redirected. + +For different platforms than x64, you may need a specific [redirection dll](bin). +Furthermore, we cannot always re-link an executable or ensure `mimalloc.dll` comes +first in the import table. In such cases the [`minject`](bin) tool can be used +to patch the executable's import tables. -We cannot always re-link an executable with `mimalloc-override.dll`, and similarly, we cannot always -ensure the the DLL comes first in the import table of the final executable. -In many cases though we can patch existing executables without any recompilation -if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the `mimalloc-override.dll` -into the import table (and put `mimalloc-redirect.dll` in the same folder) -Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388) or -the [`minject`](bin) program. ## Static override @@ -462,6 +552,7 @@ an object file instead of a library file as linkers give preference to that over archives to resolve symbols. To ensure that the standard malloc interface resolves to the _mimalloc_ library, link it as the first object file. For example: + ``` > gcc -o myprogram mimalloc.o myfile1.c ... ``` @@ -469,16 +560,20 @@ object file. For example: Another way to override statically that works on all platforms, is to link statically to mimalloc (as shown in the introduction) and include a header file in each source file that re-defines `malloc` etc. to `mi_malloc`. -This is provided by [`mimalloc-override.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-override.h). This only works reliably though if all sources are +This is provided by [`mimalloc-override.h`](include/mimalloc-override.h). This only works +reliably though if all sources are under your control or otherwise mixing of pointers from different heaps may occur! +Note: recently we also enabled static overloading on Windows. In that case you need +to link with the static CRT _release_ runtime (`/MT`) and link with the static +`mimalloc(-debug).obj` (to take precendence over the definitions in the CRT library). # Tools Generally, we recommend using the standard allocator with memory tracking tools, but mimalloc -can also be build to support the [address sanitizer][asan] or the excellent [Valgrind] tool. +can also be build to support the [address sanitizer][asan] or the excellent [Valgrind] tool. Moreover, it can be build to support Windows event tracing ([ETW]). -This has a small performance overhead but does allow detecting memory leaks and byte-precise +This has a small performance overhead but does allow detecting memory leaks and byte-precise buffer overflows directly on final executables. See also the `test/test-wrong.c` file to test with various tools. ## Valgrind @@ -505,9 +600,13 @@ you also need to tell `valgrind` to not intercept those calls itself, and use: By setting the `MIMALLOC_SHOW_STATS` environment variable you can check that mimalloc is indeed used and not the standard allocator. Even though the [Valgrind option][valgrind-soname] -is called `--soname-synonyms`, this also -works when overriding with a static library or object file. Unfortunately, it is not possible to -dynamically override mimalloc using `LD_PRELOAD` together with `valgrind`. +is called `--soname-synonyms`, this also works when overriding with a static library or object file. +To dynamically override mimalloc using `LD_PRELOAD` together with `valgrind`, use: + +``` +> valgrind --trace-children=yes --soname-synonyms=somalloc=*mimalloc* /usr/bin/env LD_PRELOAD=/usr/lib/libmimalloc.so -- +``` + See also the `test/test-wrong.c` file to test with `valgrind`. Valgrind support is in its initial development -- please report any issues. @@ -523,7 +622,7 @@ To build with the address sanitizer, use the `-DMI_TRACK_ASAN=ON` cmake option: > cmake ../.. -DMI_TRACK_ASAN=ON ``` -This can also be combined with secure mode or debug mode. +This can also be combined with secure mode or debug mode. You can then run your programs as:' ``` @@ -531,7 +630,7 @@ You can then run your programs as:' ``` When you link a program with an address sanitizer build of mimalloc, you should -generally compile that program too with the address sanitizer enabled. +generally compile that program too with the address sanitizer enabled. For example, assuming you build mimalloc in `out/debug`: ``` @@ -540,23 +639,23 @@ clang -g -o test-wrong -Iinclude test/test-wrong.c out/debug/libmimalloc-asan-de Since the address sanitizer redirects the standard allocation functions, on some platforms (macOSX for example) it is required to compile mimalloc with `-DMI_OVERRIDE=OFF`. -Adress sanitizer support is in its initial development -- please report any issues. +Address sanitizer support is in its initial development -- please report any issues. [asan]: https://github.com/google/sanitizers/wiki/AddressSanitizer ## ETW Event tracing for Windows ([ETW]) provides a high performance way to capture all allocations though -mimalloc and analyze them later. To build with ETW support, use the `-DMI_TRACK_ETW=ON` cmake option. +mimalloc and analyze them later. To build with ETW support, use the `-DMI_TRACK_ETW=ON` cmake option. -You can then capture an allocation trace using the Windows performance recorder (WPR), using the +You can then capture an allocation trace using the Windows performance recorder (WPR), using the `src/prim/windows/etw-mimalloc.wprp` profile. In an admin prompt, you can use: ``` > wpr -start src\prim\windows\etw-mimalloc.wprp -filemode > > wpr -stop .etl -``` -and then open `.etl` in the Windows Performance Analyzer (WPA), or +``` +and then open `.etl` in the Windows Performance Analyzer (WPA), or use a tool like [TraceControl] that is specialized for analyzing mimalloc traces. [ETW]: https://learn.microsoft.com/en-us/windows-hardware/test/wpt/event-tracing-for-windows @@ -601,7 +700,7 @@ as [mimalloc-bench](https://github.com/daanx/mimalloc-bench). ## Benchmark Results on a 16-core AMD 5950x (Zen3) Testing on the 16-core AMD 5950x processor at 3.4Ghz (4.9Ghz boost), with -with 32GiB memory at 3600Mhz, running Ubuntu 20.04 with glibc 2.31 and GCC 9.3.0. +32GiB memory at 3600Mhz, running Ubuntu 20.04 with glibc 2.31 and GCC 9.3.0. We measure three versions of _mimalloc_: the main version `mi` (tag:v1.7.0), the new v2.0 beta version as `xmi` (tag:v2.0.0), and the main version in secure mode as `smi` (tag:v1.7.0). @@ -823,6 +922,50 @@ provided by the bot. You will only need to do this once across all repos using o # Older Release Notes +* 2024-05-21, `v1.8.7`, `v2.1.7`: Fix build issues on less common platforms. Started upstreaming patches + from the CPython [integration](https://github.com/python/cpython/issues/113141#issuecomment-2119255217). Upstream `vcpkg` patches. +* 2024-05-13, `v1.8.6`, `v2.1.6`: Fix build errors on various (older) platforms. Refactored aligned allocation. +* 2024-04-22, `v1.8.4`, `v2.1.4`: Fixes various bugs and build issues. Add `MI_LIBC_MUSL` cmake flag for musl builds. + Free-ing code is refactored into a separate module (`free.c`). Mimalloc page info is simplified with the block size + directly available (and new `block_size_shift` to improve aligned block free-ing). + New approach to collection of abandoned segments: When + a thread terminates the segments it owns are abandoned (containing still live objects) and these can be + reclaimed by other threads. We no longer use a list of abandoned segments but this is now done using bitmaps in arena's + which is more concurrent (and more aggressive). Abandoned memory can now also be reclaimed if a thread frees an object in + an abandoned page (which can be disabled using `mi_option_abandoned_reclaim_on_free`). The option `mi_option_max_segment_reclaim` + gives a maximum percentage of abandoned segments that can be reclaimed per try (=10%). + +* 2023-04-24, `v1.8.2`, `v2.1.2`: Fixes build issues on freeBSD, musl, and C17 (UE 5.1.1). Reduce code size/complexity + by removing regions and segment-cache's and only use arenas with improved memory purging -- this may improve memory + usage as well for larger services. Renamed options for consistency. Improved Valgrind and ASAN checking. + +* 2023-04-03, `v1.8.1`, `v2.1.1`: Fixes build issues on some platforms. + +* 2023-03-29, `v1.8.0`, `v2.1.0`: Improved support dynamic overriding on Windows 11. Improved tracing precision + with [asan](#asan) and [Valgrind](#valgrind), and added Windows event tracing [ETW](#ETW) (contributed by Xinglong He). Created an OS + abstraction layer to make it easier to port and separate platform dependent code (in `src/prim`). Fixed C++ STL compilation on older Microsoft C++ compilers, and various small bug fixes. + +* 2022-12-23, `v1.7.9`, `v2.0.9`: Supports building with [asan](#asan) and improved [Valgrind](#valgrind) support. + Support arbitrary large alignments (in particular for `std::pmr` pools). + Added C++ STL allocators attached to a specific heap (thanks @vmarkovtsev). + Heap walks now visit all object (including huge objects). Support Windows nano server containers (by Johannes Schindelin,@dscho). + Various small bug fixes. + +* 2022-11-03, `v1.7.7`, `v2.0.7`: Initial support for [Valgrind](#valgrind) for leak testing and heap block overflow + detection. Initial + support for attaching heaps to a specific memory area (only in v2). Fix `realloc` behavior for zero size blocks, remove restriction to integral multiple of the alignment in `alloc_align`, improved aligned allocation performance, reduced contention with many threads on few processors (thank you @dposluns!), vs2022 support, support `pkg-config`, . + +* 2022-04-14, `v1.7.6`, `v2.0.6`: fix fallback path for aligned OS allocation on Windows, improve Windows aligned allocation + even when compiling with older SDK's, fix dynamic overriding on macOS Monterey, fix MSVC C++ dynamic overriding, fix + warnings under Clang 14, improve performance if many OS threads are created and destroyed, fix statistics for large object + allocations, using MIMALLOC_VERBOSE=1 has no maximum on the number of error messages, various small fixes. + +* 2022-02-14, `v1.7.5`, `v2.0.5` (alpha): fix malloc override on + Windows 11, fix compilation with musl, potentially reduced + committed memory, add `bin/minject` for Windows, + improved wasm support, faster aligned allocation, + various small fixes. + * 2021-11-14, `v1.7.3`, `v2.0.3` (beta): improved WASM support, improved macOS support and performance (including M1), improved performance for v2 for large objects, Python integration improvements, more standard installation directories, various small fixes. diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-aligned.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-aligned.c index ba629ef3..97dc6045 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-aligned.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-aligned.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,7 +7,7 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/prim.h" // mi_prim_get_default_heap +#include "mimalloc/prim.h" // _mi_theap_default #include // memset @@ -16,42 +16,83 @@ terms of the MIT license. A copy of the license can be found in the file // ------------------------------------------------------ static bool mi_malloc_is_naturally_aligned( size_t size, size_t alignment ) { - // objects up to `MI_MAX_ALIGN_GUARANTEE` are allocated aligned to their size (see `segment.c:_mi_segment_page_start`). + // certain blocks are always allocated at a certain natural alignment. + // (see also `arena.c:mi_arenas_page_alloc_fresh`). mi_assert_internal(_mi_is_power_of_two(alignment) && (alignment > 0)); if (alignment > size) return false; - if (alignment <= MI_MAX_ALIGN_SIZE) return true; const size_t bsize = mi_good_size(size); - return (bsize <= MI_MAX_ALIGN_GUARANTEE && (bsize & (alignment-1)) == 0); + const bool ok = (bsize <= MI_PAGE_MAX_START_BLOCK_ALIGN2 && _mi_is_power_of_two(bsize)) || // power-of-two under N + (alignment==MI_PAGE_OSPAGE_BLOCK_ALIGN2 && (bsize % MI_PAGE_OSPAGE_BLOCK_ALIGN2)==0); // or multiple of N + if (ok) { mi_assert_internal((bsize & (alignment-1)) == 0); } // since both power of 2 and alignment <= size + return ok; } +#if MI_GUARDED +static mi_decl_restrict void* mi_theap_malloc_guarded_aligned(mi_theap_t* theap, size_t size, size_t alignment, bool zero) mi_attr_noexcept { + // use over allocation for guarded blocksl + #if MI_THEAP_INITASNULL + if mi_unlikely(theap==NULL) { theap = _mi_theap_empty_get(); } + #endif + mi_assert_internal(alignment > 0 && alignment < MI_PAGE_MAX_OVERALLOC_ALIGN); + if mi_unlikely(alignment >= MI_PAGE_MAX_OVERALLOC_ALIGN || size > (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE - alignment)) { + _mi_error_message(EOVERFLOW, "(guarded) aligned allocation request is too large (size %zu, alignment %zu)\n", size, alignment); + return NULL; + } + const size_t oversize = size + alignment - 1; + void* const base = _mi_theap_malloc_guarded(theap, oversize, zero); + if (base==NULL) return NULL; + void* const p = _mi_align_up_ptr(base, alignment); + mi_track_align(base, p, (uint8_t*)p - (uint8_t*)base, size); + mi_assert_internal(mi_usable_size(p) >= size); + mi_assert_internal(_mi_is_aligned(p, alignment)); + return p; +} + +static void* mi_theap_malloc_zero_no_guarded(mi_theap_t* theap, size_t size, bool zero, size_t* usable) { + #if MI_THEAP_INITASNULL + if mi_unlikely(theap==NULL) { theap = _mi_theap_empty_get(); } + #endif + const size_t rate = theap->guarded_sample_rate; + // only write if `rate!=0` so we don't write to the constant `_mi_theap_empty` + if (rate != 0) { theap->guarded_sample_rate = 0; } + void* p = _mi_theap_malloc_zero(theap, size, zero, usable); + if (rate != 0) { theap->guarded_sample_rate = rate; } + return p; +} +#else +static void* mi_theap_malloc_zero_no_guarded(mi_theap_t* theap, size_t size, bool zero, size_t* usable) { + return _mi_theap_malloc_zero(theap, size, zero, usable); +} +#endif + // Fallback aligned allocation that over-allocates -- split out for better codegen -static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept +static mi_decl_noinline void* mi_theap_malloc_zero_aligned_at_overalloc(mi_theap_t* const theap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* usable) mi_attr_noexcept { mi_assert_internal(size <= (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE)); mi_assert_internal(alignment != 0 && _mi_is_power_of_two(alignment)); void* p; size_t oversize; - if mi_unlikely(alignment > MI_BLOCK_ALIGNMENT_MAX) { - // use OS allocation for very large alignment and allocate inside a huge page (dedicated segment with 1 page) - // This can support alignments >= MI_SEGMENT_SIZE by ensuring the object can be aligned at a point in the - // first (and single) page such that the segment info is `MI_SEGMENT_SIZE` bytes before it (so it can be found by aligning the pointer down) + if mi_unlikely(alignment > MI_PAGE_MAX_OVERALLOC_ALIGN) { + // use OS allocation for large alignments and allocate inside a singleton page (not in an arena) + // This can support alignments >= MI_PAGE_ALIGN by ensuring the object can be aligned + // in the first (and single) page such that the page info is `MI_PAGE_ALIGN` bytes before it (and can be found in the _mi_page_map). if mi_unlikely(offset != 0) { // todo: cannot support offset alignment for very large alignments yet - #if MI_DEBUG > 0 - _mi_error_message(EOVERFLOW, "aligned allocation with a very large alignment cannot be used with an alignment offset (size %zu, alignment %zu, offset %zu)\n", size, alignment, offset); - #endif + _mi_error_message(EOVERFLOW, "aligned allocation with a large alignment cannot be used with an alignment offset (size %zu, alignment %zu, offset %zu)\n", size, alignment, offset); return NULL; } oversize = (size <= MI_SMALL_SIZE_MAX ? MI_SMALL_SIZE_MAX + 1 /* ensure we use generic malloc path */ : size); - p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment); // the page block size should be large enough to align in the single huge page block - // zero afterwards as only the area from the aligned_p may be committed! + // note: no guarded as alignment > 0 + p = _mi_theap_malloc_zero_ex(theap, oversize, zero, alignment, usable); // the page block size should be large enough to align in the single huge page block if (p == NULL) return NULL; } else { // otherwise over-allocate - oversize = size + alignment - 1; - p = _mi_heap_malloc_zero(heap, oversize, zero); + mi_assert_internal(size <= (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) && alignment <= MI_PAGE_MAX_OVERALLOC_ALIGN); + mi_assert_internal(size < SIZE_MAX - alignment); // `oversize` cannot overflow + oversize = (size < MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : size) + alignment - 1; // adjust for size <= 16; with size 0 and alignment 64k, we would allocate a 64k block and pointing just beyond that. + p = mi_theap_malloc_zero_no_guarded(theap, oversize, zero, usable); if (p == NULL) return NULL; } @@ -61,69 +102,87 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t const uintptr_t adjust = (poffset == 0 ? 0 : alignment - poffset); mi_assert_internal(adjust < alignment); void* aligned_p = (void*)((uintptr_t)p + adjust); + + // note: after the above allocation, the page may be abandoned now (as it became full, see `page.c:_mi_malloc_generic`) + // and we no longer own it. We should be careful to only read constant fields in the page, + // or use safe atomic access as in `mi_page_set_has_interior_pointers`. + // (we can access the page though since the just allocated pointer keeps it alive) + mi_page_t* page = _mi_ptr_page(p); if (aligned_p != p) { - mi_page_t* page = _mi_ptr_page(p); - mi_page_set_has_aligned(page, true); + mi_page_set_has_interior_pointers(page, true); + #if MI_GUARDED + // set tag to aligned so mi_usable_size works with guard pages + if (adjust >= sizeof(mi_block_t)) { + mi_block_t* const block = (mi_block_t*)p; + block->next = MI_BLOCK_TAG_ALIGNED; + } + #endif _mi_padding_shrink(page, (mi_block_t*)p, adjust + size); } // todo: expand padding if overallocated ? - mi_assert_internal(mi_page_usable_block_size(_mi_ptr_page(p)) >= adjust + size); - mi_assert_internal(p == _mi_page_ptr_unalign(_mi_ptr_page(aligned_p), aligned_p)); + mi_assert_internal(mi_page_usable_block_size(page) >= adjust + size); mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0); mi_assert_internal(mi_usable_size(aligned_p)>=size); mi_assert_internal(mi_usable_size(p) == mi_usable_size(aligned_p)+adjust); + #if MI_DEBUG > 1 + mi_page_t* const apage = _mi_ptr_page(aligned_p); + void* unalign_p = _mi_page_ptr_unalign(apage, aligned_p); + mi_assert_internal(p == unalign_p); + #endif // now zero the block if needed - if (alignment > MI_BLOCK_ALIGNMENT_MAX) { - // for the tracker, on huge aligned allocations only the memory from the start of the large block is defined - mi_track_mem_undefined(aligned_p, size); - if (zero) { - _mi_memzero_aligned(aligned_p, mi_usable_size(aligned_p)); - } - } + //if (alignment > MI_PAGE_MAX_OVERALLOC_ALIGN) { + // // for the tracker, on huge aligned allocations only from the start of the large block is defined + // mi_track_mem_undefined(aligned_p, size); + // if (zero) { + // _mi_memzero_aligned(aligned_p, mi_usable_size(aligned_p)); + // } + //} if (p != aligned_p) { mi_track_align(p,aligned_p,adjust,mi_usable_size(aligned_p)); + #if MI_GUARDED + mi_track_mem_defined(p, sizeof(mi_block_t)); + #endif } return aligned_p; } // Generic primitive aligned allocation -- split out for better codegen -static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept +static mi_decl_noinline void* mi_theap_malloc_zero_aligned_at_generic(mi_theap_t* const theap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* usable) mi_attr_noexcept { mi_assert_internal(alignment != 0 && _mi_is_power_of_two(alignment)); // we don't allocate more than MI_MAX_ALLOC_SIZE (see ) - if mi_unlikely(size > (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE)) { - #if MI_DEBUG > 0 + if mi_unlikely(size > (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE)) { _mi_error_message(EOVERFLOW, "aligned allocation request is too large (size %zu, alignment %zu)\n", size, alignment); - #endif return NULL; } - + // use regular allocation if it is guaranteed to fit the alignment constraints. - // this is important to try as the fast path in `mi_heap_malloc_zero_aligned` only works when there exist + // this is important to try as the fast path in `mi_theap_malloc_zero_aligned` only works when there exist // a page with the right block size, and if we always use the over-alloc fallback that would never happen. if (offset == 0 && mi_malloc_is_naturally_aligned(size,alignment)) { - void* p = _mi_heap_malloc_zero(heap, size, zero); + void* p = mi_theap_malloc_zero_no_guarded(theap, size, zero, usable); mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0); - const bool is_aligned_or_null = (((uintptr_t)p) & (alignment-1))==0; + const bool is_aligned_or_null = (((uintptr_t)p) & (alignment-1))==0; if mi_likely(is_aligned_or_null) { return p; } else { // this should never happen if the `mi_malloc_is_naturally_aligned` check is correct.. mi_assert(false); - mi_free(p); + mi_free(p); } } // fall back to over-allocation - return mi_heap_malloc_zero_aligned_at_overalloc(heap,size,alignment,offset,zero); + return mi_theap_malloc_zero_aligned_at_overalloc(theap,size,alignment,offset,zero,usable); } + // Primitive aligned allocation -static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept +static inline void* mi_theap_malloc_zero_aligned_at(mi_theap_t* const theap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* usable) mi_attr_noexcept { // note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size. if mi_unlikely(alignment == 0 || !_mi_is_power_of_two(alignment)) { // require power-of-two (see ) @@ -132,94 +191,137 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t #endif return NULL; } - + + #if MI_GUARDED + #if MI_THEAP_INITASNULL + if mi_likely(theap!=NULL) + #endif + if (offset==0 && alignment < MI_PAGE_MAX_OVERALLOC_ALIGN && mi_theap_malloc_use_guarded(theap,size)) { + return mi_theap_malloc_guarded_aligned(theap, size, alignment, zero); + } + #endif + // try first if there happens to be a small block available with just the right alignment - if mi_likely(size <= MI_SMALL_SIZE_MAX && alignment <= size) { - const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)` - const size_t padsize = size + MI_PADDING_SIZE; - mi_page_t* page = _mi_heap_get_free_small_page(heap, padsize); - if mi_likely(page->free != NULL) { - const bool is_aligned = (((uintptr_t)page->free + offset) & align_mask)==0; - if mi_likely(is_aligned) - { - #if MI_STAT>1 - mi_heap_stat_increase(heap, malloc, size); - #endif - void* p = (zero ? _mi_page_malloc_zeroed(heap,page,padsize) : _mi_page_malloc(heap,page,padsize)); // call specific page malloc for better codegen - mi_assert_internal(p != NULL); - mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); - mi_track_malloc(p,size,zero); - return p; + // since most small power-of-2 blocks (under MI_PAGE_MAX_BLOCK_START_ALIGN2) are already + // naturally aligned this can be often the case. + #if MI_THEAP_INITASNULL + if mi_likely(theap!=NULL) + #endif + { + if mi_likely(size <= MI_SMALL_SIZE_MAX && alignment <= size) { + const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)` + const size_t padsize = size + MI_PADDING_SIZE; + mi_page_t* page = _mi_theap_get_free_small_page(theap, padsize); + if mi_likely(page->free != NULL) { + const bool is_aligned = (((uintptr_t)page->free + offset) & align_mask)==0; + if mi_likely(is_aligned) + { + if (usable!=NULL) { *usable = mi_page_usable_block_size(page); } + void* p = _mi_page_malloc_zero(theap, page, padsize, zero); + mi_assert_internal(p != NULL); + mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); + mi_track_malloc(p, size, zero); + return p; + } } } } // fallback to generic aligned allocation - return mi_heap_malloc_zero_aligned_at_generic(heap, size, alignment, offset, zero); + return mi_theap_malloc_zero_aligned_at_generic(theap, size, alignment, offset, zero, usable); } // ------------------------------------------------------ -// Optimized mi_heap_malloc_aligned / mi_malloc_aligned +// Internal mi_theap_malloc_aligned / mi_malloc_aligned // ------------------------------------------------------ -mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, false); +static mi_decl_restrict void* mi_theap_malloc_aligned_at(mi_theap_t* theap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_malloc_zero_aligned_at(theap, size, alignment, offset, false, NULL); } -mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_malloc_aligned_at(heap, size, alignment, 0); +mi_decl_nodiscard mi_decl_restrict void* mi_theap_malloc_aligned(mi_theap_t* theap, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_malloc_aligned_at(theap, size, alignment, 0); } -// ensure a definition is emitted -#if defined(__cplusplus) -void* _mi_extern_heap_malloc_aligned = (void*)&mi_heap_malloc_aligned; -#endif - -// ------------------------------------------------------ -// Aligned Allocation -// ------------------------------------------------------ - -mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, true); +static mi_decl_restrict void* mi_theap_zalloc_aligned_at(mi_theap_t* theap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_malloc_zero_aligned_at(theap, size, alignment, offset, true, NULL); } -mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_zalloc_aligned_at(heap, size, alignment, 0); +static mi_decl_restrict void* mi_theap_zalloc_aligned(mi_theap_t* theap, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_zalloc_aligned_at(theap, size, alignment, 0); } -mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +static mi_decl_restrict void* mi_theap_calloc_aligned_at(mi_theap_t* theap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; - return mi_heap_zalloc_aligned_at(heap, total, alignment, offset); + return mi_theap_zalloc_aligned_at(theap, total, alignment, offset); } -mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_calloc_aligned_at(heap,count,size,alignment,0); +static mi_decl_restrict void* mi_theap_calloc_aligned(mi_theap_t* theap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_calloc_aligned_at(theap, count, size, alignment, 0); } + +// ------------------------------------------------------ +// Aligned Allocation +// ------------------------------------------------------ + mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_malloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset); + return mi_theap_malloc_aligned_at(_mi_theap_default(), size, alignment, offset); } mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_malloc_aligned(mi_prim_get_default_heap(), size, alignment); + return mi_theap_malloc_aligned(_mi_theap_default(), size, alignment); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_aligned(size_t size, size_t alignment, size_t* block_size) mi_attr_noexcept { + return mi_theap_malloc_zero_aligned_at(_mi_theap_default(), size, alignment, 0, false, block_size); } mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_zalloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset); + return mi_theap_zalloc_aligned_at(_mi_theap_default(), size, alignment, offset); } mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_zalloc_aligned(mi_prim_get_default_heap(), size, alignment); + return mi_theap_zalloc_aligned(_mi_theap_default(), size, alignment); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc_aligned(size_t size, size_t alignment, size_t* block_size) mi_attr_noexcept { + return mi_theap_malloc_zero_aligned_at(_mi_theap_default(), size, alignment, 0, true, block_size); } mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_calloc_aligned_at(mi_prim_get_default_heap(), count, size, alignment, offset); + return mi_theap_calloc_aligned_at(_mi_theap_default(), count, size, alignment, offset); } mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_calloc_aligned(mi_prim_get_default_heap(), count, size, alignment); + return mi_theap_calloc_aligned(_mi_theap_default(), count, size, alignment); +} + + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_malloc_aligned_at(_mi_heap_theap(heap), size, alignment, offset); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_malloc_aligned(_mi_heap_theap(heap), size, alignment); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_zalloc_aligned_at(_mi_heap_theap(heap), size, alignment, offset); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_zalloc_aligned(_mi_heap_theap(heap), size, alignment); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_calloc_aligned_at(_mi_heap_theap(heap), count, size, alignment, offset); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_calloc_aligned(_mi_heap_theap(heap), count, size, alignment); } @@ -227,18 +329,18 @@ mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t // Aligned re-allocation // ------------------------------------------------------ -static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset, bool zero) mi_attr_noexcept { +static void* mi_theap_realloc_zero_aligned_at(mi_theap_t* theap, void* p, size_t newsize, size_t alignment, size_t offset, bool zero) mi_attr_noexcept { mi_assert(alignment > 0); - if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero); - if (p == NULL) return mi_heap_malloc_zero_aligned_at(heap,newsize,alignment,offset,zero); + if (alignment <= sizeof(uintptr_t) && offset==0) return _mi_theap_realloc_zero(theap,p,newsize,zero,NULL,NULL); + if (p == NULL) return mi_theap_malloc_zero_aligned_at(theap,newsize,alignment,offset,zero,NULL); size_t size = mi_usable_size(p); if (newsize <= size && newsize >= (size - (size / 2)) && (((uintptr_t)p + offset) % alignment) == 0) { - return p; // reallocation still fits, is aligned and not more than 50% waste + return p; // reallocation still fits, is aligned and not more than 25% waste } else { // note: we don't zero allocate upfront so we only zero initialize the expanded part - void* newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset); + void* newp = mi_theap_malloc_aligned_at(theap,newsize,alignment,offset); if (newp != NULL) { if (zero && newsize > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized @@ -252,61 +354,88 @@ static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t ne } } -static void* mi_heap_realloc_zero_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, bool zero) mi_attr_noexcept { +static void* mi_theap_realloc_zero_aligned(mi_theap_t* theap, void* p, size_t newsize, size_t alignment, bool zero) mi_attr_noexcept { mi_assert(alignment > 0); - if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero); - size_t offset = ((uintptr_t)p % alignment); // use offset of previous allocation (p can be NULL) - return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,zero); + if (alignment <= sizeof(uintptr_t)) return _mi_theap_realloc_zero(theap,p,newsize,zero,NULL,NULL); + return mi_theap_realloc_zero_aligned_at(theap,p,newsize,alignment,0,zero); } -mi_decl_nodiscard void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,false); +static void* mi_theap_realloc_aligned_at(mi_theap_t* theap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_realloc_zero_aligned_at(theap,p,newsize,alignment,offset,false); } -mi_decl_nodiscard void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { - return mi_heap_realloc_zero_aligned(heap,p,newsize,alignment,false); +static void* mi_theap_realloc_aligned(mi_theap_t* theap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { + return mi_theap_realloc_zero_aligned(theap,p,newsize,alignment,false); } -mi_decl_nodiscard void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_realloc_zero_aligned_at(heap, p, newsize, alignment, offset, true); +static void* mi_theap_rezalloc_aligned_at(mi_theap_t* theap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_realloc_zero_aligned_at(theap, p, newsize, alignment, offset, true); } -mi_decl_nodiscard void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { - return mi_heap_realloc_zero_aligned(heap, p, newsize, alignment, true); +static void* mi_theap_rezalloc_aligned(mi_theap_t* theap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { + return mi_theap_realloc_zero_aligned(theap, p, newsize, alignment, true); } -mi_decl_nodiscard void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +static void* mi_theap_recalloc_aligned_at(mi_theap_t* theap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(newcount, size, &total)) return NULL; - return mi_heap_rezalloc_aligned_at(heap, p, total, alignment, offset); + return mi_theap_rezalloc_aligned_at(theap, p, total, alignment, offset); } -mi_decl_nodiscard void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { +static void* mi_theap_recalloc_aligned(mi_theap_t* theap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(newcount, size, &total)) return NULL; - return mi_heap_rezalloc_aligned(heap, p, total, alignment); + return mi_theap_rezalloc_aligned(theap, p, total, alignment); } + mi_decl_nodiscard void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_realloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset); + return mi_theap_realloc_aligned_at(_mi_theap_default(), p, newsize, alignment, offset); } mi_decl_nodiscard void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { - return mi_heap_realloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment); + return mi_theap_realloc_aligned(_mi_theap_default(), p, newsize, alignment); } mi_decl_nodiscard void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_rezalloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset); + return mi_theap_rezalloc_aligned_at(_mi_theap_default(), p, newsize, alignment, offset); } mi_decl_nodiscard void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { - return mi_heap_rezalloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment); + return mi_theap_rezalloc_aligned(_mi_theap_default(), p, newsize, alignment); } mi_decl_nodiscard void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_recalloc_aligned_at(mi_prim_get_default_heap(), p, newcount, size, alignment, offset); + return mi_theap_recalloc_aligned_at(_mi_theap_default(), p, newcount, size, alignment, offset); } mi_decl_nodiscard void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_recalloc_aligned(mi_prim_get_default_heap(), p, newcount, size, alignment); + return mi_theap_recalloc_aligned(_mi_theap_default(), p, newcount, size, alignment); +} + + +mi_decl_nodiscard void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_realloc_aligned_at(_mi_heap_theap(heap), p, newsize, alignment, offset); +} + +mi_decl_nodiscard void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { + return mi_theap_realloc_aligned(_mi_heap_theap(heap), p, newsize, alignment); +} + +mi_decl_nodiscard void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_rezalloc_aligned_at(_mi_heap_theap(heap), p, newsize, alignment, offset); } + +mi_decl_nodiscard void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { + return mi_theap_rezalloc_aligned(_mi_heap_theap(heap), p, newsize, alignment); +} + +mi_decl_nodiscard void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_theap_recalloc_aligned_at(_mi_heap_theap(heap), p, newcount, size, alignment, offset); +} + +mi_decl_nodiscard void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { + return mi_theap_recalloc_aligned(_mi_heap_theap(heap), p, newcount, size, alignment); +} + + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-override.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-override.c index 12837cdd..93d066dc 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-override.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-override.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2026, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -9,11 +9,8 @@ terms of the MIT license. A copy of the license can be found in the file #error "this file should be included from 'alloc.c' (so aliases can work)" #endif -#if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL)) -#error "It is only possible to override "malloc" on Windows when building as a DLL (and linking the C runtime as a DLL)" -#endif -#if defined(MI_MALLOC_OVERRIDE) && !(defined(_WIN32)) +#if defined(MI_MALLOC_OVERRIDE) && !defined(_DLL) #if defined(__APPLE__) #include @@ -51,7 +48,6 @@ typedef void* mi_nothrow_t; #define MI_FORWARD02(fun,x,y) { fun(x,y); } #endif - #if defined(__APPLE__) && defined(MI_SHARED_LIB_EXPORT) && defined(MI_OSX_INTERPOSE) // define MI_OSX_IS_INTERPOSED as we should not provide forwarding definitions for // functions that are interposed (or the interposing does not work) @@ -71,24 +67,20 @@ typedef void* mi_nothrow_t; #define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun } #define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun) - __attribute__((used)) static struct mi_interpose_s _mi_interposes[] __attribute__((section("__DATA, __interpose"))) = + #define MI_INTERPOSE_DECLS(name) __attribute__((used)) static struct mi_interpose_s name[] __attribute__((section("__DATA, __interpose"))) + + MI_INTERPOSE_DECLS(_mi_interposes) = { MI_INTERPOSE_MI(malloc), MI_INTERPOSE_MI(calloc), MI_INTERPOSE_MI(realloc), MI_INTERPOSE_MI(strdup), - #if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - MI_INTERPOSE_MI(strndup), - #endif MI_INTERPOSE_MI(realpath), MI_INTERPOSE_MI(posix_memalign), MI_INTERPOSE_MI(reallocf), MI_INTERPOSE_MI(valloc), MI_INTERPOSE_FUN(malloc_size,mi_malloc_size_checked), MI_INTERPOSE_MI(malloc_good_size), - #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 - MI_INTERPOSE_MI(aligned_alloc), - #endif #ifdef MI_OSX_ZONE // we interpose malloc_default_zone in alloc-override-osx.c so we can use mi_free safely MI_INTERPOSE_MI(free), @@ -99,6 +91,12 @@ typedef void* mi_nothrow_t; MI_INTERPOSE_FUN(vfree,mi_cfree), #endif }; + #if defined(MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) + MI_INTERPOSE_DECLS(_mi_interposes_10_7) = { MI_INTERPOSE_MI(strndup) }; + #endif + #if defined(MAC_OS_X_VERSION_10_15) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15) + MI_INTERPOSE_DECLS(_mi_interposes_10_15) = { MI_INTERPOSE_MI(aligned_alloc) }; + #endif #ifdef __cplusplus extern "C" { @@ -127,21 +125,103 @@ typedef void* mi_nothrow_t; }; #elif defined(_MSC_VER) - // cannot override malloc unless using a dll. - // we just override new/delete which does work in a static library. + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRT_HYBRIDPATCHABLE + void* __cdecl _expand(_Pre_notnull_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Size) { + return mi_expand(_Block, _Size); + } + _Check_return_ _ACRTIMP + size_t __cdecl _msize_base(_Pre_notnull_ void* _Block) _CRT_NOEXCEPT { + return mi_malloc_size(_Block); + } + _Check_return_ _ACRTIMP _CRT_HYBRIDPATCHABLE + size_t __cdecl _msize(_Pre_notnull_ void* _Block) { + return mi_malloc_size(_Block); + } + _ACRTIMP + void __cdecl _free_base(_Pre_maybenull_ _Post_invalid_ void* _Block) { + mi_free(_Block); + } + _ACRTIMP _CRT_HYBRIDPATCHABLE + void __cdecl free(_Pre_maybenull_ _Post_invalid_ void* _Block) { + mi_free(_Block); + } + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRT_JIT_INTRINSIC _CRTRESTRICT _CRT_HYBRIDPATCHABLE + void* __cdecl malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size) { + return mi_malloc(_Size); + } + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _malloc_base(_In_ size_t _Size) { + return mi_malloc(_Size); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _realloc_base(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ size_t _Size) { + return mi_realloc(_Block, _Size); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT _CRT_HYBRIDPATCHABLE + void* __cdecl realloc(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Size) { + return mi_realloc(_Block, _Size); + } + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count * _Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _calloc_base(_In_ size_t _Count, _In_ size_t _Size) { + return mi_calloc(_Count, _Size); + } + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count * _Size) _ACRTIMP _CRT_JIT_INTRINSIC _CRTALLOCATOR _CRTRESTRICT + void* __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size) { + return mi_calloc(_Count, _Size); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count * _Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _recalloc_base(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ size_t _Count, _In_ size_t _Size) { + return mi_recalloc(_Block, _Count, _Size); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count * _Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _recalloc(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size) { + return mi_recalloc(_Block, _Count, _Size); + } + _ACRTIMP + void __cdecl _aligned_free(_Pre_maybenull_ _Post_invalid_ void* _Block) { + mi_free(_Block); + } + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _aligned_malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment) { + return mi_malloc_aligned(_Size, _Alignment); + } + _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _aligned_offset_malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset) { + return mi_malloc_aligned_at(_Size, _Alignment, _Offset); + } + _Check_return_ _ACRTIMP + size_t __cdecl _aligned_msize(_Pre_notnull_ void* _Block, _In_ size_t _Alignment, _In_ size_t _Offset) { + MI_UNUSED(_Alignment); MI_UNUSED(_Offset); return mi_malloc_size(_Block); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _aligned_offset_realloc(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset) { + return mi_realloc_aligned_at(_Block, _Size, _Alignment, _Offset); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count * _Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _aligned_offset_recalloc(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset) { + return mi_recalloc_aligned_at(_Block, _Count, _Size, _Alignment, _Offset); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _aligned_realloc(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment) { + return mi_realloc_aligned(_Block, _Size, _Alignment); + } + _Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count * _Size) _ACRTIMP _CRTALLOCATOR _CRTRESTRICT + void* __cdecl _aligned_recalloc(_Pre_maybenull_ _Post_invalid_ void* _Block, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment) { + return mi_recalloc_aligned(_Block, _Count, _Size, _Alignment); + } #else // On all other systems forward allocation primitives to our API mi_decl_export void* malloc(size_t size) MI_FORWARD1(mi_malloc, size) mi_decl_export void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n) mi_decl_export void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize) - mi_decl_export void free(void* p) MI_FORWARD0(mi_free, p) + mi_decl_export void free(void* p) MI_FORWARD0(mi_free, p) // In principle we do not need to forward `strdup`/`strndup` but on some systems these do not use `malloc` internally (but a more primitive call) // We only override if `strdup` is not a macro (as on some older libc's, see issue #885) #if !defined(strdup) mi_decl_export char* strdup(const char* str) MI_FORWARD1(mi_strdup, str) #endif #if !defined(strndup) && (!defined(__APPLE__) || (defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)) - mi_decl_export char* strndup(const char* str, size_t n) MI_FORWARD2(mi_strndup, str, n) + mi_decl_export char* strndup(const char* str, size_t n) MI_FORWARD2(mi_strndup, str, n) #endif #endif @@ -202,18 +282,18 @@ typedef void* mi_nothrow_t; void _ZdaPv(void* p) MI_FORWARD0(mi_free,p) // delete[] void _ZdlPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n) void _ZdaPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n) - + void _ZdlPvSt11align_val_t(void* p, size_t al) { mi_free_aligned(p,al); } void _ZdaPvSt11align_val_t(void* p, size_t al) { mi_free_aligned(p,al); } void _ZdlPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); } void _ZdaPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); } - void _ZdlPvRKSt9nothrow_t(void* p, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free(p); } // operator delete(void*, std::nothrow_t const&) + void _ZdlPvRKSt9nothrow_t(void* p, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free(p); } // operator delete(void*, std::nothrow_t const&) void _ZdaPvRKSt9nothrow_t(void* p, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free(p); } // operator delete[](void*, std::nothrow_t const&) - void _ZdlPvSt11align_val_tRKSt9nothrow_t(void* p, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free_aligned(p,al); } // operator delete(void*, std::align_val_t, std::nothrow_t const&) - void _ZdaPvSt11align_val_tRKSt9nothrow_t(void* p, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free_aligned(p,al); } // operator delete[](void*, std::align_val_t, std::nothrow_t const&) - - #if (MI_INTPTR_SIZE==8) + void _ZdlPvSt11align_val_tRKSt9nothrow_t(void* p, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free_aligned(p,al); } // operator delete(void*, std::align_val_t, std::nothrow_t const&) + void _ZdaPvSt11align_val_tRKSt9nothrow_t(void* p, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); mi_free_aligned(p,al); } // operator delete[](void*, std::align_val_t, std::nothrow_t const&) + + #if (MI_INTPTR_SIZE==8) || (MI_INTPTR_SIZE==4 && defined(__EMSCRIPTEN__)) // pr #1257 void* _Znwm(size_t n) MI_FORWARD1(mi_new,n) // new 64-bit void* _Znam(size_t n) MI_FORWARD1(mi_new,n) // new[] 64-bit void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); } @@ -248,7 +328,7 @@ extern "C" { // Forward Posix/Unix calls as well void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize) size_t malloc_size(const void* p) MI_FORWARD1(mi_usable_size,p) - #if !defined(__ANDROID__) && !defined(__FreeBSD__) + #if !defined(__ANDROID__) && !defined(__FreeBSD__) && !defined(__DragonFly__) size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p) #else size_t malloc_usable_size(const void *p) MI_FORWARD1(mi_usable_size,p) @@ -276,7 +356,9 @@ extern "C" { void cfree(void* p) { mi_free(p); } void* pvalloc(size_t size) { return mi_pvalloc(size); } void* memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); } -void* _aligned_malloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); } +#if !defined(_WIN32) +void* _aligned_malloc(size_t size, size_t alignment) { return mi_malloc_aligned(size,alignment); } +#endif void* reallocarray(void* p, size_t count, size_t size) { return mi_reallocarray(p, count, size); } // some systems define reallocarr so mark it as a weak symbol (#751) mi_decl_weak int reallocarr(void* p, size_t count, size_t size) { return mi_reallocarr(p, count, size); } @@ -289,8 +371,8 @@ mi_decl_weak int reallocarr(void* p, size_t count, size_t size) { return mi_r void __libc_free(void* p) MI_FORWARD0(mi_free, p) void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); } -#elif defined(__GLIBC__) && defined(__linux__) - // forward __libc interface (needed for glibc-based Linux distributions) +#elif defined(__linux__) + // forward __libc interface (needed for glibc-based and musl-based Linux distributions) void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc,size) void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc,count,size) void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc,p,size) @@ -311,4 +393,4 @@ mi_decl_weak int reallocarr(void* p, size_t count, size_t size) { return mi_r #pragma GCC visibility pop #endif -#endif // MI_MALLOC_OVERRIDE && !_WIN32 +#endif // MI_MALLOC_OVERRIDE diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-posix.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-posix.c index 225752fd..d4cf31df 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-posix.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc-posix.c @@ -33,12 +33,12 @@ terms of the MIT license. A copy of the license can be found in the file mi_decl_nodiscard size_t mi_malloc_size(const void* p) mi_attr_noexcept { - // if (!mi_is_in_heap_region(p)) return 0; + if (!mi_is_in_heap_region(p)) return 0; return mi_usable_size(p); } mi_decl_nodiscard size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept { - // if (!mi_is_in_heap_region(p)) return 0; + if (!mi_is_in_heap_region(p)) return 0; return mi_usable_size(p); } @@ -52,7 +52,7 @@ void mi_cfree(void* p) mi_attr_noexcept { } } -int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept { +int mi_posix_memalign(void** p, size_t alignment, size_t size) { // mi_attr_noexcept (issue #794) // Note: The spec dictates we should not modify `*p` on an error. (issue#27) // if (p == NULL) return EINVAL; @@ -61,14 +61,14 @@ int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept if (alignment==0 || !_mi_is_power_of_two(alignment)) return EINVAL; // not a power of 2 void* q = mi_malloc_aligned(size, alignment); if (q==NULL && size != 0) return ENOMEM; - mi_assert_internal(((uintptr_t)q % alignment) == 0); + mi_assert_internal(_mi_is_aligned(q,alignment)); *p = q; return 0; } mi_decl_nodiscard mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept { void* p = mi_malloc_aligned(size, alignment); - mi_assert_internal(((uintptr_t)p % alignment) == 0); + mi_assert_internal(_mi_is_aligned(p,alignment)); return p; } @@ -95,27 +95,43 @@ mi_decl_nodiscard mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size */ // C11 also requires alignment to be a power-of-two (and > 0) which is checked in mi_malloc_aligned void* p = mi_malloc_aligned(size, alignment); - mi_assert_internal(((uintptr_t)p % alignment) == 0); + mi_assert_internal(_mi_is_aligned(p,alignment)); return p; } -mi_decl_nodiscard void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept { // BSD - void* newp = mi_reallocn(p,count,size); +mi_decl_nodiscard void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept { // BSD + size_t total; + if mi_unlikely(mi_count_size_overflow(count, size, &total)) { + errno = EOVERFLOW; + return NULL; + } + void* newp = mi_realloc(p,total); if (newp==NULL) { errno = ENOMEM; } return newp; } -mi_decl_nodiscard int mi_reallocarr( void* p, size_t count, size_t size ) mi_attr_noexcept { // NetBSD - mi_assert(p != NULL); - if (p == NULL) { - errno = EINVAL; - return EINVAL; +mi_decl_nodiscard int mi_reallocarr( void* ptrp, size_t count, size_t size ) mi_attr_noexcept { // NetBSD + mi_assert(size != 0); + mi_assert(ptrp != NULL); + if (ptrp == NULL || size == 0) { + return (errno = EINVAL); + } + size_t total; + if mi_unlikely(mi_count_size_overflow(count, size, &total)) { + return (errno = EOVERFLOW); + } + void** op = (void**)ptrp; + if (total == 0) { + free(*op); + *op = NULL; + return 0; + } + else { + void* newp = mi_realloc(*op,total); + if (newp == NULL) { return (errno = ENOMEM); } + *op = newp; + return 0; } - void** op = (void**)p; - void* newp = mi_reallocarray(*op, count, size); - if mi_unlikely(newp == NULL) { return errno; } - *op = newp; - return 0; } void* mi__expand(void* p, size_t newsize) mi_attr_noexcept { // Microsoft @@ -124,12 +140,13 @@ void* mi__expand(void* p, size_t newsize) mi_attr_noexcept { // Microsoft return res; } -mi_decl_nodiscard mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict wchar_t* mi_wcsdup(const wchar_t* s) mi_attr_noexcept { if (s==NULL) return NULL; - size_t len; - for(len = 0; s[len] != 0; len++) { } - size_t size = (len+1)*sizeof(unsigned short); - unsigned short* p = (unsigned short*)mi_malloc(size); + size_t wlen; + for(wlen = 0; s[wlen] != 0 && wlen < PTRDIFF_MAX; wlen++) { } // prevent overflow on wlen+1 + size_t size; + if (mi_mul_overflow(wlen+1, sizeof(wchar_t), &size) || size > PTRDIFF_MAX) return NULL; + wchar_t* p = (wchar_t*)mi_malloc(size); if (p != NULL) { _mi_memcpy(p,s,size); } @@ -141,36 +158,36 @@ mi_decl_nodiscard mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* } int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept { - if (buf==NULL || name==NULL) return EINVAL; if (size != NULL) *size = 0; - char* p = getenv(name); // mscver warning 4996 + if (buf==NULL || name==NULL) return EINVAL; + char* p = getenv(name); if (p==NULL) { *buf = NULL; } else { *buf = mi_strdup(p); if (*buf==NULL) return ENOMEM; - if (size != NULL) *size = _mi_strlen(p); + if (size != NULL) { *size = _mi_strlen(p) + 1; } // cannot overflow as mi_strdup is limited to PTRDIFF_MAX } return 0; } -int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept { - if (buf==NULL || name==NULL) return EINVAL; +int mi_wdupenv_s(wchar_t** buf, size_t* size, const wchar_t* name) mi_attr_noexcept { if (size != NULL) *size = 0; + if (buf==NULL || name==NULL) return EINVAL; #if !defined(_WIN32) || (defined(WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)) // not supported *buf = NULL; return EINVAL; #else - unsigned short* p = (unsigned short*)_wgetenv((const wchar_t*)name); // msvc warning 4996 + wchar_t* p = (wchar_t*)_wgetenv(name); if (p==NULL) { *buf = NULL; } else { *buf = mi_wcsdup(p); if (*buf==NULL) return ENOMEM; - if (size != NULL) *size = wcslen((const wchar_t*)p); + if (size != NULL) { *size = wcslen(p) + 1; } // cannot overflow as wcsdup is limited to PTRDIFF_MAX } return 0; #endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc.c index 86aaae75..46e51c89 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/alloc.c @@ -1,5 +1,6 @@ + /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2024, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -28,20 +29,30 @@ terms of the MIT license. A copy of the license can be found in the file // Fast allocation in a page: just pop from the free list. // Fall back to generic allocation only if the list is empty. // Note: in release mode the (inlined) routine is about 7 instructions with a single test. -extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept +static mi_decl_forceinline void* mi_page_malloc_zero(mi_theap_t* theap, mi_page_t* page, size_t size, bool zero, size_t* usable) mi_attr_noexcept { - mi_assert_internal(page->block_size == 0 /* empty heap */ || mi_page_block_size(page) >= size); + if (page->block_size != 0) { // not the empty theap + mi_assert_internal(mi_page_block_size(page) >= size); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + } + + // check the free list mi_block_t* const block = page->free; if mi_unlikely(block == NULL) { - return _mi_malloc_generic(heap, size, zero, 0); + return _mi_malloc_generic(theap, size, (zero ? 1 : 0), usable); } mi_assert_internal(block != NULL && _mi_ptr_page(block) == page); + if (usable != NULL) { *usable = mi_page_usable_block_size(page); }; + // pop from the free list page->free = mi_block_next(page, block); page->used++; mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page); + mi_assert_internal(page->block_size < MI_MAX_ALIGN_SIZE || _mi_is_aligned(block, MI_MAX_ALIGN_SIZE)); + #if MI_DEBUG>3 - if (page->free_is_zero) { + if (page->free_is_zero && size > sizeof(*block)) { mi_assert_expensive(mi_mem_is_zero(block+1,size - sizeof(*block))); } #endif @@ -49,49 +60,48 @@ extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_ // allow use of the block internally // note: when tracking we need to avoid ever touching the MI_PADDING since // that is tracked by valgrind etc. as non-accessible (through the red-zone, see `mimalloc/track.h`) - mi_track_mem_undefined(block, mi_page_usable_block_size(page)); - - // zero the block? note: we need to zero the full block size (issue #63) - if mi_unlikely(zero) { - mi_assert_internal(page->block_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic) - mi_assert_internal(page->block_size >= MI_PADDING_SIZE); - if (page->free_is_zero) { - block->next = 0; - mi_track_mem_defined(block, page->block_size - MI_PADDING_SIZE); - } - else { - _mi_memzero_aligned(block, page->block_size - MI_PADDING_SIZE); - } - } - - #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN - if (!zero && !mi_page_is_huge(page)) { - memset(block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page)); - } - #elif (MI_SECURE!=0) - if (!zero) { block->next = 0; } // don't leak internal data - #endif + const size_t bsize = mi_page_usable_block_size(page); + mi_track_mem_undefined(block, bsize); #if (MI_STAT>0) - const size_t bsize = mi_page_usable_block_size(page); - if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) { - mi_heap_stat_increase(heap, normal, bsize); - mi_heap_stat_counter_increase(heap, normal_count, 1); + if (bsize <= MI_LARGE_MAX_OBJ_SIZE) { + mi_theap_stat_increase(theap, malloc_normal, bsize); #if (MI_STAT>1) + mi_theap_stat_counter_increase(theap, malloc_normal_count, 1); const size_t bin = _mi_bin(bsize); - mi_heap_stat_increase(heap, normal_bins[bin], 1); + mi_theap_stat_increase(theap, malloc_bins[bin], 1); + mi_theap_stat_increase(theap, malloc_requested, size - MI_PADDING_SIZE); #endif } #endif + // zero the block? note: we need to zero the full block size (issue #63) + if mi_likely(!zero) { + // #if MI_SECURE + block->next = 0; // don't leak internal data + // #endif + #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN + if (!mi_page_is_huge(page)) { memset(block, MI_DEBUG_UNINIT, bsize); } + #endif + } + else { + if (!page->free_is_zero) { + _mi_memzero_aligned(block, bsize); + } + else { + block->next = 0; + mi_track_mem_defined(block, bsize); + } + } + #if MI_PADDING // && !MI_TRACK_ENABLED - mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page)); + mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + bsize); ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE)); #if (MI_DEBUG>=2) - mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta)); + mi_assert_internal(delta >= 0 && bsize >= (size - MI_PADDING_SIZE + delta)); #endif mi_track_mem_defined(padding,sizeof(mi_padding_t)); // note: re-enable since mi_page_usable_block_size may set noaccess - padding->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys)); + padding->canary = mi_ptr_encode_canary(page,block,page->keys); padding->delta = (uint32_t)(delta); #if MI_PADDING_CHECK if (!mi_page_is_huge(page)) { @@ -105,35 +115,67 @@ extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_ return block; } -// extra entries for improved efficiency in `alloc-aligned.c`. -extern void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept { - return _mi_page_malloc_zero(heap,page,size,false); -} -extern void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept { - return _mi_page_malloc_zero(heap,page,size,true); +// extra entries for improved efficiency in `alloc-aligned.c` (and in `page.c:mi_malloc_generic`. +extern void* _mi_page_malloc_zero(mi_theap_t* theap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept { + return mi_page_malloc_zero(theap, page, size, zero, NULL); } -static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { - mi_assert(heap != NULL); +#if MI_GUARDED +mi_decl_restrict void* _mi_theap_malloc_guarded(mi_theap_t* theap, size_t size, bool zero) mi_attr_noexcept; +#endif + +// main allocation primitives for small and generic allocation + +// internal small size allocation +static mi_decl_forceinline mi_decl_restrict void* mi_theap_malloc_small_zero_nonnull(mi_theap_t* theap, size_t size, bool zero, size_t* usable) mi_attr_noexcept +{ + mi_assert(theap != NULL); + mi_assert(size <= MI_SMALL_SIZE_MAX); #if MI_DEBUG const uintptr_t tid = _mi_thread_id(); - mi_assert(heap->thread_id == 0 || heap->thread_id == tid); // heaps are thread local + mi_assert(theap->tld->thread_id == 0 || theap->tld->thread_id == tid); // theaps are thread local #endif - mi_assert(size <= MI_SMALL_SIZE_MAX); - #if (MI_PADDING) - if (size == 0) { size = sizeof(void*); } + #if (MI_PADDING || MI_GUARDED) + if mi_unlikely(size == 0) { size = sizeof(void*); } + #endif + #if MI_GUARDED + if mi_unlikely(mi_theap_malloc_use_guarded(theap,size)) { + return _mi_theap_malloc_guarded(theap, size, zero); + } #endif - mi_page_t* page = _mi_heap_get_free_small_page(heap, size + MI_PADDING_SIZE); - void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero); + // get page in constant time, and allocate from it + mi_page_t* page = _mi_theap_get_free_small_page(theap, size + MI_PADDING_SIZE); + void* const p = mi_page_malloc_zero(theap, page, size + MI_PADDING_SIZE, zero, usable); mi_track_malloc(p,size,zero); - #if MI_STAT>1 - if (p != NULL) { - if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); } - mi_heap_stat_increase(heap, malloc, mi_usable_size(p)); + #if MI_DEBUG>3 + if (p != NULL && zero) { + mi_assert_expensive(mi_mem_is_zero(p, size)); } #endif + return p; +} + +// internal generic allocation +static mi_decl_forceinline void* mi_theap_malloc_generic(mi_theap_t* theap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept +{ + #if MI_GUARDED + #if MI_THEAP_INITASNULL + if (theap!=NULL) + #endif + if (huge_alignment==0 && mi_theap_malloc_use_guarded(theap, size)) { + return _mi_theap_malloc_guarded(theap, size, zero); + } + #endif + #if !MI_THEAP_INITASNULL + mi_assert(theap!=NULL); + #endif + mi_assert(theap==NULL || theap->tld->thread_id == 0 || theap->tld->thread_id == _mi_thread_id()); // theaps are thread local + mi_assert((huge_alignment & 1)==0); + void* const p = _mi_malloc_generic(theap, size + MI_PADDING_SIZE, (zero ? 1 : 0) | huge_alignment, usable); // note: size can overflow but it is detected in malloc_generic + mi_track_malloc(p, size, zero); + #if MI_DEBUG>3 if (p != NULL && zero) { mi_assert_expensive(mi_mem_is_zero(p, size)); @@ -142,88 +184,165 @@ static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, return p; } +// internal small allocation +static mi_decl_forceinline mi_decl_restrict void* mi_theap_malloc_small_zero(mi_theap_t* theap, size_t size, bool zero, size_t* usable) mi_attr_noexcept { + #if MI_THEAP_INITASNULL + if (theap!=NULL) { + return mi_theap_malloc_small_zero_nonnull(theap, size, zero, usable); + } + else { + return mi_theap_malloc_generic(theap, size, zero, 0, usable); // tailcall + } + #else + return mi_theap_malloc_small_zero_nonnull(theap, size, zero, usable); + #endif +} + + // allocate a small block -mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { - return mi_heap_malloc_small_zero(heap, size, false); +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_theap_malloc_small(mi_theap_t* theap, size_t size) mi_attr_noexcept { + return mi_theap_malloc_small_zero(theap, size, false, NULL); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { + return mi_theap_malloc_small(_mi_theap_default(), size); } -mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { - return mi_heap_malloc_small(mi_prim_get_default_heap(), size); +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return mi_theap_malloc_small_zero_nonnull(_mi_heap_theap(heap), size, false, NULL); } -// The main allocation function -extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept { +// The main internal allocation functions +static mi_decl_forceinline void* mi_theap_malloc_zero_nonnull(mi_theap_t* theap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept { + // fast path for small objects if mi_likely(size <= MI_SMALL_SIZE_MAX) { mi_assert_internal(huge_alignment == 0); - return mi_heap_malloc_small_zero(heap, size, zero); + return mi_theap_malloc_small_zero_nonnull(theap, size, zero, usable); } else { - mi_assert(heap!=NULL); - mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local - void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment); // note: size can overflow but it is detected in malloc_generic - mi_track_malloc(p,size,zero); - #if MI_STAT>1 - if (p != NULL) { - if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); } - mi_heap_stat_increase(heap, malloc, mi_usable_size(p)); - } - #endif - #if MI_DEBUG>3 - if (p != NULL && zero) { - mi_assert_expensive(mi_mem_is_zero(p, size)); - } - #endif - return p; + return mi_theap_malloc_generic(theap, size, zero, huge_alignment, usable); } } -extern inline void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { - return _mi_heap_malloc_zero_ex(heap, size, zero, 0); +extern mi_decl_forceinline void* _mi_theap_malloc_zero_ex(mi_theap_t* theap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept { + // fast path for small objects + #if MI_THEAP_INITASNULL + if mi_likely(theap!=NULL && size <= MI_SMALL_SIZE_MAX) + #else + if mi_likely(size <= MI_SMALL_SIZE_MAX) + #endif + { + mi_assert_internal(huge_alignment == 0); + return mi_theap_malloc_small_zero_nonnull(theap, size, zero, usable); + } + else { + return mi_theap_malloc_generic(theap, size, zero, huge_alignment, usable); + } +} + +void* _mi_theap_malloc_zero(mi_theap_t* theap, size_t size, bool zero, size_t* usable) mi_attr_noexcept { + return _mi_theap_malloc_zero_ex(theap, size, zero, 0, usable); } -mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { - return _mi_heap_malloc_zero(heap, size, false); + +// Main allocation functions + +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_theap_malloc(mi_theap_t* theap, size_t size) mi_attr_noexcept { + return _mi_theap_malloc_zero(theap, size, false, NULL); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { + return mi_theap_malloc(_mi_theap_default(), size); } -mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { - return mi_heap_malloc(mi_prim_get_default_heap(), size); +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return mi_theap_malloc_zero_nonnull(_mi_heap_theap(heap), size, false, 0, NULL); } + // zero initialized small block mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept { - return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true); + return mi_theap_malloc_small_zero(_mi_theap_default(), size, true, NULL); } -mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { - return _mi_heap_malloc_zero(heap, size, true); +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_theap_zalloc_small(mi_theap_t* theap, size_t size) mi_attr_noexcept { + return mi_theap_malloc_small_zero(theap, size, true, NULL); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return mi_theap_malloc_small_zero_nonnull(_mi_heap_theap(heap), size, true, NULL); +} + + +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_theap_zalloc(mi_theap_t* theap, size_t size) mi_attr_noexcept { + return _mi_theap_malloc_zero(theap, size, true, NULL); } mi_decl_nodiscard mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept { - return mi_heap_zalloc(mi_prim_get_default_heap(),size); + return _mi_theap_malloc_zero(_mi_theap_default(), size, true, NULL); } +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return mi_theap_malloc_zero_nonnull(_mi_heap_theap(heap), size, true, 0, NULL); +} -mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_theap_calloc(mi_theap_t* theap, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count,size,&total)) return NULL; - return mi_heap_zalloc(heap,total); + return mi_theap_zalloc(theap,total); } mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept { - return mi_heap_calloc(mi_prim_get_default_heap(),count,size); + return mi_theap_calloc(_mi_theap_default(),count,size); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { + size_t total; + if (mi_count_size_overflow(count, size, &total)) return NULL; + return mi_heap_zalloc(heap, total); +} + +// Return usable size +mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_small(size_t size, size_t* usable) mi_attr_noexcept { + return mi_theap_malloc_small_zero(_mi_theap_default(), size, false, usable); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_theap_umalloc(mi_theap_t* theap, size_t size, size_t* usable) mi_attr_noexcept { + return _mi_theap_malloc_zero_ex(theap, size, false, 0, usable); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_umalloc(size_t size, size_t* usable) mi_attr_noexcept { + return mi_theap_umalloc(_mi_theap_default(), size, usable); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc(size_t size, size_t* usable) mi_attr_noexcept { + return _mi_theap_malloc_zero_ex(_mi_theap_default(), size, true, 0, usable); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_ucalloc(size_t count, size_t size, size_t* usable) mi_attr_noexcept { + size_t total; + if (mi_count_size_overflow(count,size,&total)) return NULL; + return mi_uzalloc(total, usable); } // Uninitialized `calloc` -mi_decl_nodiscard extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { +static mi_decl_restrict void* mi_theap_mallocn(mi_theap_t* theap, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; - return mi_heap_malloc(heap, total); + return mi_theap_malloc(theap, total); } mi_decl_nodiscard mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept { - return mi_heap_mallocn(mi_prim_get_default_heap(),count,size); + return mi_theap_mallocn(_mi_theap_default(),count,size); } +mi_decl_nodiscard mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { + size_t total; + if (mi_count_size_overflow(count, size, &total)) return NULL; + return mi_heap_malloc(heap, total); +} + + // Expand (or shrink) in place (or fail) void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { #if MI_PADDING @@ -232,25 +351,48 @@ void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { return NULL; #else if (p == NULL) return NULL; - const size_t size = _mi_usable_size(p,"mi_expand"); + const mi_page_t* const page = mi_validate_ptr_page(p,"mi_expand"); + const size_t size = _mi_usable_size(p,page); if (newsize > size) return NULL; return p; // it fits #endif } -void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept { +void* _mi_theap_realloc_zero(mi_theap_t* theap, void* p, size_t newsize, bool zero, size_t* usable_pre, size_t* usable_post) mi_attr_noexcept { // if p == NULL then behave as malloc. // else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)). // (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.) - const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL (with size 0) - if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) - mi_assert_internal(p!=NULL); - // todo: do not track as the usable size is still the same in the free; adjust potential padding? - // mi_track_resize(p,size,newsize) - // if (newsize < size) { mi_track_mem_noaccess((uint8_t*)p + newsize, size - newsize); } - return p; // reallocation still fits and not more than 50% waste - } - void* newp = mi_heap_malloc(heap,newsize); + const mi_page_t* page; + size_t size; + if (p==NULL) { + page = NULL; + size = 0; + if (usable_pre!=NULL) { *usable_pre = 0; } + } + else { + page = mi_validate_ptr_page(p,"mi_realloc"); + size = _mi_usable_size(p,page); + if (usable_pre!=NULL) { *usable_pre = mi_page_usable_block_size(page); } + } + // check if we can reuse the existing block + if mi_unlikely(newsize<=size && newsize>=(size/2) && newsize>0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) + mi_assert_internal(page!=NULL); // note: page!=NULL (since if p==NULL, we have size=0 and size>=newsize>0 + #if MI_THEAP_INITASNULL + if (theap!=NULL) + #endif + { + if (mi_page_heap(page)==_mi_theap_heap(theap)) { // and within the same heap + mi_assert_internal(p!=NULL); + // todo: do not track as the usable size is still the same in the free; adjust potential padding? + // mi_track_resize(p,size,newsize) + // if (newsize < size) { mi_track_mem_noaccess((uint8_t*)p + newsize, size - newsize); } + if (usable_post!=NULL) { *usable_post = mi_page_usable_block_size(page); } + return p; // reallocation still fits and not more than 50% waste + } + } + } + // otherwise allocate a fresh block + void* newp = mi_theap_umalloc(theap,newsize,usable_post); if mi_likely(newp != NULL) { if (zero && newsize > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized @@ -264,60 +406,86 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) const size_t copysize = (newsize > size ? size : newsize); mi_track_mem_defined(p,copysize); // _mi_useable_size may be too large for byte precise memory tracking.. _mi_memcpy(newp, p, copysize); - mi_free(p); // only free the original pointer if successful + mi_free(p); // only free the original pointer if successful // todo: optimize since page is known? } } return newp; } -mi_decl_nodiscard void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { - return _mi_heap_realloc_zero(heap, p, newsize, false); +mi_decl_nodiscard void* mi_theap_realloc(mi_theap_t* theap, void* p, size_t newsize) mi_attr_noexcept { + return _mi_theap_realloc_zero(theap, p, newsize, false, NULL, NULL); } -mi_decl_nodiscard void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { +static void* mi_theap_reallocn(mi_theap_t* theap, void* p, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; - return mi_heap_realloc(heap, p, total); + return mi_theap_realloc(theap, p, total); } // Reallocate but free `p` on errors -mi_decl_nodiscard void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { - void* newp = mi_heap_realloc(heap, p, newsize); +static void* mi_theap_reallocf(mi_theap_t* theap, void* p, size_t newsize) mi_attr_noexcept { + void* newp = mi_theap_realloc(theap, p, newsize); if (newp==NULL && p!=NULL) mi_free(p); return newp; } -mi_decl_nodiscard void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { - return _mi_heap_realloc_zero(heap, p, newsize, true); +static void* mi_theap_rezalloc(mi_theap_t* theap, void* p, size_t newsize) mi_attr_noexcept { + return _mi_theap_realloc_zero(theap, p, newsize, true, NULL, NULL); } -mi_decl_nodiscard void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { +static void* mi_theap_recalloc(mi_theap_t* theap, void* p, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; - return mi_heap_rezalloc(heap, p, total); + return mi_theap_rezalloc(theap, p, total); } mi_decl_nodiscard void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept { - return mi_heap_realloc(mi_prim_get_default_heap(),p,newsize); + return mi_theap_realloc(_mi_theap_default(),p,newsize); } mi_decl_nodiscard void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { - return mi_heap_reallocn(mi_prim_get_default_heap(),p,count,size); + return mi_theap_reallocn(_mi_theap_default(),p,count,size); +} + +mi_decl_nodiscard void* mi_urealloc(void* p, size_t newsize, size_t* usable_pre, size_t* usable_post) mi_attr_noexcept { + return _mi_theap_realloc_zero(_mi_theap_default(),p,newsize, false, usable_pre, usable_post); } // Reallocate but free `p` on errors mi_decl_nodiscard void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept { - return mi_heap_reallocf(mi_prim_get_default_heap(),p,newsize); + return mi_theap_reallocf(_mi_theap_default(),p,newsize); } mi_decl_nodiscard void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept { - return mi_heap_rezalloc(mi_prim_get_default_heap(), p, newsize); + return mi_theap_rezalloc(_mi_theap_default(), p, newsize); } mi_decl_nodiscard void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { - return mi_heap_recalloc(mi_prim_get_default_heap(), p, count, size); + return mi_theap_recalloc(_mi_theap_default(), p, count, size); +} + + +mi_decl_nodiscard void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { + return mi_theap_realloc(_mi_heap_theap(heap), p, newsize); +} + +mi_decl_nodiscard void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { + return mi_theap_reallocn(_mi_heap_theap(heap), p, count, size); +} + +// Reallocate but free `p` on errors +mi_decl_nodiscard void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { + return mi_theap_reallocf(_mi_heap_theap(heap), p, newsize); +} + +mi_decl_nodiscard void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { + return mi_theap_rezalloc(_mi_heap_theap(heap), p, newsize); +} + +mi_decl_nodiscard void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { + return mi_theap_recalloc(_mi_heap_theap(heap), p, count, size); } @@ -327,10 +495,11 @@ mi_decl_nodiscard void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_ // ------------------------------------------------------ // `strdup` using mi_malloc -mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { +mi_decl_nodiscard static mi_decl_restrict char* mi_theap_strdup(mi_theap_t* theap, const char* s) mi_attr_noexcept { if (s == NULL) return NULL; size_t len = _mi_strlen(s); - char* t = (char*)mi_heap_malloc(heap,len+1); + if (len > MI_MAX_ALLOC_SIZE - 1) return NULL; // prevent overflow on len+1 + char* t = (char*)mi_theap_malloc(theap,len+1); if (t == NULL) return NULL; _mi_memcpy(t, s, len); t[len] = 0; @@ -338,14 +507,19 @@ mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const c } mi_decl_nodiscard mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept { - return mi_heap_strdup(mi_prim_get_default_heap(), s); + return mi_theap_strdup(_mi_theap_default(), s); +} + +mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { + return mi_theap_strdup(_mi_heap_theap(heap), s); } // `strndup` using mi_malloc -mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { +mi_decl_nodiscard static mi_decl_restrict char* mi_theap_strndup(mi_theap_t* theap, const char* s, size_t n) mi_attr_noexcept { if (s == NULL) return NULL; const size_t len = _mi_strnlen(s,n); // len <= n - char* t = (char*)mi_heap_malloc(heap, len+1); + if (len > MI_MAX_ALLOC_SIZE - 1) return NULL; // prevent overflow on len+1 + char* t = (char*)mi_theap_malloc(theap, len+1); if (t == NULL) return NULL; _mi_memcpy(t, s, len); t[len] = 0; @@ -353,7 +527,11 @@ mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const } mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { - return mi_heap_strndup(mi_prim_get_default_heap(),s,n); + return mi_theap_strndup(_mi_theap_default(),s,n); +} + +mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { + return mi_theap_strndup(_mi_heap_theap(heap), s, n); } #ifndef __wasi__ @@ -362,8 +540,8 @@ mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_ #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif -#include -mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { + +mi_decl_nodiscard static mi_decl_restrict char* mi_theap_realpath(mi_theap_t* theap, const char* fname, char* resolved_name) mi_attr_noexcept { // todo: use GetFullPathNameW to allow longer file names char buf[PATH_MAX]; DWORD res = GetFullPathNameA(fname, PATH_MAX, (resolved_name == NULL ? buf : resolved_name), NULL); @@ -377,53 +555,65 @@ mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const return resolved_name; } else { - return mi_heap_strndup(heap, buf, PATH_MAX); + return mi_theap_strndup(theap, buf, PATH_MAX); } } + #else -/* + #include // pathconf + static size_t mi_path_max(void) { - static size_t path_max = 0; - if (path_max <= 0) { - long m = pathconf("/",_PC_PATH_MAX); - if (m <= 0) path_max = 4096; // guess - else if (m < 256) path_max = 256; // at least 256 - else path_max = m; + static _Atomic(size_t) path_max = 0; + size_t pmax = mi_atomic_load_acquire(&path_max); + if (pmax == 0) { + long m = 0; + #ifdef _PC_PATH_MAX + m = pathconf("/",_PC_PATH_MAX); + #endif + if (m <= 0) pmax = 4096; // guess + else if (m < 256) pmax = 256; // at least 256 + else if (m > 64*1024) pmax = 64*1024; // at most 64 KiB + else pmax = m; + size_t expected = 0; + mi_atomic_cas_strong_acq_rel(&path_max, &expected, pmax); } - return path_max; + return pmax; } -*/ -char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { + +char* mi_theap_realpath(mi_theap_t* theap, const char* fname, char* resolved_name) mi_attr_noexcept { if (resolved_name != NULL) { return realpath(fname,resolved_name); } else { + /* char* rname = realpath(fname, NULL); if (rname == NULL) return NULL; char* result = mi_heap_strdup(heap, rname); - mi_cfree(rname); // use checked free (which may be redirected to our free but that's ok) + mi_cfree(rname); // note: may leak the original pointer if allocated internally with the system allocator // note: with ASAN realpath is intercepted and mi_cfree may leak the returned pointer :-( - return result; - } - /* + return result; + */ const size_t n = mi_path_max(); - char* buf = (char*)mi_malloc(n+1); + char* const buf = (char*)mi_zalloc(n+1); if (buf == NULL) { errno = ENOMEM; return NULL; } char* rname = realpath(fname,buf); - char* result = mi_heap_strndup(heap,rname,n); // ok if `rname==NULL` + char* result = mi_theap_strndup(theap,rname,n); // ok if `rname==NULL` mi_free(buf); return result; } - */ } #endif mi_decl_nodiscard mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept { - return mi_heap_realpath(mi_prim_get_default_heap(),fname,resolved_name); + return mi_theap_realpath(_mi_theap_default(),fname,resolved_name); +} + +mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { + return mi_theap_realpath(_mi_heap_theap(heap), fname, resolved_name); } #endif @@ -473,7 +663,7 @@ static std_new_handler_t mi_get_new_handler(void) { } #else // note: on windows we could dynamically link to `?get_new_handler@std@@YAP6AXXZXZ`. -static std_new_handler_t mi_get_new_handler() { +static std_new_handler_t mi_get_new_handler(void) { return NULL; } #endif @@ -494,43 +684,57 @@ static bool mi_try_new_handler(bool nothrow) { } #endif -mi_decl_export mi_decl_noinline void* mi_heap_try_new(mi_heap_t* heap, size_t size, bool nothrow ) { +static mi_decl_noinline void* mi_theap_try_new(mi_theap_t* theap, size_t size, bool nothrow ) { void* p = NULL; while(p == NULL && mi_try_new_handler(nothrow)) { - p = mi_heap_malloc(heap,size); + p = mi_theap_malloc(theap,size); } return p; } static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow) { - return mi_heap_try_new(mi_prim_get_default_heap(), size, nothrow); + return mi_theap_try_new(_mi_theap_default(), size, nothrow); } +static mi_decl_noinline void* mi_heap_try_new(mi_heap_t* heap, size_t size, bool nothrow) { + return mi_theap_try_new(_mi_heap_theap(heap), size, nothrow); +} -mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) { - void* p = mi_heap_malloc(heap,size); - if mi_unlikely(p == NULL) return mi_heap_try_new(heap, size, false); + +mi_decl_nodiscard static mi_decl_restrict void* mi_theap_alloc_new(mi_theap_t* theap, size_t size) { + void* p = mi_theap_malloc(theap,size); + if mi_unlikely(p == NULL) return mi_theap_try_new(theap, size, false); return p; } mi_decl_nodiscard mi_decl_restrict void* mi_new(size_t size) { - return mi_heap_alloc_new(mi_prim_get_default_heap(), size); + return mi_theap_alloc_new(_mi_theap_default(), size); } +mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) { + void* p = mi_heap_malloc(heap, size); + if mi_unlikely(p == NULL) return mi_heap_try_new(heap, size, false); + return p; +} -mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) { + +mi_decl_nodiscard static mi_decl_restrict void* mi_theap_alloc_new_n(mi_theap_t* theap, size_t count, size_t size) { size_t total; if mi_unlikely(mi_count_size_overflow(count, size, &total)) { mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc return NULL; } else { - return mi_heap_alloc_new(heap,total); + return mi_theap_alloc_new(theap,total); } } mi_decl_nodiscard mi_decl_restrict void* mi_new_n(size_t count, size_t size) { - return mi_heap_alloc_new_n(mi_prim_get_default_heap(), size, count); + return mi_theap_alloc_new_n(_mi_theap_default(), count, size); +} + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) { + return mi_theap_alloc_new_n(_mi_heap_theap(heap), count, size); } @@ -577,22 +781,113 @@ mi_decl_nodiscard void* mi_new_reallocn(void* p, size_t newcount, size_t size) { } } +#if MI_GUARDED +// We always allocate a guarded allocation at an offset (`mi_page_has_interior_pointers` will be true). +// We then set the first word of the block to `0` for regular offset aligned allocations (in `alloc-aligned.c`) +// and the first word to `~0` for guarded allocations to have a correct `mi_usable_size` + +static void* mi_block_ptr_set_guarded(mi_block_t* block, size_t obj_size) { + // TODO: we can still make padding work by moving it out of the guard page area + mi_page_t* const page = _mi_ptr_page(block); + mi_page_set_has_interior_pointers(page, true); + block->next = MI_BLOCK_TAG_GUARDED; + + // set guard page at the end of the block + const size_t block_size = mi_page_block_size(page); // must use `block_size` to match `mi_free_local` + const size_t os_page_size = _mi_os_page_size(); + mi_assert_internal(block_size >= obj_size + os_page_size + sizeof(mi_block_t)); + if (block_size < obj_size + os_page_size + sizeof(mi_block_t)) { + // should never happen + mi_free(block); + return NULL; + } + uint8_t* guard_page = (uint8_t*)block + block_size - os_page_size; + // note: the alignment of the guard page relies on blocks being os_page_size aligned which + // is ensured in `mi_arena_page_alloc_fresh`. + mi_assert_internal(_mi_is_aligned(block, os_page_size)); + mi_assert_internal(_mi_is_aligned(guard_page, os_page_size)); + if (!page->memid.is_pinned && _mi_is_aligned(guard_page, os_page_size)) { + const bool ok = _mi_os_protect(guard_page, os_page_size); + if mi_unlikely(!ok) { + _mi_warning_message("failed to set a guard page behind an object (object %p of size %zu)\n", block, block_size); + } + } + else { + _mi_warning_message("unable to set a guard page behind an object due to pinned memory (large OS pages?) (object %p of size %zu)\n", block, block_size); + } + + // align pointer just in front of the guard page + size_t offset = block_size - os_page_size - obj_size; + mi_assert_internal(offset > sizeof(mi_block_t)); + if (offset > MI_PAGE_MAX_OVERALLOC_ALIGN) { + // give up to place it right in front of the guard page if the offset is too large for unalignment + offset = MI_PAGE_MAX_OVERALLOC_ALIGN; + } + uint8_t* const p = (uint8_t*)block + offset; + mi_assert_internal(p == guard_page - obj_size); + mi_track_align(block, p, offset, obj_size); + mi_track_mem_defined(block, sizeof(mi_block_t)); + return p; +} + +mi_decl_restrict void* _mi_theap_malloc_guarded(mi_theap_t* theap, size_t size, bool zero) mi_attr_noexcept +{ + // allocate multiple of page size ending in a guard page + // ensure minimal alignment requirement? + if mi_unlikely(size >= MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) { // check up front so the `req_size` won't overflow + _mi_error_message(EOVERFLOW, "(guarded) allocation request is too large (%zu bytes)\n", size); + return NULL; + } + const size_t os_page_size = _mi_os_page_size(); + const size_t obj_size = (mi_option_is_enabled(mi_option_guarded_precise) ? size : _mi_align_up(size, MI_MAX_ALIGN_SIZE)); + const size_t bsize = _mi_align_up(_mi_align_up(obj_size, MI_MAX_ALIGN_SIZE) + sizeof(mi_block_t), MI_MAX_ALIGN_SIZE); + const size_t req_size = _mi_align_up(bsize + os_page_size, os_page_size); + mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(theap, req_size, 0 /* don't zero */, NULL); + if (block==NULL) return NULL; + void* const p = mi_block_ptr_set_guarded(block, obj_size); + if (p == NULL) return p; + if (zero) { + _mi_memzero_aligned(p,obj_size); // we have to zero here as padding might have written here (if the blocksize > reqsize + os_page_size) + } + + // stats + mi_track_malloc(p, obj_size, zero); + if (!mi_theap_is_initialized(theap)) { theap = _mi_theap_default(); } + mi_theap_stat_counter_increase(theap, malloc_guarded_count, 1); + #if MI_STAT>1 + // adjust stats to only count the allocated size of the block (and not the guard page) + mi_theap_stat_adjust_decrease(theap, malloc_requested, req_size); + mi_theap_stat_increase(theap, malloc_requested, size); + #endif + #if MI_DEBUG>3 + if (zero) { + mi_assert_expensive(mi_mem_is_zero(p, size)); + } + #endif + return p; +} +#endif + // ------------------------------------------------------ // ensure explicit external inline definitions are emitted! // ------------------------------------------------------ #ifdef __cplusplus void* _mi_externs[] = { - (void*)&_mi_page_malloc, - (void*)&_mi_heap_malloc_zero, - (void*)&_mi_heap_malloc_zero_ex, + (void*)&_mi_page_malloc_zero, + (void*)&_mi_theap_malloc_zero, + (void*)&_mi_theap_malloc_zero_ex, + (void*)&mi_theap_malloc, + (void*)&mi_theap_zalloc, + (void*)&mi_theap_malloc_small, (void*)&mi_malloc, (void*)&mi_malloc_small, + (void*)&mi_zalloc, (void*)&mi_zalloc_small, (void*)&mi_heap_malloc, - (void*)&mi_heap_zalloc, (void*)&mi_heap_malloc_small, - // (void*)&mi_heap_alloc_new, - // (void*)&mi_heap_alloc_new_n + (void*)&mi_malloc_aligned + // (void*)&mi_theap_alloc_new, + // (void*)&mi_theap_alloc_new_n }; #endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena-meta.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena-meta.c new file mode 100644 index 00000000..cee38caa --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena-meta.c @@ -0,0 +1,179 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2019-2024, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +/* ---------------------------------------------------------------------------- + We have a special "mini" allocator just for allocation of meta-data like + the theap (`mi_theap_t`) or thread-local data (`mi_tld_t`). + + We reuse the bitmap of the arena's for allocation of 64b blocks inside + an arena slice (64KiB). + We always ensure that meta data is zero'd (we zero on `free`) +-----------------------------------------------------------------------------*/ + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "bitmap.h" + +/* ----------------------------------------------------------- + Meta data allocation +----------------------------------------------------------- */ + +#define MI_META_PAGE_SIZE MI_ARENA_SLICE_SIZE +#define MI_META_PAGE_ALIGN MI_ARENA_SLICE_ALIGN + +// large enough such that META_MAX_SIZE > 4k (even on 32-bit) +#define MI_META_BLOCK_SIZE (1 << (16 - MI_BCHUNK_BITS_SHIFT)) // 128 on 64-bit +#define MI_META_BLOCK_ALIGN MI_META_BLOCK_SIZE +#define MI_META_BLOCKS_PER_PAGE (MI_META_PAGE_SIZE / MI_META_BLOCK_SIZE) // 512 +#define MI_META_MAX_SIZE (MI_BCHUNK_SIZE * MI_META_BLOCK_SIZE) + +#if MI_META_MAX_SIZE <= 4096 +#error "max meta object size should be at least 4KiB" +#endif + +typedef struct mi_meta_page_s { + _Atomic(struct mi_meta_page_s*) next; // a linked list of meta-data pages (never released) + mi_memid_t memid; // provenance of the meta-page memory itself + mi_bbitmap_t blocks_free; // a small bitmap with 1 bit per block. +} mi_meta_page_t; + +static mi_decl_cache_align _Atomic(mi_meta_page_t*) mi_meta_pages = MI_ATOMIC_VAR_INIT(NULL); + + +#if MI_DEBUG > 1 +static mi_meta_page_t* mi_meta_page_of_ptr(void* p, size_t* block_idx) { + mi_meta_page_t* mpage = (mi_meta_page_t*)((uint8_t*)_mi_align_down_ptr(p,MI_META_PAGE_ALIGN) + _mi_os_secure_guard_page_size()); + if (block_idx != NULL) { + *block_idx = ((uint8_t*)p - (uint8_t*)mpage) / MI_META_BLOCK_SIZE; + } + return mpage; +} +#endif + +static mi_meta_page_t* mi_meta_page_next( mi_meta_page_t* mpage ) { + return mi_atomic_load_ptr_acquire(mi_meta_page_t, &mpage->next); +} + +static void* mi_meta_block_start( mi_meta_page_t* mpage, size_t block_idx ) { + mi_assert_internal(_mi_is_aligned((uint8_t*)mpage - _mi_os_secure_guard_page_size(), MI_META_PAGE_ALIGN)); + mi_assert_internal(block_idx < MI_META_BLOCKS_PER_PAGE); + void* p = ((uint8_t*)mpage - _mi_os_secure_guard_page_size() + (block_idx * MI_META_BLOCK_SIZE)); + mi_assert_internal(mpage == mi_meta_page_of_ptr(p,NULL)); + return p; +} + +// allocate a fresh meta page and add it to the global list. +static mi_meta_page_t* mi_meta_page_zalloc(void) { + // allocate a fresh arena slice + // note: careful with _mi_subproc as it may recurse into mi_tld and meta_page_zalloc again.. (same with _mi_os_numa_node()...) + mi_memid_t memid; + uint8_t* base = (uint8_t*)_mi_arenas_alloc_aligned(mi_heap_main(), MI_META_PAGE_SIZE, MI_META_PAGE_ALIGN, 0, + true /* commit*/, (MI_SECURE==0) /* allow large? */, + NULL /* req arena */, 0 /* thread_seq */, -1 /* numa node */, &memid); + if (base == NULL) return NULL; + mi_assert_internal(_mi_is_aligned(base,MI_META_PAGE_ALIGN)); + if (!memid.initially_zero) { + _mi_memzero_aligned(base, MI_ARENA_SLICE_SIZE); + } + + // guard pages + #if MI_SECURE >= 1 + _mi_os_secure_guard_page_set_at(base, memid); + _mi_os_secure_guard_page_set_before(base + MI_META_PAGE_SIZE, memid); + #endif + + // initialize the page and free block bitmap + mi_meta_page_t* mpage = (mi_meta_page_t*)(base + _mi_os_secure_guard_page_size()); + mpage->memid = memid; + mi_bbitmap_init(&mpage->blocks_free, MI_META_BLOCKS_PER_PAGE, true /* already_zero */); + const size_t mpage_size = offsetof(mi_meta_page_t,blocks_free) + mi_bbitmap_size(MI_META_BLOCKS_PER_PAGE, NULL); + const size_t info_blocks = _mi_divide_up(mpage_size,MI_META_BLOCK_SIZE); + const size_t guard_blocks = _mi_divide_up(_mi_os_secure_guard_page_size(), MI_META_BLOCK_SIZE); + mi_assert_internal(info_blocks + 2*guard_blocks < MI_META_BLOCKS_PER_PAGE); + mi_bbitmap_unsafe_setN(&mpage->blocks_free, info_blocks + guard_blocks, MI_META_BLOCKS_PER_PAGE - info_blocks - 2*guard_blocks); + + // push atomically in front of the meta page list + // (note: there is no ABA issue since we never free meta-pages) + mi_meta_page_t* old = mi_atomic_load_ptr_acquire(mi_meta_page_t,&mi_meta_pages); + do { + mi_atomic_store_ptr_release(mi_meta_page_t, &mpage->next, old); + } while(!mi_atomic_cas_ptr_weak_acq_rel(mi_meta_page_t,&mi_meta_pages,&old,mpage)); + return mpage; +} + + +// allocate meta-data +mi_decl_noinline void* _mi_meta_zalloc( size_t size, mi_memid_t* pmemid ) +{ + mi_assert_internal(pmemid != NULL); + size = _mi_align_up(size,MI_META_BLOCK_SIZE); + if (size == 0 || size > MI_META_MAX_SIZE) return NULL; + const size_t block_count = _mi_divide_up(size,MI_META_BLOCK_SIZE); + mi_assert_internal(block_count > 0 && block_count < MI_BCHUNK_BITS); + mi_meta_page_t* mpage0 = mi_atomic_load_ptr_acquire(mi_meta_page_t,&mi_meta_pages); + mi_meta_page_t* mpage = mpage0; + while (mpage != NULL) { + size_t block_idx; + if (mi_bbitmap_try_find_and_clearN(&mpage->blocks_free, 0, block_count, &block_idx)) { + // found and claimed `block_count` blocks + *pmemid = _mi_memid_create_meta(mpage, block_idx, block_count); + return mi_meta_block_start(mpage,block_idx); + } + else { + mpage = mi_meta_page_next(mpage); + } + } + // failed to find space in existing pages + if (mi_atomic_load_ptr_acquire(mi_meta_page_t,&mi_meta_pages) != mpage0) { + // the page list was updated by another thread in the meantime, retry + return _mi_meta_zalloc(size,pmemid); + } + // otherwise, allocate a fresh metapage and try once more + mpage = mi_meta_page_zalloc(); + if (mpage != NULL) { + size_t block_idx; + if (mi_bbitmap_try_find_and_clearN(&mpage->blocks_free, 0, block_count, &block_idx)) { + // found and claimed `block_count` blocks + *pmemid = _mi_memid_create_meta(mpage, block_idx, block_count); + return mi_meta_block_start(mpage,block_idx); + } + } + // if all this failed, allocate from the OS + return _mi_os_alloc(size, pmemid); +} + +// free meta-data +mi_decl_noinline void _mi_meta_free(void* p, size_t size, mi_memid_t memid) { + if (p==NULL) return; + if (memid.memkind == MI_MEM_META) { + mi_assert_internal(_mi_divide_up(size, MI_META_BLOCK_SIZE) == memid.mem.meta.block_count); + const size_t block_count = memid.mem.meta.block_count; + const size_t block_idx = memid.mem.meta.block_index; + mi_meta_page_t* mpage = (mi_meta_page_t*)memid.mem.meta.meta_page; + mi_assert_internal(mi_meta_page_of_ptr(p,NULL) == mpage); + mi_assert_internal(block_idx + block_count <= MI_META_BLOCKS_PER_PAGE); + mi_assert_internal(mi_bbitmap_is_clearN(&mpage->blocks_free, block_idx, block_count)); + // we zero on free (and on the initial page allocation) so we don't need a "dirty" map + _mi_memzero_aligned(mi_meta_block_start(mpage, block_idx), block_count*MI_META_BLOCK_SIZE); + mi_bbitmap_setN(&mpage->blocks_free, block_idx, block_count); + } + else { + _mi_arenas_free(p,size,memid); + } +} + +// used for debug output +bool _mi_meta_is_meta_page(void* p) +{ + mi_meta_page_t* mpage0 = mi_atomic_load_ptr_acquire(mi_meta_page_t, &mi_meta_pages); + mi_meta_page_t* mpage = mpage0; + while (mpage != NULL) { + if ((void*)mpage == p) return true; + mpage = mi_meta_page_next(mpage); + } + return false; +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena.c index 648ee844..f90b75ea 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/arena.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2019-2023, Microsoft Research, Daan Leijen +Copyright (c) 2019-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,220 +7,208 @@ terms of the MIT license. A copy of the license can be found in the file /* ---------------------------------------------------------------------------- "Arenas" are fixed area's of OS memory from which we can allocate -large blocks (>= MI_ARENA_MIN_BLOCK_SIZE, 4MiB). +large blocks (>= MI_ARENA_MIN_BLOCK_SIZE, 64KiB). In contrast to the rest of mimalloc, the arenas are shared between threads and need to be accessed using atomic operations. -Arenas are used to for huge OS page (1GiB) reservations or for reserving +Arenas are also used to for huge OS page (1GiB) reservations or for reserving OS memory upfront which can be improve performance or is sometimes needed on embedded devices. We can also employ this with WASI or `sbrk` systems to reserve large arenas upfront and be able to reuse the memory more effectively. The arena allocation needs to be thread safe and we use an atomic bitmap to allocate. -----------------------------------------------------------------------------*/ + #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/atomic.h" - -#include // memset -#include // ENOMEM - -#include "bitmap.h" // atomic bitmap - -/* ----------------------------------------------------------- - Arena allocation ------------------------------------------------------------ */ +#include "mimalloc/prim.h" +#include "bitmap.h" -// Block info: bit 0 contains the `in_use` bit, the upper bits the -// size in count of arena blocks. -typedef uintptr_t mi_block_info_t; -#define MI_ARENA_BLOCK_SIZE (MI_SEGMENT_SIZE) // 64MiB (must be at least MI_SEGMENT_ALIGN) -#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_BLOCK_SIZE/2) // 32MiB -#define MI_MAX_ARENAS (112) // not more than 126 (since we use 7 bits in the memid and an arena index + 1) - -// A memory arena descriptor -typedef struct mi_arena_s { - mi_arena_id_t id; // arena id; 0 for non-specific - mi_memid_t memid; // memid of the memory area - _Atomic(uint8_t*) start; // the start of the memory area - size_t block_count; // size of the area in arena blocks (of `MI_ARENA_BLOCK_SIZE`) - size_t field_count; // number of bitmap fields (where `field_count * MI_BITMAP_FIELD_BITS >= block_count`) - size_t meta_size; // size of the arena structure itself (including its bitmaps) - mi_memid_t meta_memid; // memid of the arena structure itself (OS or static allocation) - int numa_node; // associated NUMA node - bool exclusive; // only allow allocations if specifically for this arena - bool is_large; // memory area consists of large- or huge OS pages (always committed) - _Atomic(size_t) search_idx; // optimization to start the search for free blocks - _Atomic(mi_msecs_t) purge_expire; // expiration time when blocks should be decommitted from `blocks_decommit`. - mi_bitmap_field_t* blocks_dirty; // are the blocks potentially non-zero? - mi_bitmap_field_t* blocks_committed; // are the blocks committed? (can be NULL for memory that cannot be decommitted) - mi_bitmap_field_t* blocks_purge; // blocks that can be (reset) decommitted. (can be NULL for memory that cannot be (reset) decommitted) - mi_bitmap_field_t* blocks_abandoned; // blocks that start with an abandoned segment. (This crosses API's but it is convenient to have here) - mi_bitmap_field_t blocks_inuse[1]; // in-place bitmap of in-use blocks (of size `field_count`) - // do not add further fields here as the dirty, committed, purged, and abandoned bitmaps follow the inuse bitmap fields. -} mi_arena_t; - - -// The available arenas -static mi_decl_cache_align _Atomic(mi_arena_t*) mi_arenas[MI_MAX_ARENAS]; -static mi_decl_cache_align _Atomic(size_t) mi_arena_count; // = 0 - - -//static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int numa_node, bool exclusive, mi_memid_t memid, mi_arena_id_t* arena_id) mi_attr_noexcept; +#if (MI_ARENA_MAX_SIZE >= MI_SIZE_SIZE*UINT32_MAX) +#error "The page_t.page_woffset field is not large enough to cover a full arena (redefine it to be an offset in MI_MAX_ALIGN_SIZE sizes?)" +#endif /* ----------------------------------------------------------- Arena id's - id = arena_index + 1 ----------------------------------------------------------- */ -static size_t mi_arena_id_index(mi_arena_id_t id) { - return (size_t)(id <= 0 ? MI_MAX_ARENAS : id - 1); +mi_arena_id_t _mi_arena_id_none(void) { + return NULL; } -static mi_arena_id_t mi_arena_id_create(size_t arena_index) { - mi_assert_internal(arena_index < MI_MAX_ARENAS); - return (int)arena_index + 1; +mi_arena_t* _mi_arena_from_id(mi_arena_id_t id) { + mi_arena_t* const arena = (mi_arena_t*)id; + mi_assert_internal(arena==NULL || arena->parent==NULL); // id's should never point to sub-arena's + return arena; } -mi_arena_id_t _mi_arena_id_none(void) { - return 0; +mi_arena_id_t mi_arena_id_from_arena(mi_arena_t* arena) { + mi_assert_internal(arena==NULL || arena->parent==NULL); + return (arena==NULL ? _mi_arena_id_none() : (mi_arena_id_t)arena); } -static bool mi_arena_id_is_suitable(mi_arena_id_t arena_id, bool arena_is_exclusive, mi_arena_id_t req_arena_id) { - return ((!arena_is_exclusive && req_arena_id == _mi_arena_id_none()) || - (arena_id == req_arena_id)); + +static bool mi_arena_is_suitable(mi_arena_t* arena, mi_arena_t* req_arena) { + if (arena == req_arena) return true; // they match + if (arena == NULL) return false; + if (req_arena == NULL && !arena->is_exclusive) return true; // or the arena is not exclusive, and we didn't request a specific one + if (arena->parent != NULL && arena->parent == req_arena) return true; // sub-arena? (note that req_arena is never a sub arena) + return false; } -bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_id_t request_arena_id) { +bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_t* request_arena) { if (memid.memkind == MI_MEM_ARENA) { - return mi_arena_id_is_suitable(memid.mem.arena.id, memid.mem.arena.is_exclusive, request_arena_id); + return mi_arena_is_suitable(memid.mem.arena.arena, request_arena); } else { - return mi_arena_id_is_suitable(_mi_arena_id_none(), false, request_arena_id); + return mi_arena_is_suitable(NULL, request_arena); } } -bool _mi_arena_memid_is_os_allocated(mi_memid_t memid) { - return (memid.memkind == MI_MEM_OS); +size_t mi_arenas_get_count(mi_subproc_t* subproc) { + return mi_atomic_load_relaxed(&subproc->arena_count); } -/* ----------------------------------------------------------- - Arena allocations get a (currently) 16-bit memory id where the - lower 8 bits are the arena id, and the upper bits the block index. ------------------------------------------------------------ */ +mi_arena_t* mi_arena_from_index(mi_subproc_t* subproc, size_t idx) { + mi_assert_internal(idx < mi_arenas_get_count(subproc)); + return mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[idx]); +} -static size_t mi_block_count_of_size(size_t size) { - return _mi_divide_up(size, MI_ARENA_BLOCK_SIZE); +static size_t mi_arena_info_slices(mi_arena_t* arena) { + return arena->info_slices; } -static size_t mi_arena_block_size(size_t bcount) { - return (bcount * MI_ARENA_BLOCK_SIZE); +#if MI_DEBUG > 1 +static bool mi_heap_has_page(mi_heap_t* heap, mi_arena_t* arena, mi_page_t* page) { + mi_assert(arena->arena_idx < MI_MAX_ARENAS); + mi_arena_pages_t* arena_pages = heap->arena_pages[arena->arena_idx]; + return (page->memid.memkind == MI_MEM_ARENA && + page->memid.mem.arena.arena == arena && + arena_pages != NULL && + mi_bitmap_is_setN(arena_pages->pages, page->memid.mem.arena.slice_index, 1)); } +#endif -static size_t mi_arena_size(mi_arena_t* arena) { - return mi_arena_block_size(arena->block_count); +size_t mi_arena_min_alignment(void) { + return MI_ARENA_SLICE_ALIGN; } -static mi_memid_t mi_memid_create_arena(mi_arena_id_t id, bool is_exclusive, mi_bitmap_index_t bitmap_index) { - mi_memid_t memid = _mi_memid_create(MI_MEM_ARENA); - memid.mem.arena.id = id; - memid.mem.arena.block_index = bitmap_index; - memid.mem.arena.is_exclusive = is_exclusive; - return memid; +size_t mi_arena_min_size(void) { + return MI_ARENA_MIN_SIZE; } -static bool mi_arena_memid_indices(mi_memid_t memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) { - mi_assert_internal(memid.memkind == MI_MEM_ARENA); - *arena_index = mi_arena_id_index(memid.mem.arena.id); - *bitmap_index = memid.mem.arena.block_index; - return memid.mem.arena.is_exclusive; +static size_t mi_arena_max_object_size(void) { + size_t max_size = mi_option_get_size(mi_option_arena_max_object_size); + max_size = _mi_align_up(max_size, MI_ARENA_SLICE_SIZE); + if (max_size <= MI_ARENA_MIN_OBJ_SIZE) { + return MI_ARENA_MIN_OBJ_SIZE; + } + else if (max_size >= MI_ARENA_MAX_SIZE - MI_BCHUNK_SIZE) { // minus a bchunk to accommodate meta info + return (MI_ARENA_MAX_SIZE - MI_BCHUNK_SIZE); + } + else { + return max_size; + } +} + +mi_decl_nodiscard static bool mi_arena_commit(mi_arena_t* arena, void* start, size_t size, bool* is_zero, size_t already_committed) { + if (arena != NULL && arena->commit_fun != NULL) { + return (*arena->commit_fun)(true, start, size, is_zero, arena->commit_fun_arg); + } + else if (already_committed > 0) { + return _mi_os_commit_ex(start, size, is_zero, already_committed); + } + else { + return _mi_os_commit(start, size, is_zero); + } } /* ----------------------------------------------------------- - Special static area for mimalloc internal structures - to avoid OS calls (for example, for the arena metadata) + Util ----------------------------------------------------------- */ -#define MI_ARENA_STATIC_MAX (MI_INTPTR_SIZE*MI_KiB) // 8 KiB on 64-bit - -static mi_decl_cache_align uint8_t mi_arena_static[MI_ARENA_STATIC_MAX]; // must be cache aligned, see issue #895 -static mi_decl_cache_align _Atomic(size_t) mi_arena_static_top; -static void* mi_arena_static_zalloc(size_t size, size_t alignment, mi_memid_t* memid) { - *memid = _mi_memid_none(); - if (size == 0 || size > MI_ARENA_STATIC_MAX) return NULL; - const size_t toplow = mi_atomic_load_relaxed(&mi_arena_static_top); - if ((toplow + size) > MI_ARENA_STATIC_MAX) return NULL; - - // try to claim space - if (alignment < MI_MAX_ALIGN_SIZE) { alignment = MI_MAX_ALIGN_SIZE; } - const size_t oversize = size + alignment - 1; - if (toplow + oversize > MI_ARENA_STATIC_MAX) return NULL; - const size_t oldtop = mi_atomic_add_acq_rel(&mi_arena_static_top, oversize); - size_t top = oldtop + oversize; - if (top > MI_ARENA_STATIC_MAX) { - // try to roll back, ok if this fails - mi_atomic_cas_strong_acq_rel(&mi_arena_static_top, &top, oldtop); - return NULL; - } - - // success - *memid = _mi_memid_create(MI_MEM_STATIC); - memid->initially_zero = true; - const size_t start = _mi_align_up(oldtop, alignment); - uint8_t* const p = &mi_arena_static[start]; - _mi_memzero_aligned(p, size); - return p; +// Size of an arena +static size_t mi_arena_size(mi_arena_t* arena) { + return mi_size_of_slices(arena->slice_count); } -static void* mi_arena_meta_zalloc(size_t size, mi_memid_t* memid, mi_stats_t* stats) { - *memid = _mi_memid_none(); - - // try static - void* p = mi_arena_static_zalloc(size, MI_MAX_ALIGN_SIZE, memid); - if (p != NULL) return p; +// Start of the arena memory area +static uint8_t* mi_arena_start(mi_arena_t* arena) { + return ((uint8_t*)arena); +} - // or fall back to the OS - p = _mi_os_alloc(size, memid, stats); - if (p == NULL) return NULL; +// Start of a slice +uint8_t* mi_arena_slice_start(mi_arena_t* arena, size_t slice_index) { + mi_assert_internal(slice_index < arena->slice_count); + return (mi_arena_start(arena) + mi_size_of_slices(slice_index)); +} - // zero the OS memory if needed - if (!memid->initially_zero) { - _mi_memzero_aligned(p, size); - memid->initially_zero = true; +mi_page_t* mi_arena_page_at_slice(mi_arena_t* arena, size_t slice_index) { + mi_assert_internal(slice_index < arena->slice_count); + if (arena->pages_meta != NULL) { + mi_page_t* const page = &arena->pages_meta[slice_index]; + #if MI_PAGE_META_ALIGNED_FREE_SMALL + // pages with small blocks still have the page at the start of the slice (and set the `block_size` in pages_meta to 0) + if (page->block_size>0) return page; + #else + return page; + #endif } - return p; + // fall through (for MI_PAGE_META_ALIGNED_FREE_SMALL) + return (mi_page_t*)mi_arena_slice_start(arena,slice_index); } -static void mi_arena_meta_free(void* p, mi_memid_t memid, size_t size, mi_stats_t* stats) { - if (mi_memkind_is_os(memid.memkind)) { - _mi_os_free(p, size, memid, stats); - } - else { - mi_assert(memid.memkind == MI_MEM_STATIC); +// Arena area +void* mi_arena_area(mi_arena_id_t arena_id, size_t* size) { + if (size != NULL) *size = 0; + mi_arena_t* arena = _mi_arena_from_id(arena_id); + if (arena == NULL) return NULL; + if (size != NULL) { + mi_assert_internal(mi_size_of_slices(arena->slice_count) <= arena->total_size); + *size = arena->total_size; } + return mi_arena_start(arena); } -static void* mi_arena_block_start(mi_arena_t* arena, mi_bitmap_index_t bindex) { - return (arena->start + mi_arena_block_size(mi_bitmap_index_bit(bindex))); -} +// Create an arena memid +static mi_memid_t mi_memid_create_arena(mi_arena_t* arena, size_t slice_index, size_t slice_count) { + mi_assert_internal(slice_index < UINT32_MAX); + mi_assert_internal(slice_count < UINT32_MAX); + mi_assert_internal(slice_count > 0); + mi_assert_internal(slice_index < arena->slice_count); + mi_memid_t memid = _mi_memid_create(MI_MEM_ARENA); + memid.mem.arena.arena = arena; + memid.mem.arena.slice_index = (uint32_t)slice_index; + memid.mem.arena.slice_count = (uint32_t)slice_count; + return memid; +} -/* ----------------------------------------------------------- - Thread safe allocation in an arena ------------------------------------------------------------ */ +// get the arena and slice span +static mi_arena_t* mi_arena_from_memid(mi_memid_t memid, size_t* slice_index, size_t* slice_count) { + mi_assert_internal(memid.memkind == MI_MEM_ARENA); + mi_arena_t* arena = memid.mem.arena.arena; + if (slice_index!=NULL) { *slice_index = memid.mem.arena.slice_index; } + if (slice_count!=NULL) { *slice_count = memid.mem.arena.slice_count; } + return arena; +} -// claim the `blocks_inuse` bits -static bool mi_arena_try_claim(mi_arena_t* arena, size_t blocks, mi_bitmap_index_t* bitmap_idx, mi_stats_t* stats) -{ - size_t idx = 0; // mi_atomic_load_relaxed(&arena->search_idx); // start from last search; ok to be relaxed as the exact start does not matter - if (_mi_bitmap_try_find_from_claim_across(arena->blocks_inuse, arena->field_count, idx, blocks, bitmap_idx, stats)) { - mi_atomic_store_relaxed(&arena->search_idx, mi_bitmap_index_field(*bitmap_idx)); // start search from found location next time around - return true; - }; - return false; +static size_t mi_page_full_size(mi_page_t* page) { + if (page->memid.memkind == MI_MEM_ARENA) { + return page->memid.mem.arena.slice_count * MI_ARENA_SLICE_SIZE; + } + else if (mi_memid_is_os(page->memid) || page->memid.memkind == MI_MEM_EXTERNAL) { + mi_assert_internal((uint8_t*)page->memid.mem.os.base <= (uint8_t*)page); + const ptrdiff_t presize = (uint8_t*)page - (uint8_t*)page->memid.mem.os.base; + mi_assert_internal((ptrdiff_t)page->memid.mem.os.size >= presize); + return (presize > (ptrdiff_t)page->memid.mem.os.size ? 0 : page->memid.mem.os.size - presize); + } + else { + return 0; + } } @@ -228,646 +216,1218 @@ static bool mi_arena_try_claim(mi_arena_t* arena, size_t blocks, mi_bitmap_index Arena Allocation ----------------------------------------------------------- */ -static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t arena_index, size_t needed_bcount, - bool commit, mi_memid_t* memid, mi_os_tld_t* tld) +static mi_decl_noinline void* mi_arena_try_alloc_at( + mi_arena_t* arena, size_t slice_count, bool commit, size_t tseq, mi_memid_t* memid) { - MI_UNUSED(arena_index); - mi_assert_internal(mi_arena_id_index(arena->id) == arena_index); - - mi_bitmap_index_t bitmap_index; - if (!mi_arena_try_claim(arena, needed_bcount, &bitmap_index, tld->stats)) return NULL; + size_t slice_index; + if (!mi_bbitmap_try_find_and_clearN(arena->slices_free, tseq, slice_count, &slice_index)) return NULL; // claimed it! - void* p = mi_arena_block_start(arena, bitmap_index); - *memid = mi_memid_create_arena(arena->id, arena->exclusive, bitmap_index); + void* p = mi_arena_slice_start(arena, slice_index); + *memid = mi_memid_create_arena(arena, slice_index, slice_count); memid->is_pinned = arena->memid.is_pinned; - // none of the claimed blocks should be scheduled for a decommit - if (arena->blocks_purge != NULL) { - // this is thread safe as a potential purge only decommits parts that are not yet claimed as used (in `blocks_inuse`). - _mi_bitmap_unclaim_across(arena->blocks_purge, arena->field_count, needed_bcount, bitmap_index); - } - - // set the dirty bits (todo: no need for an atomic op here?) - if (arena->memid.initially_zero && arena->blocks_dirty != NULL) { - memid->initially_zero = _mi_bitmap_claim_across(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL); + // set the dirty bits and track which slices become accessible + size_t touched_slices = slice_count; + if (arena->memid.initially_zero) { + size_t already_dirty = 0; + memid->initially_zero = mi_bitmap_setN(arena->slices_dirty, slice_index, slice_count, &already_dirty); + mi_assert_internal(already_dirty <= touched_slices); + touched_slices -= already_dirty; } // set commit state - if (arena->blocks_committed == NULL) { - // always committed - memid->initially_committed = true; - } - else if (commit) { + if (commit) { // commit requested, but the range may not be committed as a whole: ensure it is committed now - memid->initially_committed = true; - bool any_uncommitted; - _mi_bitmap_claim_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &any_uncommitted); - if (any_uncommitted) { + const size_t already_committed = mi_bitmap_popcountN(arena->slices_committed, slice_index, slice_count); + if (already_committed < slice_count) { + // not all committed, try to commit now bool commit_zero = false; - if (!_mi_os_commit(p, mi_arena_block_size(needed_bcount), &commit_zero, tld->stats)) { - memid->initially_committed = false; + if (!mi_arena_commit(arena, p, mi_size_of_slices(slice_count), &commit_zero, mi_size_of_slices(slice_count - already_committed))) { + // if the commit fails, release ownership, and return NULL; + // note: this does not roll back dirty bits but that is ok. + mi_bbitmap_setN(arena->slices_free, slice_index, slice_count); + return NULL; } - else { - if (commit_zero) { memid->initially_zero = true; } + if (commit_zero) { + memid->initially_zero = true; } - } - } - else { - // no need to commit, but check if already fully committed - memid->initially_committed = _mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index); - } - return p; -} - -// allocate in a speficic arena -static void* mi_arena_try_alloc_at_id(mi_arena_id_t arena_id, bool match_numa_node, int numa_node, size_t size, size_t alignment, - bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld ) -{ - MI_UNUSED_RELEASE(alignment); - mi_assert_internal(alignment <= MI_SEGMENT_ALIGN); - const size_t bcount = mi_block_count_of_size(size); - const size_t arena_index = mi_arena_id_index(arena_id); - mi_assert_internal(arena_index < mi_atomic_load_relaxed(&mi_arena_count)); - mi_assert_internal(size <= mi_arena_block_size(bcount)); - - // Check arena suitability - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_index]); - if (arena == NULL) return NULL; - if (!allow_large && arena->is_large) return NULL; - if (!mi_arena_id_is_suitable(arena->id, arena->exclusive, req_arena_id)) return NULL; - if (req_arena_id == _mi_arena_id_none()) { // in not specific, check numa affinity - const bool numa_suitable = (numa_node < 0 || arena->numa_node < 0 || arena->numa_node == numa_node); - if (match_numa_node) { if (!numa_suitable) return NULL; } - else { if (numa_suitable) return NULL; } - } + // set the commit bits + mi_bitmap_setN(arena->slices_committed, slice_index, slice_count, NULL); - // try to allocate - void* p = mi_arena_try_alloc_at(arena, arena_index, bcount, commit, memid, tld); - mi_assert_internal(p == NULL || _mi_is_aligned(p, alignment)); - return p; -} + // committed + #if MI_DEBUG > 1 + if (memid->initially_zero) { + if (!mi_mem_is_zero(p, mi_size_of_slices(slice_count))) { + _mi_error_message(EFAULT, "internal error: arena allocation was not zero-initialized!\n"); + memid->initially_zero = false; + } + } + #endif + } + else { + // already fully committed. + _mi_os_reuse(p, mi_size_of_slices(slice_count)); + // if the OS has overcommit, and this is the first time we access these pages, then + // count the commit now (as at arena reserve we didn't count those commits as these are on-demand) + if (_mi_os_has_overcommit() && touched_slices > 0 && !arena->memid.is_pinned /* huge pages, issue #1236 */) { + mi_subproc_stat_increase( arena->subproc, committed, mi_size_of_slices(touched_slices)); + } + } + mi_assert_internal(mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); + memid->initially_committed = true; -// allocate from an arena with fallback to the OS -static mi_decl_noinline void* mi_arena_try_alloc(int numa_node, size_t size, size_t alignment, - bool commit, bool allow_large, - mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld ) -{ - MI_UNUSED(alignment); - mi_assert_internal(alignment <= MI_SEGMENT_ALIGN); - const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count); - if mi_likely(max_arena == 0) return NULL; - - if (req_arena_id != _mi_arena_id_none()) { - // try a specific arena if requested - if (mi_arena_id_index(req_arena_id) < max_arena) { - void* p = mi_arena_try_alloc_at_id(req_arena_id, true, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld); - if (p != NULL) return p; + // tool support + if (memid->initially_zero) { + mi_track_mem_defined(p, slice_count * MI_ARENA_SLICE_SIZE); + } + else { + mi_track_mem_undefined(p, slice_count * MI_ARENA_SLICE_SIZE); } } else { - // try numa affine allocation - for (size_t i = 0; i < max_arena; i++) { - void* p = mi_arena_try_alloc_at_id(mi_arena_id_create(i), true, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld); - if (p != NULL) return p; - } - - // try from another numa node instead.. - if (numa_node >= 0) { // if numa_node was < 0 (no specific affinity requested), all arena's have been tried already - for (size_t i = 0; i < max_arena; i++) { - void* p = mi_arena_try_alloc_at_id(mi_arena_id_create(i), false /* only proceed if not numa local */, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld); - if (p != NULL) return p; - } + // no need to commit, but check if it is already fully committed + memid->initially_committed = mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count); + if (!memid->initially_committed) { + // partly committed.. adjust stats + size_t already_committed_count = 0; + mi_bitmap_setN(arena->slices_committed, slice_index, slice_count, &already_committed_count); + mi_bitmap_clearN(arena->slices_committed, slice_index, slice_count); + mi_subproc_stat_decrease(arena->subproc, committed, mi_size_of_slices(already_committed_count)); } } - return NULL; + + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + if (commit) { mi_assert_internal(mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); } + if (commit) { mi_assert_internal(memid->initially_committed); } + mi_assert_internal(mi_bitmap_is_setN(arena->slices_dirty, slice_index, slice_count)); + + return p; } + +static int mi_reserve_os_memory_ex2(mi_subproc_t* subproc, size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id); + // try to reserve a fresh arena space -static bool mi_arena_reserve(size_t req_size, bool allow_large, mi_arena_id_t req_arena_id, mi_arena_id_t *arena_id) +static bool mi_arena_reserve(mi_subproc_t* subproc, size_t req_size, bool allow_large, mi_arena_id_t* arena_id) { - if (_mi_preloading()) return false; // use OS only while pre loading - if (req_arena_id != _mi_arena_id_none()) return false; - - const size_t arena_count = mi_atomic_load_acquire(&mi_arena_count); + const size_t arena_count = mi_arenas_get_count(subproc); if (arena_count > (MI_MAX_ARENAS - 4)) return false; + // calc reserve size_t arena_reserve = mi_option_get_size(mi_option_arena_reserve); if (arena_reserve == 0) return false; if (!_mi_os_has_virtual_reserve()) { arena_reserve = arena_reserve/4; // be conservative if virtual reserve is not supported (for WASM for example) } - arena_reserve = _mi_align_up(arena_reserve, MI_ARENA_BLOCK_SIZE); - if (arena_count >= 8 && arena_count <= 128) { - arena_reserve = ((size_t)1<<(arena_count/8)) * arena_reserve; // scale up the arena sizes exponentially + arena_reserve = _mi_align_up(arena_reserve, MI_ARENA_SLICE_SIZE); + + if (arena_count >= 1 && arena_count <= 128) { + // scale up the arena sizes exponentially every 8 entries + const size_t multiplier = (size_t)1 << _mi_clamp(arena_count/8, 0, 16); + size_t reserve = 0; + if (!mi_mul_overflow(multiplier, arena_reserve, &reserve)) { + arena_reserve = reserve; + } + } + + // try to accommodate the requested size for huge allocations + req_size = _mi_align_up(req_size + MI_ARENA_MAX_CHUNK_OBJ_SIZE, MI_ARENA_MAX_CHUNK_OBJ_SIZE); // over-reserve for meta-info + if (arena_reserve < req_size) { + arena_reserve = req_size; + } + + // check arena bounds + const size_t min_reserve = MI_ARENA_MIN_SIZE; + const size_t max_reserve = MI_ARENA_MAX_SIZE; // 16 GiB + if (arena_reserve < min_reserve) { + arena_reserve = min_reserve; + } + else if (arena_reserve > max_reserve) { + arena_reserve = max_reserve; } - if (arena_reserve < req_size) return false; // should be able to at least handle the current allocation size + + // should be able to at least handle the current allocation size + if (arena_reserve < req_size) return false; // commit eagerly? bool arena_commit = false; - if (mi_option_get(mi_option_arena_eager_commit) == 2) { arena_commit = _mi_os_has_overcommit(); } + const bool overcommit = _mi_os_has_overcommit(); + if (mi_option_get(mi_option_arena_eager_commit) == 2) { arena_commit = overcommit || mi_option_is_enabled(mi_option_allow_large_os_pages); } else if (mi_option_get(mi_option_arena_eager_commit) == 1) { arena_commit = true; } - return (mi_reserve_os_memory_ex(arena_reserve, arena_commit, allow_large, false /* exclusive? */, arena_id) == 0); + // on an OS with overcommit (Linux) we don't count the commit yet as it is on-demand. Once a slice + // is actually allocated for the first time it will be counted. + const bool adjust = (overcommit && arena_commit); + if (adjust) { mi_subproc_stat_adjust_decrease( subproc, committed, arena_reserve); } + // and try to reserve the arena + int err = mi_reserve_os_memory_ex2(subproc, arena_reserve, arena_commit, allow_large, false /* exclusive? */, arena_id); + if (err != 0) { + if (adjust) { mi_subproc_stat_adjust_increase( subproc, committed, arena_reserve); } // roll back + // failed to allocate: try a smaller size arena as fallback? + const size_t small_arena_reserve = 4 * MI_ARENA_MIN_SIZE; // 128 MiB (or 32 MiB on 32-bit) + if (arena_reserve > small_arena_reserve && small_arena_reserve > req_size) { + // try again + if (adjust) { mi_subproc_stat_adjust_decrease(subproc, committed, small_arena_reserve); } + err = mi_reserve_os_memory_ex2(subproc, small_arena_reserve, arena_commit, allow_large, false /* exclusive? */, arena_id); + if (err != 0 && adjust) { mi_subproc_stat_adjust_increase( subproc, committed, small_arena_reserve); } // roll back + } + } + return (err==0); +} + + + + +/* ----------------------------------------------------------- + Arena iteration +----------------------------------------------------------- */ + +static inline bool mi_arena_is_suitable_ex(mi_arena_t* arena, mi_arena_t* req_arena, bool match_numa, int numa_node, bool allow_pinned) { + if (!allow_pinned && arena->memid.is_pinned) return false; + if (!mi_arena_is_suitable(arena, req_arena)) return false; + if (req_arena == NULL) { // if not specific, check numa affinity + const bool numa_suitable = (numa_node < 0 || arena->numa_node < 0 || arena->numa_node == numa_node); + if (match_numa) { if (!numa_suitable) return false; } + else { if (numa_suitable) return false; } + } + return true; +} + +// determine the start of search; important to keep heaps and threads +// into their own memory regions to reduce contention. +static size_t mi_arena_start_idx(mi_heap_t* heap, size_t tseq, size_t arena_cycle) { + const size_t hseq = heap->heap_seq; + const size_t hcount = mi_atomic_load_relaxed(&heap->subproc->heap_count); + if (arena_cycle <= 1) return 0; + if (hseq==0 || hcount<=1 || arena_cycle > 0x8FF) return (tseq % arena_cycle); // common for single heap programs + + // spread heaps evenly among arena's, and then evenly for threads in their fraction + size_t start; + mi_assert_internal(arena_cycle <= 0x8FF); // prevent overflow on 32-bit + const size_t frac = (arena_cycle * 256) / hcount; // fraction in the arena_cycle; at most: arena_cycle * 0x100 + if (frac==0) { + // many heaps (> 256 per arena) + start = (hseq % arena_cycle); + } + else { + const size_t hspot = (hseq % hcount); + start = (frac * hspot) / 256; // (arena_cycle * (hseq % hcount)) / hcount + if (frac >= 512) { // at least 2 arena's per heap? + start = start + (tseq % (frac/256)); + } + } + mi_assert_internal(start < arena_cycle); + return start; } +#define mi_forall_arenas(heap, req_arena, tseq, name_arena) { \ + const size_t _arena_count = mi_arenas_get_count(heap->subproc); \ + const size_t _arena_cycle = (_arena_count == 0 ? 0 : _arena_count - 1); /* first search the arenas below the last one */ \ + /* always start searching in the arena's below the max */ \ + const size_t _start = mi_arena_start_idx(heap,tseq,_arena_cycle); \ + for (size_t _i = 0; _i < _arena_count; _i++) { \ + mi_arena_t* name_arena; \ + if (req_arena != NULL) { \ + name_arena = req_arena; /* if there is a specific req_arena, only search that one */\ + if (_i > 0) break; /* only once */ \ + } \ + else { \ + size_t _idx; \ + if (_i < _arena_cycle) { \ + _idx = _i + _start; \ + if (_idx >= _arena_cycle) { _idx -= _arena_cycle; } /* adjust so we rotate through the cycle */ \ + } \ + else { \ + _idx = _i; /* remaining arena's after the cycle */ \ + } \ + name_arena = mi_arena_from_index(heap->subproc,_idx); \ + } \ + if (name_arena != NULL) \ + { + +#define mi_forall_arenas_end() \ + } \ + } \ + } + +#define mi_forall_suitable_arenas(heap, req_arena, tseq, match_numa, numa_node, allow_large, name_arena) \ + mi_forall_arenas(heap, req_arena,tseq,name_arena) { \ + if (mi_arena_is_suitable_ex(name_arena, req_arena, match_numa, numa_node, allow_large)) { \ + +#define mi_forall_suitable_arenas_end() \ + }} \ + mi_forall_arenas_end() + +/* ----------------------------------------------------------- + Arena allocation +----------------------------------------------------------- */ -void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large, - mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld) +// allocate slices from the arenas +static mi_decl_noinline void* mi_arenas_try_find_free( + mi_heap_t* heap, size_t slice_count, size_t alignment, + bool commit, bool allow_large, mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid) { - mi_assert_internal(memid != NULL && tld != NULL); - mi_assert_internal(size > 0); - *memid = _mi_memid_none(); + // mi_assert_internal(slice_count <= mi_slice_count_of_size(MI_ARENA_MAX_CHUNK_OBJ_SIZE)); + mi_assert(alignment <= MI_ARENA_SLICE_ALIGN); + if (alignment > MI_ARENA_SLICE_ALIGN) return NULL; - const int numa_node = _mi_os_numa_node(tld); // current numa node - - // try to allocate in an arena if the alignment is small enough and the object is not too small (as for heap meta data) - if (!mi_option_is_enabled(mi_option_disallow_arena_alloc) || req_arena_id != _mi_arena_id_none()) { // is arena allocation allowed? - if (size >= MI_ARENA_MIN_OBJ_SIZE && alignment <= MI_SEGMENT_ALIGN && align_offset == 0) { - void* p = mi_arena_try_alloc(numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld); - if (p != NULL) return p; - - // otherwise, try to first eagerly reserve a new arena - if (req_arena_id == _mi_arena_id_none()) { - mi_arena_id_t arena_id = 0; - if (mi_arena_reserve(size, allow_large, req_arena_id, &arena_id)) { - // and try allocate in there - mi_assert_internal(req_arena_id == _mi_arena_id_none()); - p = mi_arena_try_alloc_at_id(arena_id, true, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld); - if (p != NULL) return p; - } - } + // search arena's + mi_forall_suitable_arenas(heap, req_arena, tseq, true /* only numa matching */, numa_node, allow_large, arena) + { + void* p = mi_arena_try_alloc_at(arena, slice_count, commit, tseq, memid); + if (p != NULL) return p; + } + mi_forall_suitable_arenas_end(); + if (numa_node < 0) return NULL; + + // search again but now regardless of preferred numa affinity + mi_forall_suitable_arenas(heap, req_arena, tseq, false /* numa non-matching now */, numa_node, allow_large, arena) + { + void* p = mi_arena_try_alloc_at(arena, slice_count, commit, tseq, memid); + if (p != NULL) return p; + } + mi_forall_suitable_arenas_end(); + return NULL; +} + +// Allocate slices from the arena's -- potentially allocating a fresh arena +static mi_decl_noinline void* mi_arenas_try_alloc( + mi_heap_t* heap, + size_t slice_count, size_t alignment, + bool commit, bool allow_large, + mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid) +{ + // mi_assert(slice_count <= MI_ARENA_MAX_CHUNK_OBJ_SLICES); + mi_assert(alignment <= MI_ARENA_SLICE_ALIGN); + void* p; + + // not too large? + if (slice_count * MI_ARENA_SLICE_SIZE > MI_ARENA_MAX_SIZE) return NULL; + + // try to find free slices in the arena's + p = mi_arenas_try_find_free(heap, slice_count, alignment, commit, allow_large, req_arena, tseq, numa_node, memid); + if (p != NULL) return p; + + // did we need a specific arena? + if (req_arena != NULL) return NULL; + + // don't create arena's while preloading (todo: or should we?) + if (_mi_preloading()) return NULL; + + // don't create arena's if OS allocation is disallowed + if (mi_option_is_enabled(mi_option_disallow_os_alloc)) return NULL; + + // otherwise, try to reserve a new arena -- but one thread at a time.. (todo: allow 2 or 4 to reduce contention?) + mi_subproc_t* const subproc = heap->subproc; + const size_t arena_count = mi_arenas_get_count(subproc); + mi_lock(&subproc->arena_reserve_lock) { + if (arena_count == mi_arenas_get_count(subproc)) { + // we are the first to enter the lock, reserve a fresh arena + mi_arena_id_t arena_id = _mi_arena_id_none(); + mi_arena_reserve(subproc, mi_size_of_slices(slice_count), allow_large, &arena_id); + } + else { + // another thread already reserved a new arena } } + // try once more to allocate in the new arena + mi_assert_internal(req_arena == NULL); + p = mi_arenas_try_find_free(heap, slice_count, alignment, commit, allow_large, req_arena, tseq, numa_node, memid); + if (p != NULL) return p; + + return NULL; +} +// Allocate from the OS (if allowed) +static void* mi_arena_os_alloc_aligned( + size_t size, size_t alignment, size_t align_offset, + bool commit, bool allow_large, + mi_arena_id_t req_arena_id, mi_memid_t* memid) +{ // if we cannot use OS allocation, return NULL if (mi_option_is_enabled(mi_option_disallow_os_alloc) || req_arena_id != _mi_arena_id_none()) { errno = ENOMEM; return NULL; } - // finally, fall back to the OS if (align_offset > 0) { - return _mi_os_alloc_aligned_at_offset(size, alignment, align_offset, commit, allow_large, memid, tld->stats); + return _mi_os_alloc_aligned_at_offset(size, alignment, align_offset, commit, allow_large, memid); } else { - return _mi_os_alloc_aligned(size, alignment, commit, allow_large, memid, tld->stats); + return _mi_os_alloc_aligned(size, alignment, commit, allow_large, memid); } } -void* _mi_arena_alloc(size_t size, bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld) + +// Allocate large sized memory +void* _mi_arenas_alloc_aligned( mi_heap_t* heap, + size_t size, size_t alignment, size_t align_offset, + bool commit, bool allow_large, + mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid) { - return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, 0, commit, allow_large, req_arena_id, memid, tld); -} + mi_assert_internal(memid != NULL); + mi_assert_internal(size > 0); + // try to allocate in an arena if the alignment is small enough and the object is not too small (as for theap meta data) + if (!mi_option_is_enabled(mi_option_disallow_arena_alloc) && // is arena allocation allowed? + size >= MI_ARENA_MIN_OBJ_SIZE && size <= mi_arena_max_object_size() && // and not too small or too large + alignment <= MI_ARENA_SLICE_ALIGN && align_offset == 0) // and good alignment + { + const size_t slice_count = mi_slice_count_of_size(size); + void* p = mi_arenas_try_alloc(heap, slice_count, alignment, commit, allow_large, req_arena, tseq, numa_node, memid); + if (p != NULL) return p; + } -void* mi_arena_area(mi_arena_id_t arena_id, size_t* size) { - if (size != NULL) *size = 0; - size_t arena_index = mi_arena_id_index(arena_id); - if (arena_index >= MI_MAX_ARENAS) return NULL; - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_index]); - if (arena == NULL) return NULL; - if (size != NULL) { *size = mi_arena_block_size(arena->block_count); } - return arena->start; + // fall back to the OS + void* p = mi_arena_os_alloc_aligned(size, alignment, align_offset, commit, allow_large, req_arena, memid); + return p; +} + +void* _mi_arenas_alloc(mi_heap_t* heap, size_t size, bool commit, bool allow_large, mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid) +{ + return _mi_arenas_alloc_aligned(heap, size, MI_ARENA_SLICE_SIZE, 0, commit, allow_large, req_arena, tseq, numa_node, memid); } + /* ----------------------------------------------------------- - Arena purge + Arena page allocation ----------------------------------------------------------- */ -static long mi_arena_purge_delay(void) { - // <0 = no purging allowed, 0=immediate purging, >0=milli-second delay - return (mi_option_get(mi_option_purge_delay) * mi_option_get(mi_option_arena_purge_mult)); +// release ownership of a page. This may free the page if all blocks were concurrently +// freed in the meantime. Returns true if the page was freed. +static bool mi_abandoned_page_unown(mi_page_t* page, mi_theap_t* current_theap) { + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(_mi_thread_id()==current_theap->tld->thread_id); + mi_thread_free_t tf_new; + mi_thread_free_t tf_old = mi_atomic_load_relaxed(&page->xthread_free); + do { + mi_assert_internal(mi_tf_is_owned(tf_old)); + while mi_unlikely(mi_tf_block(tf_old) != NULL) { + _mi_page_free_collect(page, false); // update used + if (mi_page_all_free(page)) { // it may become free just before unowning it + _mi_arenas_page_unabandon(page, current_theap); + _mi_arenas_page_free(page, current_theap); + return true; + } + tf_old = mi_atomic_load_relaxed(&page->xthread_free); + } + mi_assert_internal(mi_tf_block(tf_old)==NULL); + tf_new = mi_tf_create(NULL, false); + } while (!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tf_old, tf_new)); + return false; } -// reset or decommit in an arena and update the committed/decommit bitmaps -// assumes we own the area (i.e. blocks_in_use is claimed by us) -static void mi_arena_purge(mi_arena_t* arena, size_t bitmap_idx, size_t blocks, mi_stats_t* stats) { - mi_assert_internal(arena->blocks_committed != NULL); - mi_assert_internal(arena->blocks_purge != NULL); - mi_assert_internal(!arena->memid.is_pinned); - const size_t size = mi_arena_block_size(blocks); - void* const p = mi_arena_block_start(arena, bitmap_idx); - bool needs_recommit; - if (_mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx)) { - // all blocks are committed, we can purge freely - needs_recommit = _mi_os_purge(p, size, stats); + +static bool mi_arena_try_claim_abandoned(size_t slice_index, mi_arena_t* arena, bool* keep_abandoned) { + // found an abandoned page of the right size + mi_page_t* const page = mi_arena_page_at_slice(arena, slice_index); + // can we claim ownership? + if (!mi_page_claim_ownership(page)) { + // there was a concurrent free that reclaims this page .. + // we need to keep it in the abandoned map as the free will call `mi_arena_page_unabandon`, + // and wait for readers (us!) to finish. This is why it is very important to set the abandoned + // bit again (or otherwise the unabandon will never stop waiting). + *keep_abandoned = true; + return false; } else { - // some blocks are not committed -- this can happen when a partially committed block is freed - // in `_mi_arena_free` and it is conservatively marked as uncommitted but still scheduled for a purge - // we need to ensure we do not try to reset (as that may be invalid for uncommitted memory), - // and also undo the decommit stats (as it was already adjusted) - mi_assert_internal(mi_option_is_enabled(mi_option_purge_decommits)); - needs_recommit = _mi_os_purge_ex(p, size, false /* allow reset? */, stats); - if (needs_recommit) { _mi_stat_increase(&_mi_stats_main.committed, size); } - } - - // clear the purged blocks - _mi_bitmap_unclaim_across(arena->blocks_purge, arena->field_count, blocks, bitmap_idx); - // update committed bitmap - if (needs_recommit) { - _mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx); + // yes, we can reclaim it, keep the abandoned map entry clear + *keep_abandoned = false; + return true; } } -// Schedule a purge. This is usually delayed to avoid repeated decommit/commit calls. -// Note: assumes we (still) own the area as we may purge immediately -static void mi_arena_schedule_purge(mi_arena_t* arena, size_t bitmap_idx, size_t blocks, mi_stats_t* stats) { - mi_assert_internal(arena->blocks_purge != NULL); - const long delay = mi_arena_purge_delay(); - if (delay < 0) return; // is purging allowed at all? +// allocate initial arena_pages from the main heap +static mi_arena_pages_t* mi_arena_pages_alloc(mi_arena_t* arena); - if (_mi_preloading() || delay == 0) { - // decommit directly - mi_arena_purge(arena, bitmap_idx, blocks, stats); - } - else { - // schedule decommit - mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire); - if (expire != 0) { - mi_atomic_addi64_acq_rel(&arena->purge_expire, (mi_msecs_t)(delay/10)); // add smallish extra delay - } - else { - mi_atomic_storei64_release(&arena->purge_expire, _mi_clock_now() + delay); - } - _mi_bitmap_claim_across(arena->blocks_purge, arena->field_count, blocks, bitmap_idx, NULL); - } -} - -// purge a range of blocks -// return true if the full range was purged. -// assumes we own the area (i.e. blocks_in_use is claimed by us) -static bool mi_arena_purge_range(mi_arena_t* arena, size_t idx, size_t startidx, size_t bitlen, size_t purge, mi_stats_t* stats) { - const size_t endidx = startidx + bitlen; - size_t bitidx = startidx; - bool all_purged = false; - while (bitidx < endidx) { - // count consequetive ones in the purge mask - size_t count = 0; - while (bitidx + count < endidx && (purge & ((size_t)1 << (bitidx + count))) != 0) { - count++; - } - if (count > 0) { - // found range to be purged - const mi_bitmap_index_t range_idx = mi_bitmap_index_create(idx, bitidx); - mi_arena_purge(arena, range_idx, count, stats); - if (count == bitlen) { - all_purged = true; - } - } - bitidx += (count+1); // +1 to skip the zero bit (or end) +static mi_arena_pages_t* mi_heap_arena_pages(mi_heap_t* heap, mi_arena_t* arena) { + mi_assert_internal(arena!=NULL); + mi_assert_internal(heap!=NULL); + mi_assert(arena->arena_idx < MI_MAX_ARENAS); + return mi_atomic_load_ptr_acquire(mi_arena_pages_t, &heap->arena_pages[arena->arena_idx]); +} + +static mi_arena_t* mi_page_arena_pages(mi_page_t* page, size_t* slice_index, size_t* slice_count, mi_arena_pages_t** parena_pages) { + // todo: maybe store the arena* directly in the page? + mi_assert_internal(mi_page_is_owned(page)); + mi_arena_t* const arena = mi_arena_from_memid(page->memid, slice_index, slice_count); + mi_assert_internal(arena != NULL); + if (parena_pages != NULL) { + mi_arena_pages_t* const arena_pages = mi_heap_arena_pages(mi_page_heap(page), arena); + mi_assert_internal(arena_pages != NULL); + mi_assert_internal(slice_index==NULL || mi_bitmap_is_set(arena_pages->pages, *slice_index)); + *parena_pages = arena_pages; } - return all_purged; + return arena; } -// returns true if anything was purged -static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force, mi_stats_t* stats) -{ - if (arena->memid.is_pinned || arena->blocks_purge == NULL) return false; - mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire); - if (expire == 0) return false; - if (!force && expire > now) return false; - - // reset expire (if not already set concurrently) - mi_atomic_casi64_strong_acq_rel(&arena->purge_expire, &expire, (mi_msecs_t)0); - - // potential purges scheduled, walk through the bitmap - bool any_purged = false; - bool full_purge = true; - for (size_t i = 0; i < arena->field_count; i++) { - size_t purge = mi_atomic_load_relaxed(&arena->blocks_purge[i]); - if (purge != 0) { - size_t bitidx = 0; - while (bitidx < MI_BITMAP_FIELD_BITS) { - // find consequetive range of ones in the purge mask - size_t bitlen = 0; - while (bitidx + bitlen < MI_BITMAP_FIELD_BITS && (purge & ((size_t)1 << (bitidx + bitlen))) != 0) { - bitlen++; - } - // try to claim the longest range of corresponding in_use bits - const mi_bitmap_index_t bitmap_index = mi_bitmap_index_create(i, bitidx); - while( bitlen > 0 ) { - if (_mi_bitmap_try_claim(arena->blocks_inuse, arena->field_count, bitlen, bitmap_index)) { - break; - } - bitlen--; +static mi_arena_pages_t* mi_heap_ensure_arena_pages(mi_heap_t* heap, mi_arena_t* arena) { + mi_assert_internal(arena!=NULL); + mi_assert_internal(heap!=NULL); + mi_assert(arena->arena_idx < MI_MAX_ARENAS); + mi_arena_pages_t* arena_pages = mi_heap_arena_pages(heap, arena); + if (arena_pages==NULL) { + mi_lock(&heap->arena_pages_lock) { + arena_pages = mi_atomic_load_ptr_acquire(mi_arena_pages_t, &heap->arena_pages[arena->arena_idx]); + if (arena_pages == NULL) { // still NULL? + if (_mi_is_heap_main(heap)) { + // the page info for the main heap is always allocated as part of an arena + arena_pages = &arena->pages_main; } - // actual claimed bits at `in_use` - if (bitlen > 0) { - // read purge again now that we have the in_use bits - purge = mi_atomic_load_acquire(&arena->blocks_purge[i]); - if (!mi_arena_purge_range(arena, i, bitidx, bitlen, purge, stats)) { - full_purge = false; - } - any_purged = true; - // release the claimed `in_use` bits again - _mi_bitmap_unclaim(arena->blocks_inuse, arena->field_count, bitlen, bitmap_index); + else { + // always allocate the arena pages info from the main heap + // todo: allocate into the current arena? + arena_pages = mi_arena_pages_alloc(arena); } - bitidx += (bitlen+1); // +1 to skip the zero (or end) - } // while bitidx - } // purge != 0 - } - // if not fully purged, make sure to purge again in the future - if (!full_purge) { - const long delay = mi_arena_purge_delay(); - mi_msecs_t expected = 0; - mi_atomic_casi64_strong_acq_rel(&arena->purge_expire,&expected,_mi_clock_now() + delay); + mi_atomic_store_ptr_release(mi_arena_pages_t, &heap->arena_pages[arena->arena_idx], arena_pages); + } + } } - return any_purged; + if (_mi_is_heap_main(heap)) { mi_assert(arena_pages != NULL); } // can never fail + return arena_pages; } -static void mi_arenas_try_purge( bool force, bool visit_all, mi_stats_t* stats ) { - if (_mi_preloading() || mi_arena_purge_delay() <= 0) return; // nothing will be scheduled +static mi_page_t* mi_arenas_page_try_find_abandoned(mi_theap_t* theap, size_t slice_count, size_t block_size) +{ + mi_heap_t* const heap = _mi_theap_heap(theap); + const size_t tseq = theap->tld->thread_seq; + mi_arena_t* const req_arena = heap->exclusive_arena; + + MI_UNUSED(slice_count); + const size_t bin = _mi_bin(block_size); + if (bin >= MI_ARENA_BIN_COUNT) { + return NULL; // singleton page size + } - const size_t max_arena = mi_atomic_load_acquire(&mi_arena_count); - if (max_arena == 0) return; + // any abandoned in our size class? + mi_assert_internal(heap != NULL); + if (mi_atomic_load_relaxed(&heap->abandoned_count[bin]) == 0) { + return NULL; + } - // allow only one thread to purge at a time - static mi_atomic_guard_t purge_guard; - mi_atomic_guard(&purge_guard) + // search arena's + const bool allow_large = true; + const int any_numa = -1; + const bool match_numa = true; + mi_forall_suitable_arenas(heap, req_arena, tseq, match_numa, any_numa, allow_large, arena) { - mi_msecs_t now = _mi_clock_now(); - size_t max_purge_count = (visit_all ? max_arena : 1); - for (size_t i = 0; i < max_arena; i++) { - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[i]); - if (arena != NULL) { - if (mi_arena_try_purge(arena, now, force, stats)) { - if (max_purge_count <= 1) break; - max_purge_count--; - } + mi_arena_pages_t* const arena_pages = mi_heap_arena_pages(heap, arena); + if (arena_pages != NULL) { + size_t slice_index; + mi_bitmap_t* const bitmap = arena_pages->pages_abandoned[bin]; + + if (mi_bitmap_try_find_and_claim(bitmap, tseq, &slice_index, &mi_arena_try_claim_abandoned, arena)) { + // found an abandoned page of the right size + // and claimed ownership. + mi_page_t* page = mi_arena_page_at_slice(arena, slice_index); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(mi_heap_has_page(heap, arena, page)); + mi_atomic_decrement_relaxed(&heap->abandoned_count[bin]); + mi_theap_stat_decrease(theap, pages_abandoned, 1); + mi_theap_stat_counter_increase(theap, pages_reclaim_on_alloc, 1); + + _mi_page_free_collect(page, false); // update `used` count + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + mi_assert_internal(page->slice_committed > 0 || mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); + mi_assert_internal(mi_bitmap_is_setN(arena->slices_dirty, slice_index, slice_count)); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_block_size(page) == block_size); + mi_assert_internal(!mi_page_is_full(page)); + return page; } } } + mi_forall_suitable_arenas_end(); + return NULL; } +static uint8_t* mi_arenas_page_alloc_fresh_area(mi_theap_t* theap, size_t slice_count, size_t block_size, size_t block_alignment, bool os_align, bool commit, mi_memid_t* memid, mi_arena_pages_t** parena_pages ) { + MI_UNUSED_RELEASE(block_size); + mi_assert_internal(parena_pages!=NULL); -/* ----------------------------------------------------------- - Arena free ------------------------------------------------------------ */ + *parena_pages = NULL; + const bool allow_large = (MI_SECURE < 5); // 5 = guard page at end of each arena page + const size_t page_alignment = MI_ARENA_SLICE_ALIGN; -void _mi_arena_free(void* p, size_t size, size_t committed_size, mi_memid_t memid, mi_stats_t* stats) { - mi_assert_internal(size > 0 && stats != NULL); - mi_assert_internal(committed_size <= size); - if (p==NULL) return; - if (size==0) return; - const bool all_committed = (committed_size == size); + mi_heap_t* const heap = _mi_theap_heap(theap); + mi_tld_t* const tld = theap->tld; + mi_arena_t* const req_arena = heap->exclusive_arena; + const int numa_node = (heap->numa_node >= 0 ? heap->numa_node : tld->numa_node); - if (mi_memkind_is_os(memid.memkind)) { - // was a direct OS allocation, pass through - if (!all_committed && committed_size > 0) { - // if partially committed, adjust the committed stats (as `_mi_os_free` will increase decommit by the full size) - _mi_stat_decrease(&_mi_stats_main.committed, committed_size); + // try to allocate from free space in arena's + uint8_t* start = NULL; + *memid = _mi_memid_none(); + const size_t alloc_size = mi_size_of_slices(slice_count); + if (!mi_option_is_enabled(mi_option_disallow_arena_alloc) && // allowed to allocate from arena's? + !os_align && // not large alignment + slice_count <= mi_arena_max_object_size()/MI_ARENA_SLICE_SIZE) // and not too large + { + start = (uint8_t*)mi_arenas_try_alloc(heap, slice_count, page_alignment, commit, allow_large, req_arena, tld->thread_seq, numa_node, memid); + if (start != NULL) { + mi_arena_pages_t* const arena_pages = mi_heap_ensure_arena_pages(heap, memid->mem.arena.arena); + *parena_pages = arena_pages; + if (arena_pages==NULL) { + _mi_arenas_free(start, mi_size_of_slices(slice_count), *memid); // roll back + start = NULL; + } + else { + // note: the following assert should hold if we could check it atomically, but in a concurrent setting we may already allocate in slice_count + // mi_assert_internal(mi_bitmap_is_clearN(arena_pages->pages, memid->mem.arena.slice_index, memid->mem.arena.slice_count)); + mi_assert_internal(mi_bitmap_is_clear(arena_pages->pages, memid->mem.arena.slice_index)); + // don't set yet: mi_bitmap_set(arena_pages->pages, memid->mem.arena.slice_index); + } } - _mi_os_free(p, size, memid, stats); } - else if (memid.memkind == MI_MEM_ARENA) { - // allocated in an arena - size_t arena_idx; - size_t bitmap_idx; - mi_arena_memid_indices(memid, &arena_idx, &bitmap_idx); - mi_assert_internal(arena_idx < MI_MAX_ARENAS); - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t,&mi_arenas[arena_idx]); - mi_assert_internal(arena != NULL); - const size_t blocks = mi_block_count_of_size(size); - - // checks - if (arena == NULL) { - _mi_error_message(EINVAL, "trying to free from an invalid arena: %p, size %zu, memid: 0x%zx\n", p, size, memid); - return; - } - mi_assert_internal(arena->field_count > mi_bitmap_index_field(bitmap_idx)); - if (arena->field_count <= mi_bitmap_index_field(bitmap_idx)) { - _mi_error_message(EINVAL, "trying to free from an invalid arena block: %p, size %zu, memid: 0x%zx\n", p, size, memid); - return; - } - - // need to set all memory to undefined as some parts may still be marked as no_access (like padding etc.) - mi_track_mem_undefined(p,size); - // potentially decommit - if (arena->memid.is_pinned || arena->blocks_committed == NULL) { - mi_assert_internal(all_committed); + // otherwise fall back to the OS + if (start == NULL) { + if (os_align) { + // note: slice_count already includes the page + mi_assert_internal(slice_count >= mi_slice_count_of_size(block_size) + mi_slice_count_of_size(page_alignment)); + start = (uint8_t*)mi_arena_os_alloc_aligned(alloc_size, block_alignment, page_alignment /* align offset */, commit, allow_large, req_arena, memid); } else { - mi_assert_internal(arena->blocks_committed != NULL); - mi_assert_internal(arena->blocks_purge != NULL); - - if (!all_committed) { - // mark the entire range as no longer committed (so we recommit the full range when re-using) - _mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx); - mi_track_mem_noaccess(p,size); - if (committed_size > 0) { - // if partially committed, adjust the committed stats (is it will be recommitted when re-using) - // in the delayed purge, we now need to not count a decommit if the range is not marked as committed. - _mi_stat_decrease(&_mi_stats_main.committed, committed_size); - } - // note: if not all committed, it may be that the purge will reset/decommit the entire range - // that contains already decommitted parts. Since purge consistently uses reset or decommit that - // works (as we should never reset decommitted parts). - } - // (delay) purge the entire range - mi_arena_schedule_purge(arena, bitmap_idx, blocks, stats); + start = (uint8_t*)mi_arena_os_alloc_aligned(alloc_size, page_alignment, 0 /* align offset */, commit, allow_large, req_arena, memid); } - - // and make it available to others again - bool all_inuse = _mi_bitmap_unclaim_across(arena->blocks_inuse, arena->field_count, blocks, bitmap_idx); - if (!all_inuse) { - _mi_error_message(EAGAIN, "trying to free an already freed arena block: %p, size %zu\n", p, size); - return; - }; - } - else { - // arena was none, external, or static; nothing to do - mi_assert_internal(memid.memkind < MI_MEM_OS); } - // purge expired decommits - mi_arenas_try_purge(false, false, stats); + if (start == NULL) return NULL; + mi_assert_internal(_mi_is_aligned(start, MI_PAGE_ALIGN)); + mi_assert_internal(!os_align || _mi_is_aligned(start + page_alignment, block_alignment)); + return start; } -// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit` -// for dynamic libraries that are unloaded and need to release all their allocated memory. -static void mi_arenas_unsafe_destroy(void) { - const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count); - size_t new_max_arena = 0; - for (size_t i = 0; i < max_arena; i++) { - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[i]); - if (arena != NULL) { - if (arena->start != NULL && mi_memkind_is_os(arena->memid.memkind)) { - mi_atomic_store_ptr_release(mi_arena_t, &mi_arenas[i], NULL); - _mi_os_free(arena->start, mi_arena_size(arena), arena->memid, &_mi_stats_main); - } - else { - new_max_arena = i; +static size_t mi_page_block_start(size_t block_size, bool os_align) +{ + #if MI_GUARDED + // in a guarded build, we align pages with blocks a multiple of an OS page size, to the OS page size + // this ensures that all blocks in such pages are OS page size aligned (which is needed for the guard pages) + const size_t os_page_size = _mi_os_page_size(); + mi_assert_internal(MI_PAGE_ALIGN >= os_page_size); + if (!os_align && block_size % os_page_size == 0 && block_size > os_page_size /* at least 2 or more */ ) { + return _mi_align_up(mi_page_info_size(), os_page_size); + } + else + #endif + if (os_align) { + return MI_PAGE_ALIGN; + } + else if (_mi_is_power_of_two(block_size) && block_size <= MI_PAGE_MAX_START_BLOCK_ALIGN2) { + // naturally align power-of-2 blocks up to MI_PAGE_MAX_START_BLOCK_ALIGN2 size (4KiB) + return _mi_align_up(mi_page_info_size(), block_size); + } + else if (block_size != 0 && (block_size % MI_PAGE_OSPAGE_BLOCK_ALIGN2) == 0) { + // also align large pages that are a multiple of MI_PAGE_OSPAGE_BLOCK_ALIGN2 (4KiB) + return _mi_align_up(mi_page_info_size(), MI_PAGE_OSPAGE_BLOCK_ALIGN2); + } + else { + // otherwise start after the info + return mi_page_info_size(); + } +} + +// Free a page without modifying page_bin stats +static void mi_arenas_page_free_prim(mi_page_t* page); + +// Allocate a fresh page +static mi_page_t* mi_arenas_page_alloc_fresh(mi_theap_t* theap, size_t slice_count, size_t block_size, size_t block_alignment, bool commit) +{ + const bool os_align = (block_alignment > MI_PAGE_MAX_OVERALLOC_ALIGN); + const size_t alloc_size = mi_size_of_slices(slice_count); + mi_memid_t memid = _mi_memid_none(); + mi_arena_pages_t* arena_pages = NULL; + uint8_t* const slice_start = mi_arenas_page_alloc_fresh_area(theap,slice_count,block_size,block_alignment,os_align,commit,&memid,&arena_pages); + if (!slice_start) return NULL; + + // guard page at the end of mimalloc page? + #if MI_SECURE>=5 + mi_assert(alloc_size > _mi_os_secure_guard_page_size()); + const size_t page_noguard_size = alloc_size - _mi_os_secure_guard_page_size(); + #else + const size_t page_noguard_size = alloc_size; + #endif + + // allocate the page meta info + mi_page_t* page = NULL; + bool page_meta_is_separate = false; + size_t block_start = 0; + + // allocate page meta info at the arena start? + if (memid.memkind == MI_MEM_ARENA) { + mi_arena_t* const arena = memid.mem.arena.arena; + if (arena->pages_meta != NULL) { + mi_assert_internal(MI_PAGE_META_IS_SEPARATED!=0); + mi_page_t* const page_meta = &arena->pages_meta[memid.mem.arena.slice_index]; + mi_assert_internal(page_meta->block_size == 0); + #if MI_PAGE_META_ALIGNED_FREE_SMALL + // if `block_size <= MI_SMALL_SIZE_MAX` we put the page info in front of the slice, + // (note: it is important that `page_meta->block_size == 0` for `mi_arena_page_at_slice`) + if (block_size > MI_SMALL_SIZE_MAX) + #endif + { + page = page_meta; + page_meta_is_separate = true; + block_start = 0; + #if !defined(MI_PAGE_BLOCK_START_MAX_OFFSET) + #define MI_PAGE_BLOCK_START_MAX_OFFSET (8*MI_INTPTR_BITS) /* 512 */ + #endif + if (block_size >= MI_INTPTR_SIZE && block_size <= MI_PAGE_BLOCK_START_MAX_OFFSET && _mi_is_power_of_two(block_size)) { + block_start += block_size; + } + mi_assert_internal(page->block_size == 0); + _mi_memzero_aligned(page, sizeof(*page)); } - mi_arena_meta_free(arena, arena->meta_memid, arena->meta_size, &_mi_stats_main); } } + if (page == NULL) { + // put page meta info in front of the slice + page = (mi_page_t*)slice_start; + block_start = mi_page_block_start(block_size, os_align); + } - // try to lower the max arena. - size_t expected = max_arena; - mi_atomic_cas_strong_acq_rel(&mi_arena_count, &expected, new_max_arena); + // commit first block? + size_t commit_size = 0; + if (!memid.initially_committed) { + commit_size = _mi_align_up(block_start + block_size, MI_PAGE_MIN_COMMIT_SIZE); + if (commit_size > page_noguard_size) { commit_size = page_noguard_size; } + bool is_zero = false; + if mi_unlikely(!mi_arena_commit( mi_memid_arena(memid), slice_start, commit_size, &is_zero, 0)) { + _mi_arenas_free(slice_start, alloc_size, memid); + return NULL; + } + } + // now we can finish initalization and use `mi_arenas_free_page_prim` on error + + // zero initialize the page meta data + if (!memid.initially_zero && !page_meta_is_separate) { + _mi_memzero_aligned(page, sizeof(*page)); + } + + // set the guard page + #if MI_SECURE>=5 + if (memid.initially_committed) { + _mi_os_secure_guard_page_set_at(slice_start + page_noguard_size, memid); + } + #endif + + // claimed free slices: initialize the page partly + if (!memid.initially_zero && memid.initially_committed) { + mi_track_mem_undefined(slice_start, slice_count * MI_ARENA_SLICE_SIZE); + } + else if (memid.initially_committed) { + mi_track_mem_defined(slice_start, slice_count * MI_ARENA_SLICE_SIZE); + } + #if MI_DEBUG > 1 + if (memid.initially_zero && memid.initially_committed) { + if (!mi_mem_is_zero(slice_start, page_noguard_size)) { + _mi_error_message(EFAULT, "internal error: page memory was not zero initialized.\n"); + memid.initially_zero = false; + if (block_start > 0) { _mi_memzero_aligned(page, sizeof(*page)); } + } + } + #endif + const size_t reserved = (os_align ? 1 : (page_noguard_size - block_start) / block_size); + mi_assert_internal(reserved > 0 && reserved <= UINT16_MAX); + + // initialize the page start + uint8_t* const start = slice_start + block_start; + mi_assert_internal(start > (uint8_t*)page); + const size_t offset = start - (uint8_t*)page; + mi_assert_internal((offset % MI_SIZE_SIZE) == 0 && (offset / MI_SIZE_SIZE) < UINT32_MAX); + page->page_woffset = (uint32_t)(offset / MI_SIZE_SIZE); + + // initialize page meta-data + page->reserved = (uint16_t)reserved; + page->block_size = block_size; + page->memid = memid; + page->free_is_zero = memid.initially_zero; + + mi_assert_internal((commit && commit_size==0) || (!commit && commit_size < UINT32_MAX)); + page->slice_committed = (uint32_t)commit_size; + + mi_assert_internal(page->free==NULL); + mi_assert_internal(page_meta_is_separate == mi_page_meta_is_separated(page)); + mi_assert_internal(mi_page_slice_start(page) == slice_start); + + // now register in the arena_pages + if (arena_pages!=NULL) { + mi_assert_internal(memid.memkind == MI_MEM_ARENA); + mi_bitmap_set(arena_pages->pages, memid.mem.arena.slice_index); + } + + // and own it + mi_page_claim_ownership(page); + + // register in the page map + if mi_unlikely(!_mi_page_map_register(page)) { + mi_arenas_page_free_prim(page); + return NULL; + } + + // stats + mi_theap_stat_increase(theap, pages, 1); + mi_theap_stat_increase(theap, page_bins[_mi_page_stats_bin(page)], 1); + + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page),MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_block_size(page) == block_size); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(mi_page_is_owned(page)); + + return page; } -// Purge the arenas; if `force_purge` is true, amenable parts are purged even if not yet expired -void _mi_arenas_collect(bool force_purge, mi_stats_t* stats) { - mi_arenas_try_purge(force_purge, force_purge /* visit all? */, stats); +// Allocate a regular small/medium/large page. +static mi_page_t* mi_arenas_page_regular_alloc(mi_theap_t* theap, size_t slice_count, size_t block_size) +{ + // 1. look for an abandoned page + mi_page_t* page = mi_arenas_page_try_find_abandoned(theap, slice_count, block_size); + if (page != NULL) { + return page; // return as abandoned + } + + // 2. find a free block, potentially allocating a new arena + const long commit_on_demand = mi_option_get(mi_option_page_commit_on_demand); + const bool commit = (slice_count <= mi_slice_count_of_size(MI_PAGE_MIN_COMMIT_SIZE) || // always commit small pages + (slice_count >= mi_slice_count_of_size(UINT32_MAX)) || // always commit pages too large to hold a 32-bit slice_committed + (commit_on_demand == 2 && _mi_os_has_overcommit()) || (commit_on_demand == 0)); + page = mi_arenas_page_alloc_fresh(theap, slice_count, block_size, 1, commit); + if (page == NULL) return NULL; + + mi_assert_internal(page->memid.memkind != MI_MEM_ARENA || page->memid.mem.arena.slice_count == slice_count); + if (!_mi_page_init(theap, page)) { + mi_arenas_page_free_prim(page); + return NULL; + } + + return page; } -// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit` -// for dynamic libraries that are unloaded and need to release all their allocated memory. -void _mi_arena_unsafe_destroy_all(mi_stats_t* stats) { - mi_arenas_unsafe_destroy(); - _mi_arenas_collect(true /* force purge */, stats); // purge non-owned arenas +// Allocate a page containing one block (very large, or with large alignment) +static mi_page_t* mi_arenas_page_singleton_alloc(mi_theap_t* theap, size_t block_size, size_t block_alignment) +{ + const bool os_align = (block_alignment > MI_PAGE_MAX_OVERALLOC_ALIGN); + const size_t info_size = (os_align ? MI_PAGE_ALIGN : mi_page_info_size()); + #if MI_SECURE < 2 + const size_t slice_count = mi_slice_count_of_size(info_size + block_size); + #else + const size_t slice_count = mi_slice_count_of_size(_mi_align_up(info_size + block_size, _mi_os_secure_guard_page_size()) + _mi_os_secure_guard_page_size()); + #endif + + mi_page_t* page = mi_arenas_page_alloc_fresh(theap, slice_count, block_size, block_alignment, true /* commit singletons always */); + if (page == NULL) return NULL; + + mi_assert(page->reserved == 1); + if (!_mi_page_init(theap, page)) { + _mi_arenas_free( page, mi_page_full_size(page), page->memid); + return NULL; + } + + return page; } -// Is a pointer inside any of our arenas? -bool _mi_arena_contains(const void* p) { - const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count); - for (size_t i = 0; i < max_arena; i++) { - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[i]); - if (arena != NULL && arena->start <= (const uint8_t*)p && arena->start + mi_arena_block_size(arena->block_count) > (const uint8_t*)p) { - return true; + +mi_page_t* _mi_arenas_page_alloc(mi_theap_t* theap, size_t block_size, size_t block_alignment) { + mi_page_t* page; + // semi static assert: ensure that all non-singleton block size bins are covered. + mi_assert(_mi_bin(MI_LARGE_MAX_OBJ_SIZE) < MI_ARENA_BIN_COUNT); + if mi_unlikely(block_alignment > MI_PAGE_MAX_OVERALLOC_ALIGN) { + mi_assert_internal(_mi_is_power_of_two(block_alignment)); + page = mi_arenas_page_singleton_alloc(theap, block_size, block_alignment); + } + else if (block_size <= MI_SMALL_MAX_OBJ_SIZE) { + page = mi_arenas_page_regular_alloc(theap, mi_slice_count_of_size(MI_SMALL_PAGE_SIZE), block_size); + } + else if (block_size <= MI_MEDIUM_MAX_OBJ_SIZE) { + page = mi_arenas_page_regular_alloc(theap, mi_slice_count_of_size(MI_MEDIUM_PAGE_SIZE), block_size); + } + #if MI_ENABLE_LARGE_PAGES + else if (block_size <= MI_LARGE_MAX_OBJ_SIZE) { + page = mi_arenas_page_regular_alloc(theap, mi_slice_count_of_size(MI_LARGE_PAGE_SIZE), block_size); + } + #endif + else { + page = mi_arenas_page_singleton_alloc(theap, block_size, block_alignment); + } + if mi_unlikely(page == NULL) { + return NULL; + } + // mi_assert_internal(page == NULL || _mi_page_segment(page)->subproc == tld->subproc); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(block_alignment <= MI_PAGE_MAX_OVERALLOC_ALIGN || _mi_is_aligned(mi_page_start(page), block_alignment)); + + return page; +} + +static void mi_arenas_page_free_prim(mi_page_t* page) { + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_all_free(page)); + mi_assert_internal(page->next==NULL && page->prev==NULL); + + #if MI_DEBUG>1 + if (page->memid.memkind==MI_MEM_ARENA && !mi_page_is_full(page)) { + size_t bin = _mi_bin(mi_page_block_size(page)); + size_t slice_index; + size_t slice_count; + mi_arena_pages_t* arena_pages = NULL; + mi_arena_t* const arena = mi_page_arena_pages(page, &slice_index, &slice_count, &arena_pages); + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + mi_assert_internal(page->slice_committed > 0 || mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); + mi_assert_internal(bin >= MI_ARENA_BIN_COUNT || mi_bitmap_is_clearN(arena_pages->pages_abandoned[bin], slice_index, 1)); + mi_assert_internal(mi_bitmap_is_setN(arena_pages->pages, slice_index, 1)); + // note: we cannot check for `!mi_page_is_abandoned_and_mapped` since that may + // be (temporarily) not true if the free happens while trying to reclaim + // see `mi_arena_try_claim_abandoned` + } + #endif + + // unregister page + _mi_page_map_unregister(page); + + // recommit guard page at the end? + // we must do this since we may later allocate large spans over this page and cannot have a guard page in between + #if MI_SECURE >= 5 + if (!page->memid.is_pinned) { + _mi_os_secure_guard_page_reset_before(mi_page_slice_start(page) + mi_page_full_size(page), page->memid); + } + #endif + + // and free + if (page->memid.memkind == MI_MEM_ARENA) { + mi_arena_pages_t* arena_pages; + size_t slice_index; + size_t slice_count; MI_UNUSED(slice_count); + mi_arena_t* const arena = mi_page_arena_pages(page, &slice_index, &slice_count, &arena_pages); + mi_assert_internal(arena_pages!=NULL); + mi_bitmap_clear(arena_pages->pages, slice_index); + if (page->slice_committed > 0) { + // if committed on-demand, set the commit bits to account commit properly + mi_assert_internal(mi_page_full_size(page) >= page->slice_committed); + const size_t total_slices = page->slice_committed / MI_ARENA_SLICE_SIZE; // conservative + //mi_assert_internal(mi_bitmap_is_clearN(arena->slices_committed, slice_index, total_slices)); + mi_assert_internal(slice_count >= total_slices); + if (total_slices > 0) { + mi_bitmap_setN(arena->slices_committed, slice_index, total_slices, NULL); + } + // any left over? + const size_t extra = page->slice_committed % MI_ARENA_SLICE_SIZE; + if (extra > 0) { + // pretend it was decommitted already + mi_subproc_stat_decrease(arena->subproc, committed, extra); + } + } + else { + mi_assert_internal(mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); } } - return false; + if (mi_page_meta_is_separated(page)) { page->block_size = 0; } // for assertion checking + _mi_arenas_free( mi_page_slice_start(page), mi_page_full_size(page), page->memid); +} + +void _mi_arenas_page_free(mi_page_t* page, mi_theap_t* current_theapx) { + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_all_free(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(page->next==NULL && page->prev==NULL); + mi_assert_internal(current_theapx == NULL || _mi_thread_id()==current_theapx->tld->thread_id); + + if (current_theapx != NULL) { + mi_theap_stat_decrease(current_theapx, page_bins[_mi_page_stats_bin(page)], 1); + mi_theap_stat_decrease(current_theapx, pages, 1); + } + else { + mi_heap_t* const heap = mi_page_heap(page); + mi_heap_stat_decrease(heap, page_bins[_mi_page_stats_bin(page)], 1); + mi_heap_stat_decrease(heap, pages, 1); + } + mi_arenas_page_free_prim(page); } /* ----------------------------------------------------------- - Abandoned blocks/segments. - This is used to atomically abandon/reclaim segments - (and crosses the arena API but it is convenient to have here). - Abandoned segments still have live blocks; they get reclaimed - when a thread frees a block in it, or when a thread needs a fresh - segment; these threads scan the abandoned segments through - the arena bitmaps. + Arena abandon ----------------------------------------------------------- */ -// Maintain a count of all abandoned segments -static mi_decl_cache_align _Atomic(size_t)abandoned_count; - -size_t _mi_arena_segment_abandoned_count(void) { - return mi_atomic_load_relaxed(&abandoned_count); +void _mi_arenas_page_abandon(mi_page_t* page, mi_theap_t* current_theap) { + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(!mi_page_all_free(page)); + mi_assert_internal(page->next==NULL && page->prev == NULL); + mi_assert_internal(_mi_thread_id()==current_theap->tld->thread_id); + // mi_assert_internal(current_theap == _mi_page_associated_theap(page)); + + // add to abandoned? + mi_heap_t* heap = mi_page_heap(page); mi_assert_internal(heap==_mi_theap_heap(current_theap)); + if (page->memid.memkind==MI_MEM_ARENA && !mi_page_is_full(page)) { + // make available for allocations + size_t bin = _mi_bin(mi_page_block_size(page)); + mi_assert_internal(bin < MI_ARENA_BIN_COUNT); + if (bin < MI_ARENA_BIN_COUNT) { // paranoia + size_t slice_index; + size_t slice_count; + mi_arena_pages_t* arena_pages = NULL; + mi_arena_t* const arena = mi_page_arena_pages(page, &slice_index, &slice_count, &arena_pages); MI_UNUSED(arena); + + mi_assert_internal(!mi_page_is_singleton(page)); + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + mi_assert_internal(page->slice_committed > 0 || mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); + mi_assert_internal(mi_bitmap_is_setN(arena->slices_dirty, slice_index, slice_count)); + + mi_page_set_abandoned_mapped(page); + const bool was_clear = mi_bitmap_set(arena_pages->pages_abandoned[bin], slice_index); + MI_UNUSED(was_clear); mi_assert_internal(was_clear); + mi_atomic_increment_relaxed(&heap->abandoned_count[bin]); + mi_theap_stat_increase(current_theap, pages_abandoned, 1); + mi_abandoned_page_unown(page, current_theap); + return; + } + } + // otherwise, + // page is full (or a singleton), or the page is OS/externally allocated + // leave as is; it will be reclaimed when an object is free'd in the page + // but for non-arena pages, add to the subproc list so these can be visited + if (page->memid.memkind != MI_MEM_ARENA && mi_option_is_enabled(mi_option_visit_abandoned)) { + mi_lock(&heap->os_abandoned_pages_lock) { + // push in front + page->prev = NULL; + page->next = heap->os_abandoned_pages; + if (page->next != NULL) { page->next->prev = page; } + heap->os_abandoned_pages = page; + } + } + mi_theap_stat_increase(current_theap, pages_abandoned, 1); + mi_abandoned_page_unown(page, current_theap); } -// reclaim a specific abandoned segment; `true` on success. -// sets the thread_id. -bool _mi_arena_segment_clear_abandoned(mi_segment_t* segment ) -{ - if (segment->memid.memkind != MI_MEM_ARENA) { - // not in an arena, consider it un-abandoned now. - // but we need to still claim it atomically -- we use the thread_id for that. - size_t expected = 0; - if (mi_atomic_cas_strong_acq_rel(&segment->thread_id, &expected, _mi_thread_id())) { - mi_atomic_decrement_relaxed(&abandoned_count); - return true; + +// this is called from `free.c:mi_free_try_collect_mt` only. +bool _mi_arenas_page_try_reabandon_to_mapped(mi_page_t* page) { + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(!mi_page_is_abandoned_mapped(page)); + mi_assert_internal(!mi_page_is_full(page)); + mi_assert_internal(!mi_page_all_free(page)); + mi_assert_internal(!mi_page_is_singleton(page)); + if (mi_page_is_full(page) || mi_page_is_abandoned_mapped(page) || page->memid.memkind != MI_MEM_ARENA) { + return false; + } + else { + // do not use _mi_heap_theap as we may call this during shutdown of threads and don't want to reinitialize the theap + mi_theap_t* const theap = _mi_page_associated_theap_peek(page); + if (theap == NULL) { + return false; } else { - return false; + mi_theap_stat_counter_increase(theap, pages_reabandon_full, 1); + mi_theap_stat_adjust_decrease(theap, pages_abandoned, 1); // adjust as we are not abandoning fresh + _mi_arenas_page_abandon(page, theap); + return true; } } - // arena segment: use the blocks_abandoned bitmap. - size_t arena_idx; - size_t bitmap_idx; - mi_arena_memid_indices(segment->memid, &arena_idx, &bitmap_idx); - mi_assert_internal(arena_idx < MI_MAX_ARENAS); - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_idx]); - mi_assert_internal(arena != NULL); - bool was_marked = _mi_bitmap_unclaim(arena->blocks_abandoned, arena->field_count, 1, bitmap_idx); - if (was_marked) { - mi_assert_internal(mi_atomic_load_relaxed(&segment->thread_id) == 0); - mi_atomic_decrement_relaxed(&abandoned_count); - mi_atomic_store_release(&segment->thread_id, _mi_thread_id()); +} + +// called from `mi_free` if trying to unabandon an abandoned page +void _mi_arenas_page_unabandon(mi_page_t* page, mi_theap_t* current_theapx) { + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(current_theapx==NULL || _mi_thread_id()==current_theapx->tld->thread_id); + + mi_heap_t* const heap = mi_page_heap(page); + if (mi_page_is_abandoned_mapped(page)) { + mi_assert_internal(page->memid.memkind==MI_MEM_ARENA); + // remove from the abandoned map + const size_t bin = _mi_bin(mi_page_block_size(page)); + mi_assert_internal(bin < MI_ARENA_BIN_COUNT); + size_t slice_index; + size_t slice_count; + mi_arena_pages_t* arena_pages; + mi_arena_t* arena = mi_page_arena_pages(page, &slice_index, &slice_count, &arena_pages); MI_UNUSED(arena); + + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + mi_assert_internal(page->slice_committed > 0 || mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count)); + + // this busy waits until a concurrent reader (from alloc_abandoned) is done + mi_bitmap_clear_once_set(arena_pages->pages_abandoned[bin], slice_index); + mi_page_clear_abandoned_mapped(page); + mi_atomic_decrement_relaxed(&heap->abandoned_count[bin]); + } + else { + // page is full (or a singleton), page is OS allocated + // if not an arena page, remove from the subproc os pages list + if (page->memid.memkind != MI_MEM_ARENA && mi_option_is_enabled(mi_option_visit_abandoned)) { + mi_lock(&heap->os_abandoned_pages_lock) { + if (page->prev != NULL) { page->prev->next = page->next; } + if (page->next != NULL) { page->next->prev = page->prev; } + if (heap->os_abandoned_pages == page) { heap->os_abandoned_pages = page->next; } + page->next = NULL; + page->prev = NULL; + } + } + } + if (current_theapx!=NULL) { + mi_theap_stat_decrease(current_theapx, pages_abandoned, 1); + } + else { + mi_heap_stat_decrease(heap, pages_abandoned, 1); } - // mi_assert_internal(was_marked); - mi_assert_internal(!was_marked || _mi_bitmap_is_claimed(arena->blocks_inuse, arena->field_count, 1, bitmap_idx)); - //mi_assert_internal(arena->blocks_committed == NULL || _mi_bitmap_is_claimed(arena->blocks_committed, arena->field_count, 1, bitmap_idx)); - return was_marked; } -// mark a specific segment as abandoned -// clears the thread_id. -void _mi_arena_segment_mark_abandoned(mi_segment_t* segment) -{ - mi_atomic_store_release(&segment->thread_id, 0); - mi_assert_internal(segment->used == segment->abandoned); - if (segment->memid.memkind != MI_MEM_ARENA) { - // not in an arena; count it as abandoned and return - mi_atomic_increment_relaxed(&abandoned_count); - return; - } - size_t arena_idx; - size_t bitmap_idx; - mi_arena_memid_indices(segment->memid, &arena_idx, &bitmap_idx); - mi_assert_internal(arena_idx < MI_MAX_ARENAS); - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_idx]); - mi_assert_internal(arena != NULL); - const bool was_unmarked = _mi_bitmap_claim(arena->blocks_abandoned, arena->field_count, 1, bitmap_idx, NULL); - if (was_unmarked) { mi_atomic_increment_relaxed(&abandoned_count); } - mi_assert_internal(was_unmarked); - mi_assert_internal(_mi_bitmap_is_claimed(arena->blocks_inuse, arena->field_count, 1, bitmap_idx)); + +/* ----------------------------------------------------------- + Arena free +----------------------------------------------------------- */ +static void mi_arena_schedule_purge(mi_arena_t* arena, size_t slice_index, size_t slices); +static void mi_arenas_try_purge(bool force, bool visit_all, mi_subproc_t* subproc, size_t tseq); + +void _mi_arenas_free(void* p, size_t size, mi_memid_t memid) { + if (p==NULL) return; + if (size==0) return; + + // need to set all memory to undefined as some parts may still be marked as no_access (like padding etc.) + mi_track_mem_undefined(p, size); + + if (mi_memkind_is_os(memid.memkind)) { + // was a direct OS allocation, pass through + _mi_os_free(p, size, memid); + } + else if (memid.memkind == MI_MEM_ARENA) { + // allocated in an arena + size_t slice_count; + size_t slice_index; + mi_arena_t* arena = mi_arena_from_memid(memid, &slice_index, &slice_count); + mi_assert_internal((size%MI_ARENA_SLICE_SIZE)==0); + mi_assert_internal((slice_count*MI_ARENA_SLICE_SIZE)==size); + mi_assert_internal(mi_arena_slice_start(arena,slice_index) <= (uint8_t*)p); + mi_assert_internal(mi_arena_slice_start(arena,slice_index) + mi_size_of_slices(slice_count) > (uint8_t*)p); + // checks + if (arena == NULL) { + _mi_error_message(EINVAL, "trying to free from an invalid arena: %p, size %zu, memkind: 0x%x\n", p, size, memid.memkind); + return; + } + mi_assert_internal(slice_index < arena->slice_count); + mi_assert_internal(slice_index >= mi_arena_info_slices(arena)); + if (slice_index < mi_arena_info_slices(arena) || slice_index >= arena->slice_count) { + _mi_error_message(EINVAL, "trying to free from an invalid arena block: %p, size %zu, memkind: 0x%x\n", p, size, memid.memkind); + return; + } + + // potentially decommit + if (!arena->memid.is_pinned /* && !arena->memid.initially_committed */) { // todo: allow decommit even if initially committed? + // (delay) purge the page + mi_arena_schedule_purge(arena, slice_index, slice_count); + } + + // and make it available to others again + bool all_inuse = mi_bbitmap_setN(arena->slices_free, slice_index, slice_count); + if (!all_inuse) { + _mi_error_message(EAGAIN, "trying to free an already freed arena block: %p, size %zu\n", mi_arena_slice_start(arena,slice_index), mi_size_of_slices(slice_count)); + return; + }; + } + else if (memid.memkind == MI_MEM_META) { + _mi_meta_free(p, size, memid); + } + else { + // arena was none, external, or static; nothing to do + mi_assert_internal(mi_memid_needs_no_free(memid)); + } + + // try to purge expired decommits + // mi_arenas_try_purge(false, false, NULL); } -// start a cursor at a randomized arena -void _mi_arena_field_cursor_init(mi_heap_t* heap, mi_arena_field_cursor_t* current) { - const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count); - current->start = (max_arena == 0 ? 0 : (mi_arena_id_t)( _mi_heap_random_next(heap) % max_arena)); - current->count = 0; - current->bitmap_idx = 0; +// Purge the arenas; if `force_purge` is true, amenable parts are purged even if not yet expired +void _mi_arenas_collect(bool force_purge, bool visit_all, mi_tld_t* tld) { + mi_arenas_try_purge(force_purge, visit_all, tld->subproc, tld->thread_seq); } -// reclaim abandoned segments -// this does not set the thread id (so it appears as still abandoned) -mi_segment_t* _mi_arena_segment_clear_abandoned_next(mi_arena_field_cursor_t* previous ) -{ - const int max_arena = (int)mi_atomic_load_relaxed(&mi_arena_count); - if (max_arena <= 0 || mi_atomic_load_relaxed(&abandoned_count) == 0) return NULL; - - int count = previous->count; - size_t field_idx = mi_bitmap_index_field(previous->bitmap_idx); - size_t bit_idx = mi_bitmap_index_bit_in_field(previous->bitmap_idx) + 1; - // visit arena's (from previous) - for (; count < max_arena; count++, field_idx = 0, bit_idx = 0) { - mi_arena_id_t arena_idx = previous->start + count; - if (arena_idx >= max_arena) { arena_idx = arena_idx % max_arena; } // wrap around - mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_idx]); + +// Is a pointer contained in the given arena area? +static bool mi_arena_strictly_contains(mi_arena_t* arena, const void* p) { + return (arena != NULL && + mi_arena_start(arena) <= (const uint8_t*)p && + mi_arena_start(arena) + mi_size_of_slices(arena->slice_count) >(const uint8_t*)p); +} + +// Is a pointer inside any of our arenas? +static bool mi_arenas_contain_ex(const void* p, mi_arena_t* parent) { + mi_subproc_t* subproc = _mi_subproc(); + const size_t max_arena = mi_arenas_get_count(subproc); + for (size_t i = 0; i < max_arena; i++) { + mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[i]); if (arena != NULL) { - // visit the abandoned fields (starting at previous_idx) - for ( ; field_idx < arena->field_count; field_idx++, bit_idx = 0) { - size_t field = mi_atomic_load_relaxed(&arena->blocks_abandoned[field_idx]); - if mi_unlikely(field != 0) { // skip zero fields quickly - // visit each set bit in the field (todo: maybe use `ctz` here?) - for ( ; bit_idx < MI_BITMAP_FIELD_BITS; bit_idx++) { - // pre-check if the bit is set - size_t mask = ((size_t)1 << bit_idx); - if mi_unlikely((field & mask) == mask) { - mi_bitmap_index_t bitmap_idx = mi_bitmap_index_create(field_idx, bit_idx); - // try to reclaim it atomically - if (_mi_bitmap_unclaim(arena->blocks_abandoned, arena->field_count, 1, bitmap_idx)) { - mi_atomic_decrement_relaxed(&abandoned_count); - previous->bitmap_idx = bitmap_idx; - previous->count = count; - mi_assert_internal(_mi_bitmap_is_claimed(arena->blocks_inuse, arena->field_count, 1, bitmap_idx)); - mi_segment_t* segment = (mi_segment_t*)mi_arena_block_start(arena, bitmap_idx); - mi_assert_internal(mi_atomic_load_relaxed(&segment->thread_id) == 0); - //mi_assert_internal(arena->blocks_committed == NULL || _mi_bitmap_is_claimed(arena->blocks_committed, arena->field_count, 1, bitmap_idx)); - return segment; - } - } - } + if (parent==NULL || arena==parent || arena->parent==parent) { + if (mi_arena_strictly_contains(arena, p)) { + return true; } } } } - // no more found - previous->bitmap_idx = 0; - previous->count = 0; - return NULL; + return false; +} + +// Is a pointer inside any of our arenas? +bool _mi_arenas_contain(const void* p) { + return mi_arenas_contain_ex(p, NULL); +} + +// Is a pointer contained in the given arena area? +bool mi_arena_contains(mi_arena_id_t arena_id, const void* p) { + mi_arena_t* arena = _mi_arena_from_id(arena_id); + if (arena==NULL) return false; + else if (mi_arena_strictly_contains(arena, p)) return true; + else return mi_arenas_contain_ex(p, arena); // maybe a subarena? +} + + +/* ----------------------------------------------------------- + Remove an arena. +----------------------------------------------------------- */ + +// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit` +// for dynamic libraries that are unloaded and need to release all their allocated memory. +static void mi_arenas_unsafe_destroy(mi_subproc_t* subproc) { + mi_assert_internal(subproc != NULL); + const size_t arena_count = mi_arenas_get_count(subproc); + for (size_t i = 0; i < arena_count; i++) { + mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[i]); + if (arena != NULL) { + // mi_lock_done(&arena->abandoned_visit_lock); + mi_atomic_store_ptr_release(mi_arena_t, &subproc->arenas[i], NULL); + if (mi_memkind_is_os(arena->memid.memkind)) { + _mi_os_free_ex(mi_arena_start(arena), mi_arena_size(arena), true, arena->memid, subproc); // pass `subproc` to avoid accessing the theap pointer (in `_mi_subproc()`) + } + } + } + // try to lower the max arena. + size_t expected = arena_count; + mi_atomic_cas_strong_acq_rel(&subproc->arena_count, &expected, (size_t)0); +} + + +// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit` +// for dynamic libraries that are unloaded and need to release all their allocated memory. +void _mi_arenas_unsafe_destroy_all(mi_subproc_t* subproc) { + mi_arenas_unsafe_destroy(subproc); + // mi_arenas_try_purge(true /* force purge */, true /* visit all*/, subproc, 0 /* thread seq */); // purge non-owned arenas } @@ -875,102 +1435,320 @@ mi_segment_t* _mi_arena_segment_clear_abandoned_next(mi_arena_field_cursor_t* pr Add an arena. ----------------------------------------------------------- */ -static bool mi_arena_add(mi_arena_t* arena, mi_arena_id_t* arena_id, mi_stats_t* stats) { +static bool mi_arenas_add(mi_subproc_t* subproc, mi_arena_t* arena, mi_arena_id_t* arena_id) +{ mi_assert_internal(arena != NULL); - mi_assert_internal((uintptr_t)mi_atomic_load_ptr_relaxed(uint8_t,&arena->start) % MI_SEGMENT_ALIGN == 0); - mi_assert_internal(arena->block_count > 0); - if (arena_id != NULL) { *arena_id = -1; } + mi_assert_internal(arena->slice_count > 0); + if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); } + + // try to find a NULL entry + mi_arena_t* expected; + size_t count = mi_arenas_get_count(subproc); + for( size_t i = 0; i < count; i++) { + if (mi_arena_from_index(subproc,i) == NULL) { + arena->arena_idx = i; + expected = NULL; + if (mi_atomic_cas_ptr_strong_release(mi_arena_t, &subproc->arenas[i], &expected, arena)) { + // success + if (arena_id != NULL) { *arena_id = mi_arena_id_from_arena(arena); } + return true; + } + } + } - size_t i = mi_atomic_increment_acq_rel(&mi_arena_count); - if (i >= MI_MAX_ARENAS) { - mi_atomic_decrement_acq_rel(&mi_arena_count); - return false; + // otherwise, try to allocate a fresh slot + while(countarena_count, &count, count+1)) { + arena->arena_idx = count; + expected = NULL; + if (mi_atomic_cas_ptr_strong_release(mi_arena_t, &subproc->arenas[count], &expected, arena)) { + mi_subproc_stat_counter_increase(arena->subproc, arena_count, 1); + if (arena_id != NULL) { *arena_id = mi_arena_id_from_arena(arena); } + return true; + } + } } - _mi_stat_counter_increase(&stats->arena_count,1); - arena->id = mi_arena_id_create(i); - mi_atomic_store_ptr_release(mi_arena_t,&mi_arenas[i], arena); - if (arena_id != NULL) { *arena_id = arena->id; } - return true; + + // failed + arena->arena_idx = 0; + arena->subproc = NULL; + return false; +} + +static size_t mi_arena_pages_size(size_t slice_count, size_t* bitmap_base) { + if (slice_count == 0) slice_count = MI_BCHUNK_BITS; + mi_assert_internal((slice_count % MI_BCHUNK_BITS) == 0); + const size_t base_size = _mi_align_up(sizeof(mi_arena_pages_t), MI_BCHUNK_SIZE); + const size_t bitmaps_count = 1 + MI_ARENA_BIN_COUNT; // pages, and abandoned + const size_t bitmaps_size = bitmaps_count * mi_bitmap_size(slice_count, NULL); + const size_t size = base_size + bitmaps_size; + if (bitmap_base != NULL) *bitmap_base = base_size; + return size; } -static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int numa_node, bool exclusive, mi_memid_t memid, mi_arena_id_t* arena_id) mi_attr_noexcept +static size_t mi_arena_info_slices_needed(size_t slice_count, size_t* bitmap_base) { + if (slice_count == 0) slice_count = MI_BCHUNK_BITS; + mi_assert_internal((slice_count % MI_BCHUNK_BITS) == 0); + const size_t base_size = _mi_align_up(sizeof(mi_arena_t), MI_BCHUNK_SIZE); + const size_t bitmaps_count = 4 + MI_ARENA_BIN_COUNT; // commit, dirty, purge, pages, and abandoned + const size_t bitmaps_size = bitmaps_count * mi_bitmap_size(slice_count, NULL) + mi_bbitmap_size(slice_count, NULL); // + free + #if MI_PAGE_META_IS_SEPARATED + const size_t pages_size = slice_count * sizeof(mi_page_t); + #else + const size_t pages_size = 0; + #endif + const size_t size = base_size + bitmaps_size + pages_size; + + const size_t os_page_size = _mi_os_page_size(); + const size_t info_size = _mi_align_up(size, os_page_size) + _mi_os_secure_guard_page_size(); + const size_t info_slices = mi_slice_count_of_size(info_size); + + if (bitmap_base != NULL) *bitmap_base = base_size; + return info_slices; +} + +static mi_bitmap_t* mi_arena_bitmap_init(size_t slice_count, uint8_t** base) { + mi_bitmap_t* bitmap = (mi_bitmap_t*)(*base); + *base = (*base) + mi_bitmap_init(bitmap, slice_count, true /* already zero */); + return bitmap; +} + +static mi_bbitmap_t* mi_arena_bbitmap_init(size_t slice_count, uint8_t** base) { + mi_bbitmap_t* bbitmap = (mi_bbitmap_t*)(*base); + *base = (*base) + mi_bbitmap_init(bbitmap, slice_count, true /* already zero */); + return bbitmap; +} + +static mi_arena_pages_t* mi_arena_pages_alloc(mi_arena_t* arena) { + const size_t slice_count = arena->slice_count; + size_t bitmap_base = 0; + const size_t size = mi_arena_pages_size(slice_count, &bitmap_base); + mi_arena_pages_t* arena_pages = (mi_arena_pages_t*)mi_heap_zalloc_aligned(mi_heap_main(), size, MI_BCHUNK_SIZE); + if (arena_pages==NULL) return NULL; + uint8_t* base = (uint8_t*)arena_pages + bitmap_base; + mi_assert_internal(_mi_is_aligned(base, MI_BCHUNK_SIZE)); + arena_pages->pages = mi_arena_bitmap_init(slice_count, &base); + for (size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) { + arena_pages->pages_abandoned[i] = mi_arena_bitmap_init(slice_count, &base); + } + return arena_pages; +} + +static mi_arena_t* mi_arena_initialize(mi_subproc_t* subproc, void* start, + size_t slice_count, mi_arena_t* parent, size_t total_size, + int numa_node, bool exclusive, + mi_memid_t memid, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) { - if (arena_id != NULL) *arena_id = _mi_arena_id_none(); - if (size < MI_ARENA_BLOCK_SIZE) return false; + mi_assert_internal(_mi_is_aligned(start,MI_ARENA_SLICE_ALIGN)); + mi_assert_internal(mi_size_of_slices(slice_count)>=MI_ARENA_MIN_SIZE); - if (is_large) { - mi_assert_internal(memid.initially_committed && memid.is_pinned); + if (slice_count > MI_BITMAP_MAX_BIT_COUNT) { // 16 GiB for now + // note: this should never happen if called from `mi_manage_os_memory` (as that allocates sub-arenas when needed) + _mi_warning_message("cannot use OS memory since it is too large (size %zu MiB, maximum is %zu MiB)", mi_size_of_slices(slice_count)/MI_MiB, mi_size_of_slices(MI_BITMAP_MAX_BIT_COUNT)/MI_MiB); + return NULL; } - const size_t bcount = size / MI_ARENA_BLOCK_SIZE; - const size_t fields = _mi_divide_up(bcount, MI_BITMAP_FIELD_BITS); - const size_t bitmaps = (memid.is_pinned ? 3 : 5); - const size_t asize = sizeof(mi_arena_t) + (bitmaps*fields*sizeof(mi_bitmap_field_t)); - mi_memid_t meta_memid; - mi_arena_t* arena = (mi_arena_t*)mi_arena_meta_zalloc(asize, &meta_memid, &_mi_stats_main); // TODO: can we avoid allocating from the OS? - if (arena == NULL) return false; + size_t bitmap_base; + const size_t info_slices = mi_arena_info_slices_needed(slice_count, &bitmap_base); + if (slice_count < info_slices+1) { + _mi_warning_message("cannot use OS memory since it is not large enough (size %zu KiB, minimum required is %zu KiB)", mi_size_of_slices(slice_count)/MI_KiB, mi_size_of_slices(info_slices+1)/MI_KiB); + return NULL; + } + // else if (info_slices >= MI_ARENA_MAX_CHUNK_OBJ_SLICES) { + // _mi_warning_message("cannot use OS memory since it is too large with respect to the maximum object size (size %zu MiB, meta-info slices %zu, maximum object slices are %zu)", mi_size_of_slices(slice_count)/MI_MiB, info_slices, MI_ARENA_MAX_CHUNK_OBJ_SLICES); + // return NULL; + // } + + mi_arena_t* arena = (mi_arena_t*)start; + + // commit & zero if needed + if (!memid.initially_committed) { + size_t commit_size = mi_size_of_slices(info_slices); + // leave a guard OS page decommitted at the end? + if (!memid.is_pinned) { commit_size -= _mi_os_secure_guard_page_size(); } + bool ok = false; + if (commit_fun != NULL) { + ok = (*commit_fun)(true /* commit */, arena, commit_size, NULL, commit_fun_arg); + } + else { + ok = _mi_os_commit(arena, commit_size, NULL); + } + if (!ok) { + _mi_warning_message("unable to commit meta-data for OS memory"); + return NULL; + } + } + else if (!memid.is_pinned) { + // if MI_SECURE, set a guard page at the end of the arena info + // todo: this does not respect the commit_fun as the memid is of external memory + _mi_os_secure_guard_page_set_before((uint8_t*)arena + mi_size_of_slices(info_slices), memid); + } + if (!memid.initially_zero) { + _mi_memzero(arena, mi_size_of_slices(info_slices) - _mi_os_secure_guard_page_size()); + } - // already zero'd due to zalloc - // _mi_memzero(arena, asize); - arena->id = _mi_arena_id_none(); + // init + arena->subproc = subproc; arena->memid = memid; - arena->exclusive = exclusive; - arena->meta_size = asize; - arena->meta_memid = meta_memid; - arena->block_count = bcount; - arena->field_count = fields; - arena->start = (uint8_t*)start; - arena->numa_node = numa_node; // TODO: or get the current numa node if -1? (now it allows anyone to allocate on -1) - arena->is_large = is_large; + arena->is_exclusive = exclusive; + arena->slice_count = slice_count; + arena->info_slices = info_slices; + if (numa_node<0 && mi_option_is_enabled(mi_option_arena_is_numa_local)) { + arena->numa_node = _mi_os_numa_node(); + } + else { + arena->numa_node = numa_node; + } arena->purge_expire = 0; - arena->search_idx = 0; - // consequetive bitmaps - arena->blocks_dirty = &arena->blocks_inuse[fields]; // just after inuse bitmap - arena->blocks_abandoned = &arena->blocks_inuse[2 * fields]; // just after dirty bitmap - arena->blocks_committed = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[3*fields]); // just after abandoned bitmap - arena->blocks_purge = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[4*fields]); // just after committed bitmap - // initialize committed bitmap? - if (arena->blocks_committed != NULL && arena->memid.initially_committed) { - memset((void*)arena->blocks_committed, 0xFF, fields*sizeof(mi_bitmap_field_t)); // cast to void* to avoid atomic warning + arena->commit_fun = commit_fun; + arena->commit_fun_arg = commit_fun_arg; + arena->parent = parent; + arena->total_size = total_size; + + // init bitmaps + uint8_t* base = mi_arena_start(arena) + bitmap_base; + arena->slices_free = mi_arena_bbitmap_init(slice_count, &base); + arena->slices_committed = mi_arena_bitmap_init(slice_count, &base); + arena->slices_dirty = mi_arena_bitmap_init(slice_count, &base); + arena->slices_purge = mi_arena_bitmap_init(slice_count, &base); + arena->pages_main.pages = mi_arena_bitmap_init(slice_count, &base); + for (size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) { + arena->pages_main.pages_abandoned[i] = mi_arena_bitmap_init(slice_count, &base); + } + #if MI_PAGE_META_IS_SEPARATED + arena->pages_meta = (mi_page_t*)base; + base += (slice_count * sizeof(mi_page_t)); + #else + arena->pages_meta = NULL; + #endif + mi_assert_internal(mi_size_of_slices(info_slices) >= (size_t)(base - mi_arena_start(arena))); + + // reserve our meta info (and reserve slices outside the memory area) + mi_bbitmap_unsafe_setN(arena->slices_free, info_slices /* start */, arena->slice_count - info_slices); + if (memid.initially_committed) { + mi_bitmap_unsafe_setN(arena->slices_committed, 0, arena->slice_count); + } + if (!memid.initially_zero) { + mi_bitmap_unsafe_setN(arena->slices_dirty, 0, arena->slice_count); + } + + if (!mi_arenas_add(subproc, arena, arena_id)) { return NULL; } + return arena; +} + +static bool mi_manage_os_memory_ex2(mi_subproc_t* subproc, void* start, size_t size, int numa_node, bool exclusive, + mi_memid_t memid, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept +{ + // checks + mi_assert(start!=NULL); + if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); } + if (start==NULL) return false; + if (!_mi_is_aligned(start, MI_ARENA_SLICE_SIZE)) { + // we can align the start since the memid tracks the real base of the memory. + void* const aligned_start = _mi_align_up_ptr(start, MI_ARENA_SLICE_SIZE); + const size_t diff = (uint8_t*)aligned_start - (uint8_t*)start; + if (diff >= size || (size - diff) < MI_ARENA_SLICE_SIZE) { + _mi_warning_message("after alignment, the size of the arena becomes too small (memory at %p with size %zu)\n", start, size); + return false; + } + start = aligned_start; + size = size - diff; + } + + // allocate enough arena's to span the full memory area + // the first arena is the owner, the rest are "sub-arena" (with `parent` pointing to the first one) + size_t total_slice_count = _mi_align_down(size / MI_ARENA_SLICE_SIZE, MI_BCHUNK_BITS); + size_t total_size = mi_size_of_slices(total_slice_count); + if (total_size < MI_ARENA_MIN_SIZE) { + _mi_warning_message("cannot use OS memory since it is not large enough (size %zu KiB, minimum required is %zu KiB)", size/MI_KiB, MI_ARENA_MIN_SIZE/MI_KiB); + return false; } - // and claim leftover blocks if needed (so we never allocate there) - ptrdiff_t post = (fields * MI_BITMAP_FIELD_BITS) - bcount; - mi_assert_internal(post >= 0); - if (post > 0) { - // don't use leftover bits at the end - mi_bitmap_index_t postidx = mi_bitmap_index_create(fields - 1, MI_BITMAP_FIELD_BITS - post); - _mi_bitmap_claim(arena->blocks_inuse, fields, post, postidx, NULL); + mi_arena_t* parent = NULL; + do { + // counting down on the total_slice_count + size_t slice_count = total_slice_count; + if (slice_count > MI_BITMAP_MAX_BIT_COUNT) { // 16 GiB for now (with 64KiB slices) + slice_count = MI_BITMAP_MAX_BIT_COUNT; + } + + // initialize + mi_arena_t* arena = mi_arena_initialize( subproc, start, slice_count, parent, + (parent==NULL ? total_size : 0), numa_node, exclusive, + memid, commit_fun, commit_fun_arg, + (parent==NULL ? arena_id : NULL)); + if (arena==NULL) { + // failed to initialize due to failing commit or too many arena's + if (parent==NULL) { + return false; + } + else { + // partial success, but failed to use the full area.. + // todo: roll-back in this case? that requires a lock on the arena's array though + mi_assert(mi_size_of_slices(total_slice_count) <= parent->total_size); + parent->total_size -= mi_size_of_slices(total_slice_count); + return true; + } + } + + // success + if (parent==NULL) { + parent = arena; + memid.memkind = MI_MEM_NONE; + } + mi_assert(slice_count <= total_slice_count); + total_slice_count -= slice_count; + start = (uint8_t*)start + mi_size_of_slices(slice_count); } - return mi_arena_add(arena, arena_id, &_mi_stats_main); + while (total_slice_count > 0); + return true; } -bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { +bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_pinned, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { mi_memid_t memid = _mi_memid_create(MI_MEM_EXTERNAL); + memid.mem.os.base = start; + memid.mem.os.size = size; memid.initially_committed = is_committed; memid.initially_zero = is_zero; - memid.is_pinned = is_large; - return mi_manage_os_memory_ex2(start,size,is_large,numa_node,exclusive,memid, arena_id); + memid.is_pinned = is_pinned; + return mi_manage_os_memory_ex2(_mi_subproc(), start, size, numa_node, exclusive, memid, NULL, NULL, arena_id); } +bool mi_manage_memory(void* start, size_t size, bool is_committed, bool is_pinned, bool is_zero, int numa_node, bool exclusive, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept +{ + mi_memid_t memid = _mi_memid_create(MI_MEM_EXTERNAL); + memid.mem.os.base = start; + memid.mem.os.size = size; + memid.initially_committed = is_committed; + memid.initially_zero = is_zero; + memid.is_pinned = is_pinned; + return mi_manage_os_memory_ex2(_mi_subproc(), start, size, numa_node, exclusive, memid, commit_fun, commit_fun_arg, arena_id); +} + + // Reserve a range of regular OS memory -int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { +static int mi_reserve_os_memory_ex2(mi_subproc_t* subproc, size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) { if (arena_id != NULL) *arena_id = _mi_arena_id_none(); - size = _mi_align_up(size, MI_ARENA_BLOCK_SIZE); // at least one block + size = _mi_align_up(size, MI_ARENA_SLICE_SIZE); // at least one slice mi_memid_t memid; - void* start = _mi_os_alloc_aligned(size, MI_SEGMENT_ALIGN, commit, allow_large, &memid, &_mi_stats_main); + void* start = _mi_os_alloc_aligned(size, MI_ARENA_SLICE_ALIGN, commit, allow_large, &memid); if (start == NULL) return ENOMEM; - const bool is_large = memid.is_pinned; // todo: use separate is_large field? - if (!mi_manage_os_memory_ex2(start, size, is_large, -1 /* numa node */, exclusive, memid, arena_id)) { - _mi_os_free_ex(start, size, commit, memid, &_mi_stats_main); + if (!mi_manage_os_memory_ex2(subproc, start, size, -1 /* numa node */, exclusive, memid, NULL, NULL, arena_id)) { + _mi_os_free_ex(start, size, commit, memid, NULL); _mi_verbose_message("failed to reserve %zu KiB memory\n", _mi_divide_up(size, 1024)); return ENOMEM; } - _mi_verbose_message("reserved %zu KiB memory%s\n", _mi_divide_up(size, 1024), is_large ? " (in large os pages)" : ""); + _mi_verbose_message("reserved %zu KiB memory%s\n", _mi_divide_up(size, 1024), memid.is_pinned ? " (in large os pages)" : ""); + // mi_debug_show_arenas(true, true, false); + return 0; } +// Reserve a range of regular OS memory +int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { + return mi_reserve_os_memory_ex2(_mi_subproc(), size, commit, allow_large, exclusive, arena_id); +} // Manage a range of regular OS memory bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept { @@ -987,55 +1765,235 @@ int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noe Debugging ----------------------------------------------------------- */ -static size_t mi_debug_show_bitmap(const char* prefix, const char* header, size_t block_count, mi_bitmap_field_t* fields, size_t field_count ) { - _mi_verbose_message("%s%s:\n", prefix, header); - size_t bcount = 0; - size_t inuse_count = 0; - for (size_t i = 0; i < field_count; i++) { - char buf[MI_BITMAP_FIELD_BITS + 1]; - uintptr_t field = mi_atomic_load_relaxed(&fields[i]); - for (size_t bit = 0; bit < MI_BITMAP_FIELD_BITS; bit++, bcount++) { - if (bcount < block_count) { - bool inuse = ((((uintptr_t)1 << bit) & field) != 0); - if (inuse) inuse_count++; - buf[bit] = (inuse ? 'x' : '.'); +// Return idx of the slice past the last used slice +static size_t mi_arena_used_slices(mi_arena_t* arena) { + size_t idx; + if (mi_bbitmap_bsr_inv(arena->slices_free, &idx)) { + return (idx + 1); + } + else { + return mi_arena_info_slices(arena); + } +} + +static size_t mi_debug_show_bfield(mi_bfield_t field, char* buf, size_t* k) { + size_t bit_set_count = 0; + for (int bit = 0; bit < MI_BFIELD_BITS; bit++) { + bool is_set = ((((mi_bfield_t)1 << bit) & field) != 0); + if (is_set) bit_set_count++; + buf[*k] = (is_set ? 'x' : '.'); + *k = *k + 1; + } + return bit_set_count; +} + +typedef enum mi_ansi_color_e { + MI_BLACK = 30, + MI_MAROON, + MI_DARKGREEN, + MI_ORANGE, + MI_NAVY, + MI_PURPLE, + MI_TEAL, + MI_GRAY, + MI_DARKGRAY = 90, + MI_RED, + MI_GREEN, + MI_YELLOW, + MI_BLUE, + MI_MAGENTA, + MI_CYAN, + MI_WHITE +} mi_ansi_color_t; + +static void mi_debug_color(char* buf, size_t* k, mi_ansi_color_t color) { + *k += _mi_snprintf(buf + *k, 32, "\x1B[%dm", (int)color); +} + +static int mi_page_commit_usage(mi_page_t* page) { + // if (mi_page_size(page) <= MI_PAGE_MIN_COMMIT_SIZE) return 100; + const size_t committed_size = mi_page_committed(page); + const size_t used_size = page->used * mi_page_block_size(page); + return (int)(used_size * 100 / committed_size); +} + +static size_t mi_debug_show_page_bfield(char* buf, size_t* k, mi_arena_t* arena, size_t slice_index, long* pbit_of_page, mi_ansi_color_t* pcolor_of_page ) { + size_t bit_set_count = 0; + long bit_of_page = *pbit_of_page; + mi_ansi_color_t color = *pcolor_of_page; + mi_ansi_color_t prev_color = MI_GRAY; + for (int bit = 0; bit < MI_BFIELD_BITS; bit++, bit_of_page--) { + // bool is_set = ((((mi_bfield_t)1 << bit) & field) != 0); + void* start = mi_arena_slice_start(arena, slice_index + bit); + mi_page_t* page = _mi_safe_ptr_page(start); + char c = ' '; + if (page!=NULL && start==mi_page_slice_start(page)) { + mi_assert_internal(bit_of_page <= 0); + bit_set_count++; + c = 'p'; + color = MI_GRAY; + if (mi_page_is_singleton(page)) { c = 's'; } + else if (mi_page_is_full(page)) { c = 'f'; } + if (!mi_page_is_abandoned(page)) { c = _mi_toupper(c); } + int commit_usage = mi_page_commit_usage(page); + if (commit_usage < 25) { color = MI_MAROON; } + else if (commit_usage < 50) { color = MI_ORANGE; } + else if (commit_usage < 75) { color = MI_TEAL; } + else color = MI_DARKGREEN; + bit_of_page = (long)page->memid.mem.arena.slice_count; + } + else { + c = '?'; + if (bit_of_page > 0) { c = '-'; } + else if (_mi_meta_is_meta_page(start)) { c = 'm'; color = MI_GRAY; } + else if (slice_index + bit < arena->info_slices) { c = 'i'; color = MI_GRAY; } + // else if (mi_bitmap_is_setN(arena->pages_purge, slice_index + bit, NULL)) { c = '*'; } + else if (mi_bbitmap_is_setN(arena->slices_free, slice_index+bit,1)) { + if (mi_bitmap_is_set(arena->slices_purge, slice_index + bit)) { c = '~'; color = MI_ORANGE; } + else if (mi_bitmap_is_set(arena->slices_committed, slice_index + bit)) { c = '_'; color = MI_GRAY; } + else { c = '.'; color = MI_GRAY; } + } + if (bit==MI_BFIELD_BITS-1 && bit_of_page > 1) { c = '>'; } + } + if (color != prev_color) { + mi_debug_color(buf, k, color); + prev_color = color; + } + buf[*k] = c; *k += 1; + } + mi_debug_color(buf, k, MI_GRAY); + *pbit_of_page = bit_of_page; + *pcolor_of_page = color; + return bit_set_count; +} + +static size_t mi_debug_show_chunks(const char* header1, const char* header2, const char* header3, + size_t slice_count, size_t chunk_count, + mi_bchunk_t* chunks, mi_bchunkmap_t* chunk_bins, bool invert, mi_arena_t* arena, bool narrow) +{ + _mi_raw_message("\x1B[37m%s%s%s (use/commit: \x1B[31m0 - 25%%\x1B[33m - 50%%\x1B[36m - 75%%\x1B[32m - 100%%\x1B[0m)\n", header1, header2, header3); + const size_t fields_per_line = (narrow ? 2 : 4); + const size_t used_slice_count = mi_arena_used_slices(arena); + size_t bit_count = 0; + size_t bit_set_count = 0; + long bit_of_page = 0; + mi_ansi_color_t color_of_page = MI_GRAY; + for (size_t i = 0; i < chunk_count && bit_count < slice_count; i++) { + char buf[5*MI_BCHUNK_BITS + 64]; _mi_memzero(buf, sizeof(buf)); + if (bit_count > used_slice_count && i+2 < chunk_count) { + const size_t diff = chunk_count - 1 - i; + bit_count += diff*MI_BCHUNK_BITS; + _mi_raw_message(" |\n"); + i = chunk_count-1; + } + + size_t k = 0; + + if (i<10) { buf[k++] = ('0' + (char)i); buf[k++] = ' '; buf[k++] = ' '; } + else if (i<100) { buf[k++] = ('0' + (char)(i/10)); buf[k++] = ('0' + (char)(i%10)); buf[k++] = ' '; } + else if (i<1000) { buf[k++] = ('0' + (char)(i/100)); buf[k++] = ('0' + (char)((i%100)/10)); buf[k++] = ('0' + (char)(i%10)); } + + char chunk_kind = ' '; + if (chunk_bins != NULL) { + switch (mi_bbitmap_debug_get_bin(chunk_bins,i)) { + case MI_CBIN_SMALL: chunk_kind = 'S'; break; + case MI_CBIN_MEDIUM: chunk_kind = 'M'; break; + case MI_CBIN_LARGE: chunk_kind = 'L'; break; + case MI_CBIN_HUGE: chunk_kind = 'H'; break; + case MI_CBIN_OTHER: chunk_kind = 'X'; break; + default: chunk_kind = ' '; break; // suppress warning + // case MI_CBIN_NONE: chunk_kind = 'N'; break; + } + } + buf[k++] = chunk_kind; + buf[k++] = ' '; + + for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) { + if (j > 0 && (j % fields_per_line) == 0) { + // buf[k++] = '\n'; _mi_memset(buf+k,' ',7); k += 7; + _mi_raw_message(" %s\n\x1B[37m", buf); + _mi_memzero(buf, sizeof(buf)); + _mi_memset(buf, ' ', 5); k = 5; + } + if (bit_count < slice_count) { + mi_bfield_t bfield = 0; + if (chunks!=NULL) { + bfield = chunks[i].bfields[j]; + } + if (invert) bfield = ~bfield; + size_t xcount = (chunks==NULL ? mi_debug_show_page_bfield(buf, &k, arena, bit_count, &bit_of_page, &color_of_page) + : mi_debug_show_bfield(bfield, buf, &k)); + if (invert) xcount = MI_BFIELD_BITS - xcount; + bit_set_count += xcount; + buf[k++] = ' '; } else { - buf[bit] = ' '; + _mi_memset(buf + k, 'o', MI_BFIELD_BITS); + k += MI_BFIELD_BITS; } + bit_count += MI_BFIELD_BITS; } - buf[MI_BITMAP_FIELD_BITS] = 0; - _mi_verbose_message("%s %s\n", prefix, buf); + _mi_raw_message(" %s\n\x1B[37m", buf); } - _mi_verbose_message("%s total ('x'): %zu\n", prefix, inuse_count); - return inuse_count; + _mi_raw_message("\x1B[0m total pages: %zu\n", bit_set_count); + return bit_set_count; } -void mi_debug_show_arenas(bool show_inuse, bool show_abandoned, bool show_purge) mi_attr_noexcept { - size_t max_arenas = mi_atomic_load_relaxed(&mi_arena_count); - size_t inuse_total = 0; - size_t abandoned_total = 0; - size_t purge_total = 0; +//static size_t mi_debug_show_bitmap_binned(const char* header1, const char* header2, const char* header3, size_t slice_count, +// mi_bitmap_t* bitmap, mi_bchunkmap_t* chunk_bins, bool invert, mi_arena_t* arena, bool narrow) { +// return mi_debug_show_chunks(header1, header2, header3, slice_count, mi_bitmap_chunk_count(bitmap), &bitmap->chunks[0], chunk_bins, invert, arena, narrow); +//} + +static void mi_debug_show_arenas_ex(mi_heap_t* heap, bool show_pages, bool narrow) mi_attr_noexcept { + mi_subproc_t* subproc = heap->subproc; + size_t max_arenas = mi_arenas_get_count(subproc); + //size_t free_total = 0; + //size_t slice_total = 0; + //size_t abandoned_total = 0; + size_t page_total = 0; for (size_t i = 0; i < max_arenas; i++) { - mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]); + mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[i]); if (arena == NULL) break; - _mi_verbose_message("arena %zu: %zu blocks of size %zuMiB (in %zu fields) %s\n", i, arena->block_count, MI_ARENA_BLOCK_SIZE / MI_MiB, arena->field_count, (arena->memid.is_pinned ? ", pinned" : "")); - if (show_inuse) { - inuse_total += mi_debug_show_bitmap(" ", "inuse blocks", arena->block_count, arena->blocks_inuse, arena->field_count); - } - if (arena->blocks_committed != NULL) { - mi_debug_show_bitmap(" ", "committed blocks", arena->block_count, arena->blocks_committed, arena->field_count); - } - if (show_abandoned) { - abandoned_total += mi_debug_show_bitmap(" ", "abandoned blocks", arena->block_count, arena->blocks_abandoned, arena->field_count); - } - if (show_purge && arena->blocks_purge != NULL) { - purge_total += mi_debug_show_bitmap(" ", "purgeable blocks", arena->block_count, arena->blocks_purge, arena->field_count); + mi_assert(arena->subproc == subproc); + // slice_total += arena->slice_count; + _mi_raw_message("%sarena %zu at %p: %zu slices (%zu MiB)%s%s, subproc: %p, numa: %i\n", + (arena->parent==NULL ? "" : "(sub)"), i, arena, arena->slice_count, (size_t)(mi_size_of_slices(arena->slice_count)/MI_MiB), + (arena->memid.is_pinned ? ", pinned" : ""), (arena->is_exclusive ? ", exclusive" : ""), + arena->subproc, arena->numa_node); + //if (show_inuse) { + // free_total += mi_debug_show_bbitmap("in-use slices", arena->slice_count, arena->slices_free, true, NULL); + //} + //if (show_committed) { + // mi_debug_show_bitmap("committed slices", arena->slice_count, arena->slices_committed, false, NULL); + //} + // todo: abandoned slices + //if (show_purge) { + // purge_total += mi_debug_show_bitmap("purgeable slices", arena->slice_count, arena->slices_purge, false, NULL); + //} + if (show_pages) { + // mi_arena_pages_t* arena_pages = mi_heap_arena_pages(heap, arena); + // if (arena_pages != NULL) + { + const char* header1 = "chunks (p:page, f:full, s:singleton, P,F,S:not abandoned, i:arena-info, m:meta-data, ~:free-purgable, _:free-committed, .:free-reserved)"; + const char* header2 = (narrow ? "\n " : " "); + const char* header3 = "(chunk bin: S:small, M : medium, L : large, X : other)"; + page_total += mi_debug_show_chunks(header1, header2, header3, arena->slice_count, + mi_bbitmap_chunk_count(arena->slices_free), NULL, + arena->slices_free->chunkmap_bins, false, arena, narrow); + } } } - if (show_inuse) _mi_verbose_message("total inuse blocks : %zu\n", inuse_total); - if (show_abandoned) _mi_verbose_message("total abandoned blocks: %zu\n", abandoned_total); - if (show_purge) _mi_verbose_message("total purgeable blocks: %zu\n", purge_total); + // if (show_inuse) _mi_raw_message("total inuse slices : %zu\n", slice_total - free_total); + // if (show_abandoned) _mi_raw_message("total abandoned slices: %zu\n", abandoned_total); + if (show_pages) _mi_raw_message("total pages in arenas: %zu\n", page_total); +} + +void mi_debug_show_arenas(void) mi_attr_noexcept { + mi_debug_show_arenas_ex(mi_heap_main(), true /* show pages */, true /* narrow? */); +} + +void mi_arenas_print(void) mi_attr_noexcept { + mi_debug_show_arenas(); } @@ -1044,7 +2002,7 @@ void mi_debug_show_arenas(bool show_inuse, bool show_abandoned, bool show_purge) ----------------------------------------------------------- */ // reserve at a specific numa node int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { - if (arena_id != NULL) *arena_id = -1; + if (arena_id != NULL) *arena_id = NULL; if (pages==0) return 0; if (numa_node < -1) numa_node = -1; if (numa_node >= 0) numa_node = numa_node % _mi_os_numa_node_count(); @@ -1058,8 +2016,8 @@ int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_m } _mi_verbose_message("numa node %i: reserved %zu GiB huge pages (of the %zu GiB requested)\n", numa_node, pages_reserved, pages); - if (!mi_manage_os_memory_ex2(p, hsize, true, numa_node, exclusive, memid, arena_id)) { - _mi_os_free(p, hsize, memid, &_mi_stats_main); + if (!mi_manage_os_memory_ex2(_mi_subproc(), p, hsize, numa_node, exclusive, memid, NULL, NULL, arena_id)) { + _mi_os_free(p, hsize, memid); return ENOMEM; } return 0; @@ -1074,17 +2032,17 @@ int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t if (pages == 0) return 0; // pages per numa node - size_t numa_count = (numa_nodes > 0 ? numa_nodes : _mi_os_numa_node_count()); - if (numa_count <= 0) numa_count = 1; + int numa_count = (numa_nodes > 0 && numa_nodes <= INT_MAX ? (int)numa_nodes : _mi_os_numa_node_count()); + if (numa_count <= 0) { numa_count = 1; } const size_t pages_per = pages / numa_count; const size_t pages_mod = pages % numa_count; const size_t timeout_per = (timeout_msecs==0 ? 0 : (timeout_msecs / numa_count) + 50); // reserve evenly among numa nodes - for (size_t numa_node = 0; numa_node < numa_count && pages > 0; numa_node++) { + for (int numa_node = 0; numa_node < numa_count && pages > 0; numa_node++) { size_t node_pages = pages_per; // can be 0 - if (numa_node < pages_mod) node_pages++; - int err = mi_reserve_huge_os_pages_at(node_pages, (int)numa_node, timeout_per); + if ((size_t)numa_node < pages_mod) { node_pages++; } + int err = mi_reserve_huge_os_pages_at(node_pages, numa_node, timeout_per); if (err) return err; if (pages < node_pages) { pages = 0; @@ -1106,3 +2064,516 @@ int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserv return err; } + + + + +/* ----------------------------------------------------------- + Arena purge +----------------------------------------------------------- */ + +static long mi_arena_purge_delay(void) { + // <0 = no purging allowed, 0=immediate purging, >0=milli-second delay + const long delay = mi_option_get(mi_option_purge_delay); + const long mult = mi_option_get(mi_option_arena_purge_mult); + if (delay<0 || mult<0) { return -1; } + if (delay==0 || mult==0) { return 0; } + size_t total; + if (mi_mul_overflow((size_t)delay, (size_t)mult, &total)) { return delay; } + if (total > LONG_MAX) { return delay; } + return (long)total; +} + +// reset or decommit in an arena and update the commit bitmap +// assumes we own the area (i.e. slices_free is claimed by us) +// returns if the memory is no longer committed (versus reset which keeps the commit) +static bool mi_arena_purge(mi_arena_t* arena, size_t slice_index, size_t slice_count) { + mi_assert_internal(!arena->memid.is_pinned); + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + + const size_t size = mi_size_of_slices(slice_count); + void* const p = mi_arena_slice_start(arena, slice_index); + //const bool all_committed = mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count); + size_t already_committed; + mi_bitmap_setN(arena->slices_committed, slice_index, slice_count, &already_committed); // pretend all committed.. (as we lack a clearN call that counts the already set bits..) + const bool all_committed = (already_committed == slice_count); + const bool needs_recommit = _mi_os_purge_ex(p, size, all_committed /* allow reset? */, mi_size_of_slices(already_committed), arena->commit_fun, arena->commit_fun_arg); + + if (needs_recommit) { + // no longer committed + mi_bitmap_clearN(arena->slices_committed, slice_index, slice_count); + // we just counted in the purge to decommit all, but the some part was not committed so adjust that here + // mi_subproc_stat_decrease(arena->subproc, committed, mi_size_of_slices(slice_count - already_committed)); + } + else if (!all_committed) { + // we cannot assume any of these are committed any longer (even with reset since we did setN and may have marked uncommitted slices as committed) + mi_bitmap_clearN(arena->slices_committed, slice_index, slice_count); + // we adjust the commit count as parts will be re-committed + // mi_subproc_stat_decrease(arena->subproc, committed, mi_size_of_slices(already_committed)); + } + + return needs_recommit; +} + + +// Schedule a purge. This is usually delayed to avoid repeated decommit/commit calls. +// Note: assumes we (still) own the area as we may purge immediately +static void mi_arena_schedule_purge(mi_arena_t* arena, size_t slice_index, size_t slice_count) { + const long delay = mi_arena_purge_delay(); + if (arena->memid.is_pinned || delay < 0 || _mi_preloading()) return; // is purging allowed at all? + + mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count)); + if (delay == 0) { + // purge directly + mi_arena_purge(arena, slice_index, slice_count); + } + else { + // schedule purge + const mi_msecs_t expire = _mi_clock_now() + delay; + mi_msecs_t expire0 = 0; + if (mi_atomic_casi64_strong_acq_rel(&arena->purge_expire, &expire0, expire)) { + // expiration was not yet set + // maybe set the global arenas expire as well (if it wasn't set already) + mi_assert_internal(expire0==0); + mi_atomic_casi64_strong_acq_rel(&arena->subproc->purge_expire, &expire0, expire); + } + else { + // already an expiration was set + } + mi_bitmap_setN(arena->slices_purge, slice_index, slice_count, NULL); + } +} + +typedef struct mi_purge_visit_info_s { + mi_msecs_t now; + mi_msecs_t delay; + bool all_purged; + bool any_purged; +} mi_purge_visit_info_t; + +static bool mi_arena_try_purge_range(mi_arena_t* arena, size_t slice_index, size_t slice_count) { + mi_assert(slice_count < MI_BCHUNK_BITS); + if (mi_bbitmap_try_clearNC(arena->slices_free, slice_index, slice_count)) { + // purge + bool decommitted = mi_arena_purge(arena, slice_index, slice_count); MI_UNUSED(decommitted); + mi_assert_internal(!decommitted || mi_bitmap_is_clearN(arena->slices_committed, slice_index, slice_count)); + // and reset the free range + mi_bbitmap_setN(arena->slices_free, slice_index, slice_count); + return true; + } + else { + // was allocated again already + return false; + } +} + +static bool mi_arena_try_purge_visitor(size_t slice_index, size_t slice_count, mi_arena_t* arena, void* arg) { + mi_purge_visit_info_t* vinfo = (mi_purge_visit_info_t*)arg; + // try to purge: first claim the free blocks + if (mi_arena_try_purge_range(arena, slice_index, slice_count)) { + vinfo->any_purged = true; + vinfo->all_purged = true; + } + else if (slice_count > 1) + { + // failed to claim the full range, try per slice instead + for (size_t i = 0; i < slice_count; i++) { + const bool purged = mi_arena_try_purge_range(arena, slice_index + i, 1); + vinfo->any_purged = vinfo->any_purged || purged; + vinfo->all_purged = vinfo->all_purged && purged; + } + } + // don't clear the purge bits as that is done atomically be the _bitmap_forall_set_ranges + // mi_bitmap_clearN(arena->slices_purge, slice_index, slice_count); + return true; // continue +} + +// returns +// -1 = nothing was purged +// 0 = nothing was purged yet because have not yet reached the expire time +// 1 = some pages in the arena were purged +static int mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force) +{ + // check pre-conditions + if (arena->memid.is_pinned) return -1; + + // expired yet? + mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire); + if (!force) { + if (expire==0) return -1; + if (expire > now) return 0; + } + + // reset expire + mi_atomic_storei64_release(&arena->purge_expire, (mi_msecs_t)0); + mi_subproc_stat_counter_increase(arena->subproc, arena_purges, 1); + + // go through all purge info's (with max MI_BFIELD_BITS ranges at a time) + // this also clears those ranges atomically (so any newly freed blocks will get purged next + // time around) + mi_purge_visit_info_t vinfo = { now, mi_arena_purge_delay(), true /*all?*/, false /*any?*/}; + + // we purge by at least `minslices` to not fragment transparent huge pages for example + const size_t minslices = mi_slice_count_of_size(_mi_os_minimal_purge_size()); + _mi_bitmap_forall_setc_rangesn(arena->slices_purge, minslices, &mi_arena_try_purge_visitor, arena, &vinfo); + + return (vinfo.any_purged ? 1 : -1); +} + + +static void mi_arenas_try_purge(bool force, bool visit_all, mi_subproc_t* subproc, size_t tseq) +{ + // try purge can be called often so try to only run when needed + const long delay = mi_arena_purge_delay(); + if (_mi_preloading() || delay <= 0) return; // nothing will be scheduled + + // check if any arena needs purging? + const mi_msecs_t now = _mi_clock_now(); + const mi_msecs_t arenas_expire = mi_atomic_loadi64_acquire(&subproc->purge_expire); + if (!visit_all && !force && (arenas_expire == 0 || arenas_expire > now)) return; + + const size_t max_arena = mi_arenas_get_count(subproc); + if (max_arena == 0) return; + + // allow only one thread to purge at a time (todo: allow concurrent purging?) + static mi_atomic_guard_t purge_guard; + mi_atomic_guard(&purge_guard) + { + // increase global expire: at most one purge per delay cycle + if (arenas_expire > now) { mi_atomic_storei64_release(&subproc->purge_expire, now + (delay/10)); } + const size_t arena_start = tseq % max_arena; + size_t max_purge_count = (visit_all ? max_arena : (max_arena/4)+1); + bool all_visited = true; + bool any_purged = false; + for (size_t _i = 0; _i < max_arena; _i++) { + size_t i = _i + arena_start; + if (i >= max_arena) { i -= max_arena; } + mi_arena_t* arena = mi_arena_from_index(subproc,i); + if (arena != NULL) { + const int purged = mi_arena_try_purge(arena, now, force); + if (purged >= 0) { // purged, or arena expire is not yet reached + any_purged = true; + if (purged >= 1) { // purged + if (max_purge_count <= 1) { + all_visited = false; + break; + } + max_purge_count--; + } + } + } + } + if (all_visited && !any_purged) { + mi_atomic_storei64_release(&subproc->purge_expire, (mi_msecs_t)0); + } + } +} + + +/* ----------------------------------------------------------- + Visit all pages and blocks in a heap +----------------------------------------------------------- */ + +typedef struct mi_heap_visit_info_s { + mi_heap_t* heap; + mi_block_visit_fun* visitor; + void* arg; + bool visit_blocks; +} mi_heap_visit_info_t; + +static bool mi_heap_visit_page(mi_page_t* page, mi_heap_visit_info_t* vinfo) { + mi_heap_area_t area; + _mi_heap_area_init(&area, page); + mi_assert_internal(vinfo->heap == mi_page_heap(page)); + if (!vinfo->visitor(vinfo->heap, &area, NULL, area.block_size, vinfo->arg)) { + return false; + } + if (vinfo->visit_blocks) { + return _mi_theap_area_visit_blocks(&area, page, vinfo->visitor, vinfo->arg); + } + else { + return true; + } +} + +static bool mi_heap_visit_page_at(size_t slice_index, size_t slice_count, mi_arena_t* arena, void* arg) { + MI_UNUSED(slice_count); + mi_heap_visit_info_t* vinfo = (mi_heap_visit_info_t*)arg; + mi_page_t* page = mi_arena_page_at_slice(arena, slice_index); + return mi_heap_visit_page(page, vinfo); +} + +bool _mi_heap_visit_blocks(mi_heap_t* heap, bool abandoned_only, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) { + mi_assert(visitor!=NULL); + if (visitor==NULL) return false; + if (heap==NULL) { heap = mi_heap_main(); } + // visit all pages in a heap + // we don't have to claim because we assume we are the only thread running (with this heap). + // (but we could atomically claim as well by first doing abandoned_reclaim and afterwards reabandoning). + mi_heap_visit_info_t visit_info = { heap, visitor, arg, visit_blocks }; + bool ok = true; + mi_forall_arenas(heap, NULL, 0, arena) { + mi_arena_pages_t* arena_pages = mi_heap_arena_pages(heap, arena); + if (ok && arena_pages != NULL) { + if (abandoned_only) { + for (size_t bin = 0; ok && bin < MI_ARENA_BIN_COUNT; bin++) { + // todo: if we had a single abandoned page map as well, this can be faster. + if (mi_atomic_load_relaxed(&heap->abandoned_count[bin]) > 0) { + ok = _mi_bitmap_forall_set(arena_pages->pages_abandoned[bin], &mi_heap_visit_page_at, arena, &visit_info); + } + } + } + else { + ok = _mi_bitmap_forall_set(arena_pages->pages, &mi_heap_visit_page_at, arena, &visit_info); + } + } + } + mi_forall_arenas_end(); + if (!ok) return false; + + // visit abandoned pages in OS allocated memory + // (technically we don't need the initial lock as we assume we are the only thread running in this subproc) + mi_page_t* page = NULL; + mi_lock(&heap->os_abandoned_pages_lock) { + page = heap->os_abandoned_pages; + } + while (ok && page != NULL) { + mi_page_t* next = page->next; // read upfront in case the visitor frees the page + ok = mi_heap_visit_page(page, &visit_info); + page = next; + } + + return ok; +} + +bool mi_heap_visit_blocks(mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) { + return _mi_heap_visit_blocks(heap, false, visit_blocks, visitor, arg); +} + +bool mi_heap_visit_abandoned_blocks(mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) { + return _mi_heap_visit_blocks(heap, true, visit_blocks, visitor, arg); +} + + +typedef struct mi_heap_delete_visit_info_s { + mi_heap_t* heap_target; + mi_theap_t* theap_target; + mi_theap_t* theap; +} mi_heap_delete_visit_info_t; + +static bool mi_heap_delete_page(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) { + MI_UNUSED(block); MI_UNUSED(block_size); MI_UNUSED(heap); + mi_heap_delete_visit_info_t* info = (mi_heap_delete_visit_info_t*)arg; + mi_heap_t* heap_target = info->heap_target; + mi_theap_t* const theap = NULL; // info->theap; mi_assert_internal(_mi_theap_heap(theap) == heap); + mi_page_t* const page = (mi_page_t*)area->reserved1; + + mi_page_claim_ownership(page); // claim ownership + if (mi_page_is_abandoned(page)) { + _mi_arenas_page_unabandon(page,theap); + } + else { + page->next = page->prev = NULL; // yikes.. better not to try to access this from a thread later on.. + mi_page_set_theap(page,NULL); // set threadid to abandoned + } + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(mi_page_is_owned(page)); + + if (page->used==0) { + // free the page + _mi_arenas_page_free(page, theap); + } + else if (heap_target==NULL) { + #if MI_GUARDED + _mi_page_unguard_all(page); // remove potential interior guard pages + #endif + // destroy the page + page->used=0; // note: invariant `|local_free| + |free| == reserved - used` does not hold in this case + _mi_arenas_page_free(page, theap); + } + else { + // move the page to `heap_target` as an abandoned page + // first remove it from the current heap + const size_t sbin = _mi_page_stats_bin(page); + size_t slice_index; + size_t slice_count; + mi_arena_pages_t* arena_pages = NULL; + mi_arena_t* const arena = mi_page_arena_pages(page, &slice_index, &slice_count, &arena_pages); + mi_assert_internal(mi_bitmap_is_set(arena_pages->pages, slice_index)); + mi_bitmap_clear(arena_pages->pages, slice_index); + if (theap != NULL) { + mi_theap_stat_decrease(theap, page_bins[sbin], 1); + mi_theap_stat_decrease(theap, pages, 1); + } + else { + mi_heap_stat_decrease((mi_heap_t*)heap, page_bins[_mi_page_stats_bin(page)], 1); + mi_heap_stat_decrease((mi_heap_t*)heap, pages, 1); + } + mi_theap_t* theap_target = info->theap_target; + + // and then add it to the new target heap + mi_arena_pages_t* arena_pages_target = mi_heap_ensure_arena_pages(heap_target, arena); + if mi_unlikely(arena_pages_target==NULL) { + // if we cannot allocate this, we move it to the main heap instead (which does not require allocation) + heap_target = mi_heap_main(); + theap_target = mi_heap_theap(heap_target); + arena_pages_target = mi_heap_ensure_arena_pages(heap_target, arena); + mi_assert_internal(arena_pages_target!=NULL); + } + mi_assert_internal(mi_bitmap_is_clear(arena_pages_target->pages, slice_index)); + mi_bitmap_set(arena_pages_target->pages, slice_index); + page->heap = heap_target; + mi_theap_stat_increase(theap_target, page_bins[sbin], 1); + mi_theap_stat_increase(theap_target, pages, 1); + + // and abandon in the new heap + _mi_arenas_page_abandon(page,theap_target); + } + return true; +} + +static void mi_heap_delete_pages(mi_heap_t* heap, mi_heap_t* heap_target) { + mi_theap_t* const theap_target = (heap_target != NULL ? _mi_heap_theap(heap_target) : NULL); + // mi_theap_t* const theap = _mi_heap_theap(heap); + mi_heap_delete_visit_info_t info = { heap_target, theap_target, NULL }; + _mi_heap_visit_blocks(heap, false, false, &mi_heap_delete_page, &info); + #if MI_DEBUG>1 + // no more arena pages? + for (size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) { + mi_arena_pages_t* const arena_pages = mi_atomic_load_ptr_relaxed(mi_arena_pages_t, &heap->arena_pages[i]); + if (arena_pages!=NULL) { + mi_assert_internal(mi_bitmap_is_all_clear(arena_pages->pages)); + } + } + // nor os abandoned pages? + mi_lock(&heap->os_abandoned_pages_lock) { + + mi_assert_internal(heap->os_abandoned_pages == NULL); + } + // nor arena abandoned pages? + for (size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) { + mi_assert_internal(mi_atomic_load_relaxed(&heap->abandoned_count[i])==0); + } + #endif +} + +void _mi_heap_move_pages(mi_heap_t* heap_from, mi_heap_t* heap_to) { + if (_mi_is_heap_main(heap_from)) return; + if (heap_to==NULL) { heap_to = mi_heap_main(); } + mi_heap_delete_pages(heap_from, heap_to); +} + +void _mi_heap_destroy_pages(mi_heap_t* heap_from) { + if (_mi_is_heap_main(heap_from)) return; + mi_heap_delete_pages(heap_from, NULL); +} + +/* ----------------------------------------------------------- + Unloading and reloading an arena. +----------------------------------------------------------- */ +/* +static bool mi_arena_page_register(size_t slice_index, size_t slice_count, mi_arena_t* arena, void* arg) { + MI_UNUSED(arg); MI_UNUSED(slice_count); + mi_assert_internal(slice_count == 1); + mi_page_t* page = mi_arena_page_at_slice(arena, slice_index); + mi_assert_internal(mi_bitmap_is_setN(page->memid.mem.arena.arena->pages, page->memid.mem.arena.slice_index, 1)); + if (!_mi_page_map_register(page)) return false; // break + mi_assert_internal(_mi_ptr_page(page)==page); + return true; +} + +mi_decl_nodiscard static bool mi_arena_pages_reregister(mi_arena_t* arena) { + return _mi_bitmap_forall_set(arena->pages, &mi_arena_page_register, arena, NULL); +} + +mi_decl_export bool mi_arena_unload(mi_arena_id_t arena_id, void** base, size_t* accessed_size, size_t* full_size) { + mi_arena_t* arena = _mi_arena_from_id(arena_id); + if (arena==NULL) { + return false; + } + else if (!arena->is_exclusive) { + _mi_warning_message("cannot unload a non-exclusive arena (id %zu at %p)\n", arena_id, arena); + return false; + } + else if (arena->memid.memkind != MI_MEM_EXTERNAL) { + _mi_warning_message("can only unload managed arena's for external memory (id %zu at %p)\n", arena_id, arena); + return false; + } + + // find accessed size + const size_t asize = mi_size_of_slices(mi_arena_used_slices(arena)); + if (base != NULL) { *base = (void*)arena; } + if (full_size != NULL) { *full_size = arena->memid.mem.os.size; } + if (accessed_size != NULL) { *accessed_size = asize; } + + // adjust abandoned page count + mi_subproc_t* const subproc = arena->subproc; + for (size_t bin = 0; bin < MI_ARENA_BIN_COUNT; bin++) { + const size_t count = mi_bitmap_popcount(arena->pages_abandoned[bin]); + if (count > 0) { mi_atomic_decrement_acq_rel(&subproc->abandoned_count[bin]); } + } + + // unregister the pages + _mi_page_map_unregister_range(arena, asize); + + // set arena entry to NULL + const size_t count = mi_arenas_get_count(subproc); + for(size_t i = 0; i < count; i++) { + if (mi_arena_from_index(subproc, i) == arena) { + mi_atomic_store_ptr_release(mi_arena_t, &subproc->arenas[i], NULL); + if (i + 1 == count) { // try adjust the count? + size_t expected = count; + mi_atomic_cas_strong_acq_rel(&subproc->arena_count, &expected, count-1); + } + break; + } + } + return true; +} + +mi_decl_export bool mi_arena_reload(void* start, size_t size, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) { + // assume the memory area is already containing the arena + if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); } + if (start == NULL || size == 0) return false; + mi_arena_t* arena = (mi_arena_t*)start; + mi_memid_t memid = arena->memid; + if (memid.memkind != MI_MEM_EXTERNAL) { + _mi_warning_message("can only reload arena's from external memory (%p)\n", arena); + return false; + } + if (memid.mem.os.base != start) { + _mi_warning_message("the reloaded arena base address differs from the external memory (arena: %p, external: %p)\n", arena, start); + return false; + } + if (memid.mem.os.size != size) { + _mi_warning_message("the reloaded arena size differs from the external memory (arena size: %zu, external size: %zu)\n", arena->memid.mem.os.size, size); + return false; + } + if (!arena->is_exclusive) { + _mi_warning_message("the reloaded arena is not exclusive\n"); + return false; + } + + // re-initialize + arena->is_exclusive = true; + arena->commit_fun = commit_fun; + arena->commit_fun_arg = commit_fun_arg; + arena->subproc = _mi_subproc(); + if (!mi_arenas_add(arena->subproc, arena, arena_id)) { + return false; + } + if (!mi_arena_pages_reregister(arena)) { + // todo: clear arena entry in the subproc? + return false; + } + + // adjust abandoned page count + for (size_t bin = 0; bin < MI_ARENA_BIN_COUNT; bin++) { + const size_t count = mi_bitmap_popcount(arena->pages_abandoned[bin]); + if (count > 0) { mi_atomic_decrement_acq_rel(&arena->subproc->abandoned_count[bin]); } + } + + return true; +} + +*/ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.c index 4b6be66b..1b9e2854 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.c @@ -1,436 +1,1940 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2019-2023 Microsoft Research, Daan Leijen +Copyright (c) 2019-2024 Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ /* ---------------------------------------------------------------------------- -Concurrent bitmap that can set/reset sequences of bits atomically, -represented as an array of fields where each field is a machine word (`size_t`) - -There are two api's; the standard one cannot have sequences that cross -between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS). - -The `_across` postfixed functions do allow sequences that can cross over -between the fields. (This is used in arena allocation) +Concurrent bitmap that can set/reset sequences of bits atomically ---------------------------------------------------------------------------- */ #include "mimalloc.h" #include "mimalloc/internal.h" +#include "mimalloc/bits.h" +#include "mimalloc/prim.h" // _mi_prim_thread_yield #include "bitmap.h" -/* ----------------------------------------------------------- - Bitmap definition ------------------------------------------------------------ */ +#ifndef MI_OPT_SIMD +#define MI_OPT_SIMD 0 +#endif + +/* -------------------------------------------------------------------------------- + bfields +-------------------------------------------------------------------------------- */ -// The bit mask for a given number of blocks at a specified bit index. -static inline size_t mi_bitmap_mask_(size_t count, size_t bitidx) { - mi_assert_internal(count + bitidx <= MI_BITMAP_FIELD_BITS); - mi_assert_internal(count > 0); - if (count >= MI_BITMAP_FIELD_BITS) return MI_BITMAP_FIELD_FULL; - if (count == 0) return 0; - return ((((size_t)1 << count) - 1) << bitidx); +static inline size_t mi_bfield_ctz(mi_bfield_t x) { + return mi_ctz(x); } +static inline size_t mi_bfield_clz(mi_bfield_t x) { + return mi_clz(x); +} -/* ----------------------------------------------------------- - Claim a bit sequence atomically ------------------------------------------------------------ */ +static inline size_t mi_bfield_popcount(mi_bfield_t x) { + return mi_popcount(x); +} -// Try to atomically claim a sequence of `count` bits in a single -// field at `idx` in `bitmap`. Returns `true` on success. -inline bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx) -{ - mi_assert_internal(bitmap_idx != NULL); - mi_assert_internal(count <= MI_BITMAP_FIELD_BITS); - mi_assert_internal(count > 0); - mi_bitmap_field_t* field = &bitmap[idx]; - size_t map = mi_atomic_load_relaxed(field); - if (map==MI_BITMAP_FIELD_FULL) return false; // short cut - - // search for 0-bit sequence of length count - const size_t mask = mi_bitmap_mask_(count, 0); - const size_t bitidx_max = MI_BITMAP_FIELD_BITS - count; - -#ifdef MI_HAVE_FAST_BITSCAN - size_t bitidx = mi_ctz(~map); // quickly find the first zero bit if possible -#else - size_t bitidx = 0; // otherwise start at 0 -#endif - size_t m = (mask << bitidx); // invariant: m == mask shifted by bitidx - - // scan linearly for a free range of zero bits - while (bitidx <= bitidx_max) { - const size_t mapm = (map & m); - if (mapm == 0) { // are the mask bits free at bitidx? - mi_assert_internal((m >> bitidx) == mask); // no overflow? - const size_t newmap = (map | m); - mi_assert_internal((newmap^map) >> bitidx == mask); - if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { // TODO: use weak cas here? - // no success, another thread claimed concurrently.. keep going (with updated `map`) - continue; +static inline mi_bfield_t mi_bfield_clear_least_bit(mi_bfield_t x) { + return (x & (x-1)); +} + +// find the least significant bit that is set (i.e. count trailing zero's) +// return false if `x==0` (with `*idx` undefined) and true otherwise, +// with the `idx` is set to the bit index (`0 <= *idx < MI_BFIELD_BITS`). +static inline bool mi_bfield_find_least_bit(mi_bfield_t x, size_t* idx) { + return mi_bsf(x,idx); +} + +// find the most significant bit that is set. +// return false if `x==0` (with `*idx` undefined) and true otherwise, +// with the `idx` is set to the bit index (`0 <= *idx < MI_BFIELD_BITS`). +static inline bool mi_bfield_find_highest_bit(mi_bfield_t x, size_t* idx) { + return mi_bsr(x, idx); +} + + + +// find each set bit in a bit field `x` and clear it, until it becomes zero. +static inline bool mi_bfield_foreach_bit(mi_bfield_t* x, size_t* idx) { + const bool found = mi_bfield_find_least_bit(*x, idx); + *x = mi_bfield_clear_least_bit(*x); + return found; +} + +static inline mi_bfield_t mi_bfield_zero(void) { + return 0; +} + +static inline mi_bfield_t mi_bfield_one(void) { + return 1; +} + +static inline mi_bfield_t mi_bfield_all_set(void) { + return ~((mi_bfield_t)0); +} + +// mask of `bit_count` bits set shifted to the left by `shiftl` +static inline mi_bfield_t mi_bfield_mask(size_t bit_count, size_t shiftl) { + mi_assert_internal(bit_count > 0); + mi_assert_internal(bit_count + shiftl <= MI_BFIELD_BITS); + mi_assert_internal(shiftl < MI_BFIELD_BITS); + const mi_bfield_t mask0 = (bit_count < MI_BFIELD_BITS ? (mi_bfield_one() << bit_count)-1 : mi_bfield_all_set()); + return (mask0 << shiftl); +} + + +// ------- mi_bfield_atomic_set --------------------------------------- +// the `_set` functions return also the count of bits that were already set (for commit statistics) +// the `_clear` functions return also whether the new bfield is all clear or not (for the chunk_map) + +// Set a bit atomically. Returns `true` if the bit transitioned from 0 to 1 +static inline bool mi_bfield_atomic_set(_Atomic(mi_bfield_t)*b, size_t idx) { + mi_assert_internal(idx < MI_BFIELD_BITS); + const mi_bfield_t mask = mi_bfield_mask(1, idx);; + const mi_bfield_t old = mi_atomic_or_acq_rel(b, mask); + return ((old&mask) == 0); +} + +// Clear a bit atomically. Returns `true` if the bit transitioned from 1 to 0. +// `all_clear` is set if the new bfield is zero. +static inline bool mi_bfield_atomic_clear(_Atomic(mi_bfield_t)*b, size_t idx, bool* all_clear) { + mi_assert_internal(idx < MI_BFIELD_BITS); + const mi_bfield_t mask = mi_bfield_mask(1, idx);; + mi_bfield_t old = mi_atomic_and_acq_rel(b, ~mask); + if (all_clear != NULL) { *all_clear = ((old&~mask)==0); } + return ((old&mask) == mask); +} + +// Clear a bit but only when/once it is set. This is used by concurrent free's while +// the page is abandoned and mapped. This can incure a busy wait :-( but it should +// happen almost never (and is accounted for in the stats) +static inline void mi_bfield_atomic_clear_once_set(_Atomic(mi_bfield_t)*b, size_t idx) { + mi_assert_internal(idx < MI_BFIELD_BITS); + const mi_bfield_t mask = mi_bfield_mask(1, idx);; + mi_bfield_t old = mi_atomic_load_relaxed(b); + do { + if mi_unlikely((old&mask) == 0) { + old = mi_atomic_load_acquire(b); + if ((old&mask)==0) { + mi_subproc_stat_counter_increase(_mi_subproc(), pages_unabandon_busy_wait, 1); } - else { - // success, we claimed the bits! - *bitmap_idx = mi_bitmap_index_create(idx, bitidx); - return true; + while ((old&mask)==0) { // busy wait + _mi_prim_thread_yield(); + old = mi_atomic_load_acquire(b); } } + } while (!mi_atomic_cas_weak_acq_rel(b,&old, (old&~mask))); + mi_assert_internal((old&mask)==mask); // we should only clear when it was set +} + +// Set a mask set of bits atomically, and return true of the mask bits transitioned from all 0's to 1's. +// `already_set` contains the count of bits that were already set (used when committing ranges to account +// statistics correctly). +static inline bool mi_bfield_atomic_set_mask(_Atomic(mi_bfield_t)*b, mi_bfield_t mask, size_t* already_set) { + mi_assert_internal(mask != 0); + mi_bfield_t old = mi_atomic_load_relaxed(b); + while (!mi_atomic_cas_weak_acq_rel(b, &old, old|mask)) {}; // try to atomically set the mask bits until success + if (already_set!=NULL) { *already_set = mi_bfield_popcount(old&mask); } + return ((old&mask) == 0); +} + +// Clear a mask set of bits atomically, and return true of the mask bits transitioned from all 1's to 0's +// `all_clear` is set to `true` if the new bfield became zero. +static inline bool mi_bfield_atomic_clear_mask(_Atomic(mi_bfield_t)*b, mi_bfield_t mask, bool* all_clear) { + mi_assert_internal(mask != 0); + mi_bfield_t old = mi_atomic_load_relaxed(b); + while (!mi_atomic_cas_weak_acq_rel(b, &old, old&~mask)) {}; // try to atomically clear the mask bits until success + if (all_clear != NULL) { *all_clear = ((old&~mask)==0); } + return ((old&mask) == mask); +} + +static inline bool mi_bfield_atomic_setX(_Atomic(mi_bfield_t)*b, size_t* already_set) { + const mi_bfield_t old = mi_atomic_exchange_release(b, mi_bfield_all_set()); + if (already_set!=NULL) { *already_set = mi_bfield_popcount(old); } + return (old==0); +} + +// static inline bool mi_bfield_atomic_clearX(_Atomic(mi_bfield_t)*b, bool* all_clear) { +// const mi_bfield_t old = mi_atomic_exchange_release(b, mi_bfield_zero()); +// if (all_clear!=NULL) { *all_clear = true; } +// return (~old==0); +// } + +// ------- mi_bfield_atomic_try_clear --------------------------------------- + + +// Tries to clear a mask atomically, and returns true if the mask bits atomically transitioned from mask to 0 +// and false otherwise (leaving the bit field as is). +// `all_clear` is set to `true` if the new bfield became zero. +static inline bool mi_bfield_atomic_try_clear_mask_of(_Atomic(mi_bfield_t)*b, mi_bfield_t mask, mi_bfield_t expect, bool* all_clear) { + mi_assert_internal(mask != 0); + // try to atomically clear the mask bits + do { + if ((expect & mask) != mask) { // are all bits still set? + if (all_clear != NULL) { *all_clear = (expect == 0); } + return false; + } + } while (!mi_atomic_cas_weak_acq_rel(b, &expect, expect & ~mask)); + if (all_clear != NULL) { *all_clear = ((expect & ~mask) == 0); } + return true; +} + +static inline bool mi_bfield_atomic_try_clear_mask(_Atomic(mi_bfield_t)* b, mi_bfield_t mask, bool* all_clear) { + mi_assert_internal(mask != 0); + const mi_bfield_t expect = mi_atomic_load_relaxed(b); + return mi_bfield_atomic_try_clear_mask_of(b, mask, expect, all_clear); +} + +// Tries to clear a bit atomically. Returns `true` if the bit transitioned from 1 to 0 +// and `false` otherwise leaving the bfield `b` as-is. +// `all_clear` is set to true if the new bfield became zero (and false otherwise) +mi_decl_maybe_unused static inline bool mi_bfield_atomic_try_clear(_Atomic(mi_bfield_t)* b, size_t idx, bool* all_clear) { + mi_assert_internal(idx < MI_BFIELD_BITS); + const mi_bfield_t mask = mi_bfield_one()<bfields[i], idx); + if (already_set != NULL) { *already_set = (was_clear ? 0 : 1); } + return was_clear; +} + +// Set `0 < n <= MI_BFIELD_BITS`, and return true of the mask bits transitioned from all 0's to 1's. +// `already_set` contains the count of bits that were already set (used when committing ranges to account +// statistics correctly). +// Can cross over two bfields. +static inline bool mi_bchunk_setNX(mi_bchunk_t* chunk, size_t cidx, size_t n, size_t* already_set) { + mi_assert_internal(cidx < MI_BCHUNK_BITS); + mi_assert_internal(n > 0 && n <= MI_BFIELD_BITS); + const size_t i = cidx / MI_BFIELD_BITS; + const size_t idx = cidx % MI_BFIELD_BITS; + if mi_likely(idx + n <= MI_BFIELD_BITS) { + // within one field + return mi_bfield_atomic_set_mask(&chunk->bfields[i], mi_bfield_mask(n,idx), already_set); + } + else { + // spanning two fields + const size_t m = MI_BFIELD_BITS - idx; // bits to clear in the first field + mi_assert_internal(m < n); + mi_assert_internal(i < MI_BCHUNK_FIELDS - 1); + mi_assert_internal(idx + m <= MI_BFIELD_BITS); + size_t already_set1; + const bool all_set1 = mi_bfield_atomic_set_mask(&chunk->bfields[i], mi_bfield_mask(m, idx), &already_set1); + mi_assert_internal(n - m > 0); + mi_assert_internal(n - m < MI_BFIELD_BITS); + size_t already_set2; + const bool all_set2 = mi_bfield_atomic_set_mask(&chunk->bfields[i+1], mi_bfield_mask(n - m, 0), &already_set2); + if (already_set != NULL) { *already_set = already_set1 + already_set2; } + return (all_set1 && all_set2); + } +} + +// Set a sequence of `n` bits within a chunk. +// Returns true if all bits transitioned from 0 to 1 (or 1 to 0). +mi_decl_noinline static bool mi_bchunk_xsetNC(mi_xset_t set, mi_bchunk_t* chunk, size_t cidx, size_t n, size_t* palready_set, bool* pmaybe_all_clear) { + mi_assert_internal(cidx + n <= MI_BCHUNK_BITS); + mi_assert_internal(n>0); + bool all_transition = true; + bool maybe_all_clear = true; + size_t total_already_set = 0; + size_t idx = cidx % MI_BFIELD_BITS; + size_t field = cidx / MI_BFIELD_BITS; + while (n > 0) { + size_t m = MI_BFIELD_BITS - idx; // m is the bits to xset in this field + if (m > n) { m = n; } + mi_assert_internal(idx + m <= MI_BFIELD_BITS); + mi_assert_internal(field < MI_BCHUNK_FIELDS); + const mi_bfield_t mask = mi_bfield_mask(m, idx); + size_t already_set = 0; + bool all_clear = false; + const bool transition = (set ? mi_bfield_atomic_set_mask(&chunk->bfields[field], mask, &already_set) + : mi_bfield_atomic_clear_mask(&chunk->bfields[field], mask, &all_clear)); + mi_assert_internal((transition && already_set == 0) || (!transition && already_set > 0)); + all_transition = all_transition && transition; + total_already_set += already_set; + maybe_all_clear = maybe_all_clear && all_clear; + // next field + field++; + idx = 0; + mi_assert_internal(m <= n); + n -= m; + } + if (palready_set!=NULL) { *palready_set = total_already_set; } + if (pmaybe_all_clear!=NULL) { *pmaybe_all_clear = maybe_all_clear; } + return all_transition; +} + +static inline bool mi_bchunk_setN(mi_bchunk_t* chunk, size_t cidx, size_t n, size_t* already_set) { + mi_assert_internal(n>0 && n <= MI_BCHUNK_BITS); + if (n==1) return mi_bchunk_set(chunk, cidx, already_set); + // if (n==8 && (cidx%8) == 0) return mi_bchunk_set8(chunk, cidx, already_set); + // if (n==MI_BFIELD_BITS) return mi_bchunk_setX(chunk, cidx, already_set); + if (n<=MI_BFIELD_BITS) return mi_bchunk_setNX(chunk, cidx, n, already_set); + return mi_bchunk_xsetNC(MI_BIT_SET, chunk, cidx, n, already_set, NULL); +} + +// ------- mi_bchunk_clear --------------------------------------- + +static inline bool mi_bchunk_clear(mi_bchunk_t* chunk, size_t cidx, bool* all_clear) { + mi_assert_internal(cidx < MI_BCHUNK_BITS); + const size_t i = cidx / MI_BFIELD_BITS; + const size_t idx = cidx % MI_BFIELD_BITS; + return mi_bfield_atomic_clear(&chunk->bfields[i], idx, all_clear); +} + +static inline bool mi_bchunk_clearN(mi_bchunk_t* chunk, size_t cidx, size_t n, bool* maybe_all_clear) { + mi_assert_internal(n>0 && n <= MI_BCHUNK_BITS); + if (n==1) return mi_bchunk_clear(chunk, cidx, maybe_all_clear); + // if (n==8) return mi_bchunk_clear8(chunk, cidx, maybe_all_clear); + // if (n==MI_BFIELD_BITS) return mi_bchunk_clearX(chunk, cidx, maybe_all_clear); + // TODO: implement mi_bchunk_xsetNX instead of setNX + return mi_bchunk_xsetNC(MI_BIT_CLEAR, chunk, cidx, n, NULL, maybe_all_clear); +} + +// Check if a sequence of `n` bits within a chunk are all set/cleared. +// This can cross bfield's +mi_decl_noinline static size_t mi_bchunk_popcountNC(mi_bchunk_t* chunk, size_t field_idx, size_t idx, size_t n) { + mi_assert_internal((field_idx*MI_BFIELD_BITS) + idx + n <= MI_BCHUNK_BITS); + size_t count = 0; + while (n > 0) { + size_t m = MI_BFIELD_BITS - idx; // m is the bits to xset in this field + if (m > n) { m = n; } + mi_assert_internal(idx + m <= MI_BFIELD_BITS); + mi_assert_internal(field_idx < MI_BCHUNK_FIELDS); + const size_t mask = mi_bfield_mask(m, idx); + count += mi_bfield_atomic_popcount_mask(&chunk->bfields[field_idx], mask); + // next field + field_idx++; + idx = 0; + n -= m; + } + return count; +} + +// Count set bits a sequence of `n` bits. +static inline size_t mi_bchunk_popcountN(mi_bchunk_t* chunk, size_t cidx, size_t n) { + mi_assert_internal(cidx + n <= MI_BCHUNK_BITS); + mi_assert_internal(n>0); + if (n==0) return 0; + const size_t i = cidx / MI_BFIELD_BITS; + const size_t idx = cidx % MI_BFIELD_BITS; + if (n==1) { return (mi_bfield_atomic_is_set(&chunk->bfields[i], idx) ? 1 : 0); } + if (idx + n <= MI_BFIELD_BITS) { return mi_bfield_atomic_popcount_mask(&chunk->bfields[i], mi_bfield_mask(n, idx)); } + return mi_bchunk_popcountNC(chunk, i, idx, n); +} + + +// ------- mi_bchunk_is_xset --------------------------------------- + +// Check if a sequence of `n` bits within a chunk are all set/cleared. +// This can cross bfield's +mi_decl_noinline static bool mi_bchunk_is_xsetNC(mi_xset_t set, const mi_bchunk_t* chunk, size_t field_idx, size_t idx, size_t n) { + mi_assert_internal((field_idx*MI_BFIELD_BITS) + idx + n <= MI_BCHUNK_BITS); + while (n > 0) { + size_t m = MI_BFIELD_BITS - idx; // m is the bits to xset in this field + if (m > n) { m = n; } + mi_assert_internal(idx + m <= MI_BFIELD_BITS); + mi_assert_internal(field_idx < MI_BCHUNK_FIELDS); + const size_t mask = mi_bfield_mask(m, idx); + if (!mi_bfield_atomic_is_xset_mask(set, &chunk->bfields[field_idx], mask)) { + return false; + } + // next field + field_idx++; + idx = 0; + n -= m; + } + return true; +} + +// Check if a sequence of `n` bits within a chunk are all set/cleared. +static inline bool mi_bchunk_is_xsetN(mi_xset_t set, const mi_bchunk_t* chunk, size_t cidx, size_t n) { + mi_assert_internal(cidx + n <= MI_BCHUNK_BITS); + mi_assert_internal(n>0); + if (n==0) return true; + const size_t i = cidx / MI_BFIELD_BITS; + const size_t idx = cidx % MI_BFIELD_BITS; + if (n==1) { return mi_bfield_atomic_is_xset(set, &chunk->bfields[i], idx); } + if (idx + n <= MI_BFIELD_BITS) { return mi_bfield_atomic_is_xset_mask(set, &chunk->bfields[i], mi_bfield_mask(n, idx)); } + return mi_bchunk_is_xsetNC(set, chunk, i, idx, n); +} + + +// ------- mi_bchunk_try_clear --------------------------------------- + +// Clear `0 < n <= MI_BITFIELD_BITS`. Can cross over a bfield boundary. +static inline bool mi_bchunk_try_clearNX(mi_bchunk_t* chunk, size_t cidx, size_t n, bool* pmaybe_all_clear) { + mi_assert_internal(cidx < MI_BCHUNK_BITS); + mi_assert_internal(n <= MI_BFIELD_BITS); + const size_t i = cidx / MI_BFIELD_BITS; + const size_t idx = cidx % MI_BFIELD_BITS; + if mi_likely(idx + n <= MI_BFIELD_BITS) { + // within one field + return mi_bfield_atomic_try_clear_mask(&chunk->bfields[i], mi_bfield_mask(n, idx), pmaybe_all_clear); + } + else { + // spanning two fields (todo: use double-word atomic ops?) + const size_t m = MI_BFIELD_BITS - idx; // bits to clear in the first field + mi_assert_internal(m < n); + mi_assert_internal(i < MI_BCHUNK_FIELDS - 1); + bool field1_is_clear; + if (!mi_bfield_atomic_try_clear_mask(&chunk->bfields[i], mi_bfield_mask(m, idx), &field1_is_clear)) return false; + // try the second field as well + mi_assert_internal(n - m > 0); + mi_assert_internal(n - m < MI_BFIELD_BITS); + bool field2_is_clear; + if (!mi_bfield_atomic_try_clear_mask(&chunk->bfields[i+1], mi_bfield_mask(n - m, 0), &field2_is_clear)) { + // we failed to clear the second field, restore the first one + mi_bfield_atomic_set_mask(&chunk->bfields[i], mi_bfield_mask(m, idx), NULL); + return false; + } + if (pmaybe_all_clear != NULL) { *pmaybe_all_clear = field1_is_clear && field2_is_clear; } + return true; + } +} + +// Clear a full aligned bfield. +// static inline bool mi_bchunk_try_clearX(mi_bchunk_t* chunk, size_t cidx, bool* pmaybe_all_clear) { +// mi_assert_internal(cidx < MI_BCHUNK_BITS); +// mi_assert_internal((cidx%MI_BFIELD_BITS) == 0); +// const size_t i = cidx / MI_BFIELD_BITS; +// return mi_bfield_atomic_try_clearX(&chunk->bfields[i], pmaybe_all_clear); +// } + +// Try to atomically clear a sequence of `n` bits within a chunk. +// Returns true if all bits transitioned from 1 to 0, +// and false otherwise leaving all bit fields as is. +// Note: this is the complex one as we need to unwind partial atomic operations if we fail halfway.. +// `maybe_all_clear` is set to `true` if all the bfields involved become zero. +mi_decl_noinline static bool mi_bchunk_try_clearNC(mi_bchunk_t* chunk, size_t cidx, size_t n, bool* pmaybe_all_clear) { + mi_assert_internal(cidx + n <= MI_BCHUNK_BITS); + mi_assert_internal(n>0); + if (pmaybe_all_clear != NULL) { *pmaybe_all_clear = true; } + if (n==0) return true; + + // first field + const size_t start_idx = cidx % MI_BFIELD_BITS; + const size_t start_field = cidx / MI_BFIELD_BITS; + size_t field = start_field; + size_t m = MI_BFIELD_BITS - start_idx; // m are the bits to clear in this field + if (m > n) { m = n; } + mi_assert_internal(start_idx + m <= MI_BFIELD_BITS); + mi_assert_internal(start_field < MI_BCHUNK_FIELDS); + const mi_bfield_t mask_start = mi_bfield_mask(m, start_idx); + bool maybe_all_clear; + if (!mi_bfield_atomic_try_clear_mask(&chunk->bfields[field], mask_start, &maybe_all_clear)) return false; + + // done? + mi_assert_internal(m <= n); + n -= m; + + // continue with mid fields and last field: if these fail we need to recover by unsetting previous fields + // mid fields? + while (n >= MI_BFIELD_BITS) { + field++; + mi_assert_internal(field < MI_BCHUNK_FIELDS); + bool field_is_clear; + if (!mi_bfield_atomic_try_clearX(&chunk->bfields[field], &field_is_clear)) goto restore; + maybe_all_clear = maybe_all_clear && field_is_clear; + n -= MI_BFIELD_BITS; + } + + // last field? + if (n > 0) { + mi_assert_internal(n < MI_BFIELD_BITS); + field++; + mi_assert_internal(field < MI_BCHUNK_FIELDS); + const mi_bfield_t mask_end = mi_bfield_mask(n, 0); + bool field_is_clear; + if (!mi_bfield_atomic_try_clear_mask(&chunk->bfields[field], mask_end, &field_is_clear)) goto restore; + maybe_all_clear = maybe_all_clear && field_is_clear; + } + + if (pmaybe_all_clear != NULL) { *pmaybe_all_clear = maybe_all_clear; } + return true; + +restore: + // `field` is the index of the field that failed to set atomically; we need to restore all previous fields + mi_assert_internal(field > start_field); + while( field > start_field) { + field--; + if (field == start_field) { + mi_bfield_atomic_set_mask(&chunk->bfields[field], mask_start, NULL); + } else { - // on to the next bit range -#ifdef MI_HAVE_FAST_BITSCAN - mi_assert_internal(mapm != 0); - const size_t shift = (count == 1 ? 1 : (MI_INTPTR_BITS - mi_clz(mapm) - bitidx)); - mi_assert_internal(shift > 0 && shift <= count); -#else - const size_t shift = 1; + mi_bfield_atomic_setX(&chunk->bfields[field], NULL); // mid-field: set all bits again + } + } + return false; +} + + +static inline bool mi_bchunk_try_clearN(mi_bchunk_t* chunk, size_t cidx, size_t n, bool* maybe_all_clear) { + mi_assert_internal(n>0); + // if (n==MI_BFIELD_BITS) return mi_bchunk_try_clearX(chunk, cidx, maybe_all_clear); + if (n<=MI_BFIELD_BITS) return mi_bchunk_try_clearNX(chunk, cidx, n, maybe_all_clear); + return mi_bchunk_try_clearNC(chunk, cidx, n, maybe_all_clear); +} + + +// ------- mi_bchunk_try_find_and_clear --------------------------------------- + +#if MI_OPT_SIMD && defined(__AVX2__) +mi_decl_maybe_unused static inline __m256i mi_mm256_zero(void) { + return _mm256_setzero_si256(); +} +mi_decl_maybe_unused static inline __m256i mi_mm256_ones(void) { + return _mm256_set1_epi64x(~0); +} +mi_decl_maybe_unused static inline bool mi_mm256_is_ones(__m256i vec) { + return _mm256_testc_si256(vec, _mm256_cmpeq_epi32(vec, vec)); +} +mi_decl_maybe_unused static inline bool mi_mm256_is_zero( __m256i vec) { + return _mm256_testz_si256(vec,vec); +} #endif - bitidx += shift; - m <<= shift; + +static inline bool mi_bchunk_try_find_and_clear_at(mi_bchunk_t* chunk, size_t chunk_idx, size_t* pidx) { + mi_assert_internal(chunk_idx < MI_BCHUNK_FIELDS); + // note: this must be acquire (and not relaxed), or otherwise the AVX code below can loop forever + // as the compiler won't reload the registers vec1 and vec2 from memory again. + const mi_bfield_t b = mi_atomic_load_acquire(&chunk->bfields[chunk_idx]); + size_t idx; + if (mi_bfield_find_least_bit(b, &idx)) { // find the least bit + if mi_likely(mi_bfield_atomic_try_clear_mask_of(&chunk->bfields[chunk_idx], mi_bfield_mask(1,idx), b, NULL)) { // clear it atomically + *pidx = (chunk_idx*MI_BFIELD_BITS) + idx; + mi_assert_internal(*pidx < MI_BCHUNK_BITS); + return true; } } - // no bits found return false; } -// Find `count` bits of 0 and set them to 1 atomically; returns `true` on success. -// Starts at idx, and wraps around to search in all `bitmap_fields` fields. -// `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields. -bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx) { - size_t idx = start_field_idx; - for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) { - if (idx >= bitmap_fields) { idx = 0; } // wrap - if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) { +// Find least 1-bit in a chunk and try to clear it atomically +// set `*pidx` to the bit index (0 <= *pidx < MI_BCHUNK_BITS) on success. +// This is used to find free slices and abandoned pages and should be efficient. +// todo: try neon version +static inline bool mi_bchunk_try_find_and_clear(mi_bchunk_t* chunk, size_t* pidx) { + #if MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==256) + for(int tries=0; tries<4; tries++) { // paranoia: at most 4 tries + const __m256i vec = _mm256_load_si256((const __m256i*)chunk->bfields); + const __m256i vcmp = _mm256_cmpeq_epi64(vec, mi_mm256_zero()); // (elem64 == 0 ? 0xFF : 0) + const uint32_t mask = ~_mm256_movemask_epi8(vcmp); // mask of most significant bit of each byte (so each 8 bits are all set or clear) + // mask is inverted, so each 8-bits is 0xFF iff the corresponding elem64 has a bit set (and thus can be cleared) + if (mask==0) return false; + mi_assert_internal((_tzcnt_u32(mask)%8) == 0); // tzcnt == 0, 8, 16, or 24 + const size_t chunk_idx = _tzcnt_u32(mask) / 8; + if (mi_bchunk_try_find_and_clear_at(chunk, chunk_idx, pidx)) return true; + // try again + // note: there must be an atomic release/acquire in between or otherwise the registers may not be reloaded + // we add an explicit memory barrier as older gcc compilers do not reload the registers even with an atomic acquire (issue #1206) + #if defined(__GNUC__) + __asm __volatile ("" : : "g"(chunk) : "memory"); + #endif + } + #elif MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==512) + for(int tries=0; tries<4; tries++) { // paranoia: at most 4 tries + size_t chunk_idx = 0; + #if 0 + // one vector at a time + __m256i vec = _mm256_load_si256((const __m256i*)chunk->bfields); + if (mi_mm256_is_zero(vec)) { + chunk_idx += 4; + vec = _mm256_load_si256(((const __m256i*)chunk->bfields) + 1); + } + const __m256i vcmp = _mm256_cmpeq_epi64(vec, mi_mm256_zero()); // (elem64 == 0 ? 0xFF : 0) + const uint32_t mask = ~_mm256_movemask_epi8(vcmp); // mask of most significant bit of each byte (so each 8 bits are all set or clear) + // mask is inverted, so each 8-bits is 0xFF iff the corresponding elem64 has a bit set (and thus can be cleared) + if (mask==0) return false; + mi_assert_internal((_tzcnt_u32(mask)%8) == 0); // tzcnt == 0, 8, 16, or 24 + chunk_idx += _tzcnt_u32(mask) / 8; + #else + // a cache line is 64b so we can just as well load all at the same time + const __m256i vec1 = _mm256_load_si256((const __m256i*)chunk->bfields); + const __m256i vec2 = _mm256_load_si256(((const __m256i*)chunk->bfields)+1); + const __m256i cmpv = mi_mm256_zero(); + const __m256i vcmp1 = _mm256_cmpeq_epi64(vec1, cmpv); // (elem64 == 0 ? 0xFF : 0) + const __m256i vcmp2 = _mm256_cmpeq_epi64(vec2, cmpv); // (elem64 == 0 ? 0xFF : 0) + const uint32_t mask1 = ~_mm256_movemask_epi8(vcmp1); // mask of most significant bit of each byte (so each 8 bits are all set or clear) + const uint32_t mask2 = ~_mm256_movemask_epi8(vcmp2); // mask of most significant bit of each byte (so each 8 bits are all set or clear) + const uint64_t mask = ((uint64_t)mask2 << 32) | mask1; + // mask is inverted, so each 8-bits is 0xFF iff the corresponding elem64 has a bit set (and thus can be cleared) + if (mask==0) return false; + mi_assert_internal((_tzcnt_u64(mask)%8) == 0); // tzcnt == 0, 8, 16, 24 , .. + chunk_idx = mi_ctz(mask) / 8; + #endif + if (mi_bchunk_try_find_and_clear_at(chunk, chunk_idx, pidx)) return true; + // try again + // note: there must be an atomic release/acquire in between or otherwise the registers may not be reloaded + // we add an explicit memory barrier as older gcc compilers do not reload the registers even with an atomic acquire (issue #1206) + #if defined(__GNUC__) + __asm __volatile ("" : : "g"(chunk) : "memory"); + #endif + } + #elif MI_OPT_SIMD && (MI_BCHUNK_BITS==512) && MI_ARCH_ARM64 + for(int tries=0; tries<4; tries++) { // paranoia: at most 4 tries + // a cache line is 64b so we can just as well load all at the same time (?) + const uint64x2_t vzero1_lo = vceqzq_u64(vld1q_u64((uint64_t*)chunk->bfields)); // 2x64 bit is_zero + const uint64x2_t vzero1_hi = vceqzq_u64(vld1q_u64((uint64_t*)chunk->bfields + 2)); // 2x64 bit is_zero + const uint64x2_t vzero2_lo = vceqzq_u64(vld1q_u64((uint64_t*)chunk->bfields + 4)); // 2x64 bit is_zero + const uint64x2_t vzero2_hi = vceqzq_u64(vld1q_u64((uint64_t*)chunk->bfields + 6)); // 2x64 bit is_zero + const uint32x4_t vzero1 = vuzp1q_u32(vreinterpretq_u32_u64(vzero1_lo),vreinterpretq_u32_u64(vzero1_hi)); // unzip even elements: narrow to 4x32 bit is_zero () + const uint32x4_t vzero2 = vuzp1q_u32(vreinterpretq_u32_u64(vzero2_lo),vreinterpretq_u32_u64(vzero2_hi)); // unzip even elements: narrow to 4x32 bit is_zero () + const uint32x4_t vzero1x = vreinterpretq_u32_u64(vshrq_n_u64(vreinterpretq_u64_u32(vzero1), 24)); // shift-right 2x32bit elem by 24: lo 16 bits contain the 2 lo bytes + const uint32x4_t vzero2x = vreinterpretq_u32_u64(vshrq_n_u64(vreinterpretq_u64_u32(vzero2), 24)); + const uint16x8_t vzero12 = vreinterpretq_u16_u32(vuzp1q_u32(vzero1x,vzero2x)); // unzip even 32-bit elements into one vector + const uint8x8_t vzero = vmovn_u16(vzero12); // narrow the bottom 16-bits + const uint64_t mask = ~vget_lane_u64(vreinterpret_u64_u8(vzero), 0); // 1 byte for each bfield (0xFF => bfield has a bit set) + if (mask==0) return false; + mi_assert_internal((mi_ctz(mask)%8) == 0); // tzcnt == 0, 8, 16, 24 , .. + const size_t chunk_idx = mi_ctz(mask) / 8; + if (mi_bchunk_try_find_and_clear_at(chunk, chunk_idx, pidx)) return true; + // try again + // note: there must be an atomic release/acquire in between or otherwise the registers may not be reloaded + // we add an explicit memory barrier as older gcc compilers do not reload the registers even with an atomic acquire (issue #1206) + #if defined(__GNUC__) + __asm __volatile ("" : : "g"(chunk) : "memory"); + #endif + } + #else + for (int i = 0; i < MI_BCHUNK_FIELDS; i++) { + if (mi_bchunk_try_find_and_clear_at(chunk, i, pidx)) return true; + } + #endif + return false; +} + +static inline bool mi_bchunk_try_find_and_clear_1(mi_bchunk_t* chunk, size_t n, size_t* pidx) { + mi_assert_internal(n==1); MI_UNUSED(n); + return mi_bchunk_try_find_and_clear(chunk, pidx); +} + +mi_decl_maybe_unused static inline bool mi_bchunk_try_find_and_clear8_at(mi_bchunk_t* chunk, size_t chunk_idx, size_t* pidx) { + const mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[chunk_idx]); + // has_set8 has low bit in each byte set if the byte in x == 0xFF + const mi_bfield_t has_set8 = + ((~b - MI_BFIELD_LO_BIT8) & // high bit set if byte in x is 0xFF or < 0x7F + (b & MI_BFIELD_HI_BIT8)) // high bit set if byte in x is >= 0x80 + >> 7; // shift high bit to low bit + size_t idx; + if (mi_bfield_find_least_bit(has_set8, &idx)) { // find least 1-bit + mi_assert_internal(idx <= (MI_BFIELD_BITS - 8)); + mi_assert_internal((idx%8)==0); + if mi_likely(mi_bfield_atomic_try_clear_mask_of(&chunk->bfields[chunk_idx], (mi_bfield_t)0xFF << idx, b, NULL)) { // unset the byte atomically + *pidx = (chunk_idx*MI_BFIELD_BITS) + idx; + mi_assert_internal(*pidx + 8 <= MI_BCHUNK_BITS); return true; } } return false; } -// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled -bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, - const size_t start_field_idx, const size_t count, - mi_bitmap_pred_fun_t pred_fun, void* pred_arg, - mi_bitmap_index_t* bitmap_idx) { - size_t idx = start_field_idx; - for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) { - if (idx >= bitmap_fields) idx = 0; // wrap - if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) { - if (pred_fun == NULL || pred_fun(*bitmap_idx, pred_arg)) { +// find least aligned byte in a chunk with all bits set, and try unset it atomically +// set `*pidx` to its bit index (0 <= *pidx < MI_BCHUNK_BITS) on success. +// Used to find medium size pages in the free blocks. +// todo: try neon version +static mi_decl_noinline bool mi_bchunk_try_find_and_clear8(mi_bchunk_t* chunk, size_t* pidx) { + #if MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==512) + while (true) { + // since a cache-line is 64b, load all at once + const __m256i vec1 = _mm256_load_si256((const __m256i*)chunk->bfields); + const __m256i vec2 = _mm256_load_si256((const __m256i*)chunk->bfields+1); + const __m256i cmpv = mi_mm256_ones(); + const __m256i vcmp1 = _mm256_cmpeq_epi8(vec1, cmpv); // (byte == ~0 ? 0xFF : 0) + const __m256i vcmp2 = _mm256_cmpeq_epi8(vec2, cmpv); // (byte == ~0 ? 0xFF : 0) + const uint32_t mask1 = _mm256_movemask_epi8(vcmp1); // mask of most significant bit of each byte + const uint32_t mask2 = _mm256_movemask_epi8(vcmp2); // mask of most significant bit of each byte + const uint64_t mask = ((uint64_t)mask2 << 32) | mask1; + // mask is inverted, so each bit is 0xFF iff the corresponding byte has a bit set (and thus can be cleared) + if (mask==0) return false; + const size_t bidx = _tzcnt_u64(mask); // byte-idx of the byte in the chunk + const size_t chunk_idx = bidx / 8; + const size_t idx = (bidx % 8)*8; + mi_assert_internal(chunk_idx < MI_BCHUNK_FIELDS); + if mi_likely(mi_bfield_atomic_try_clear8(&chunk->bfields[chunk_idx], idx, NULL)) { // clear it atomically + *pidx = (chunk_idx*MI_BFIELD_BITS) + idx; + mi_assert_internal(*pidx + 8 <= MI_BCHUNK_BITS); + return true; + } + // try again + // note: there must be an atomic release/acquire in between or otherwise the registers may not be reloaded } + } + #else + for (int i = 0; i < MI_BCHUNK_FIELDS; i++) { + if (mi_bchunk_try_find_and_clear8_at(chunk, i, pidx)) return true; + } + return false; + #endif +} + +static inline bool mi_bchunk_try_find_and_clear_8(mi_bchunk_t* chunk, size_t n, size_t* pidx) { + mi_assert_internal(n==8); MI_UNUSED(n); + return mi_bchunk_try_find_and_clear8(chunk, pidx); +} + + +// find a sequence of `n` bits in a chunk with `0 < n <= MI_BFIELD_BITS` with all bits set, +// and try to clear them atomically. +// set `*pidx` to its bit index (0 <= *pidx <= MI_BCHUNK_BITS - n) on success. +// will cross bfield boundaries. +mi_decl_noinline static bool mi_bchunk_try_find_and_clearNX(mi_bchunk_t* chunk, size_t n, size_t* pidx) { + if (n == 0 || n > MI_BFIELD_BITS) return false; + const mi_bfield_t mask = mi_bfield_mask(n, 0); + // for all fields in the chunk + for (int i = 0; i < MI_BCHUNK_FIELDS; i++) { + mi_bfield_t b0 = mi_atomic_load_relaxed(&chunk->bfields[i]); + mi_bfield_t b = b0; + size_t idx; + + // is there a range inside the field? + while (mi_bfield_find_least_bit(b, &idx)) { // find least 1-bit + if (idx + n > MI_BFIELD_BITS) break; // too short: maybe cross over, or continue with the next field + + const size_t bmask = mask<>idx == mask); + if ((b&bmask) == bmask) { // found a match with all bits set, try clearing atomically + if mi_likely(mi_bfield_atomic_try_clear_mask_of(&chunk->bfields[i], bmask, b0, NULL)) { + *pidx = (i*MI_BFIELD_BITS) + idx; + mi_assert_internal(*pidx < MI_BCHUNK_BITS); + mi_assert_internal(*pidx + n <= MI_BCHUNK_BITS); + return true; + } + else { + // if we failed to atomically commit, reload b and try again from the start + b = b0 = mi_atomic_load_acquire(&chunk->bfields[i]); + } + } + else { + // advance by clearing the least run of ones, for example, with n>=4, idx=2: + // b = 1111 1101 1010 1100 + // .. + (1< 0) { + const size_t pre = mi_bfield_ctz(~mi_atomic_load_relaxed(&chunk->bfields[i+1])); + if (post + pre >= n) { + // it fits -- try to claim it atomically + const size_t cidx = (i*MI_BFIELD_BITS) + (MI_BFIELD_BITS - post); + if (mi_bchunk_try_clearNX(chunk, cidx, n, NULL)) { + // we cleared all atomically + *pidx = cidx; + mi_assert_internal(*pidx < MI_BCHUNK_BITS); + mi_assert_internal(*pidx + n <= MI_BCHUNK_BITS); + return true; + } + } + } + } + } + return false; +} + +// find a sequence of `n` bits in a chunk with `n <= MI_BCHUNK_BITS` with all bits set, +// and try to clear them atomically. +// set `*pidx` to its bit index (0 <= *pidx <= MI_BCHUNK_BITS - n) on success. +// This can cross bfield boundaries. +static mi_decl_noinline bool mi_bchunk_try_find_and_clearNC(mi_bchunk_t* chunk, size_t n, size_t* pidx) { + if (n == 0 || n > MI_BCHUNK_BITS) return false; // cannot be more than a chunk + + // we first scan ahead to see if there is a range of `n` set bits, and only then try to clear atomically + mi_assert_internal(n>0); + const size_t skip_count = (n-1)/MI_BFIELD_BITS; + size_t cidx; + for (size_t i = 0; i < MI_BCHUNK_FIELDS - skip_count; i++) + { + size_t m = n; // bits to go + + // first field + mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[i]); + size_t ones = mi_bfield_clz(~b); + + cidx = (i*MI_BFIELD_BITS) + (MI_BFIELD_BITS - ones); // start index + if (ones >= m) { + // we found enough bits already! + m = 0; + } + else if (ones > 0) { + // keep scanning further fields until we have enough bits + m -= ones; + size_t j = 1; // field count from i + while (i+j < MI_BCHUNK_FIELDS) { + mi_assert_internal(m > 0); + b = mi_atomic_load_relaxed(&chunk->bfields[i+j]); + ones = mi_bfield_ctz(~b); + if (ones >= m) { + // we found enough bits + m = 0; + break; + } + else if (ones == MI_BFIELD_BITS) { + // not enough yet, proceed to the next field + j++; + m -= MI_BFIELD_BITS; + } + else { + // the range was not enough, start from scratch + i = i + j - 1; // no need to re-scan previous fields, except the last one (with clz this time) + mi_assert_internal(m>0); + break; + } + } + } + + // did we find a range? + if (m==0) { + if (mi_bchunk_try_clearN(chunk, cidx, n, NULL)) { + // we cleared all atomically + *pidx = cidx; + mi_assert_internal(*pidx < MI_BCHUNK_BITS); + mi_assert_internal(*pidx + n <= MI_BCHUNK_BITS); return true; } - // predicate returned false, unclaim and look further - _mi_bitmap_unclaim(bitmap, bitmap_fields, count, *bitmap_idx); + // note: if we fail for a small `n` on the first field, we don't rescan that field (as `i` is incremented) } + // otherwise continue searching } return false; } -// Set `count` bits at `bitmap_idx` to 0 atomically -// Returns `true` if all `count` bits were 1 previously. -bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - const size_t idx = mi_bitmap_index_field(bitmap_idx); - const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const size_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); - // mi_assert_internal((bitmap[idx] & mask) == mask); - const size_t prev = mi_atomic_and_acq_rel(&bitmap[idx], ~mask); - return ((prev & mask) == mask); -} - - -// Set `count` bits at `bitmap_idx` to 1 atomically -// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit. -bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero) { - const size_t idx = mi_bitmap_index_field(bitmap_idx); - const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const size_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); - //mi_assert_internal(any_zero != NULL || (bitmap[idx] & mask) == 0); - size_t prev = mi_atomic_or_acq_rel(&bitmap[idx], mask); - if (any_zero != NULL) { *any_zero = ((prev & mask) != mask); } - return ((prev & mask) == 0); -} - -// Returns `true` if all `count` bits were 1. `any_ones` is `true` if there was at least one bit set to one. -static bool mi_bitmap_is_claimedx(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_ones) { - const size_t idx = mi_bitmap_index_field(bitmap_idx); - const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const size_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); - const size_t field = mi_atomic_load_relaxed(&bitmap[idx]); - if (any_ones != NULL) { *any_ones = ((field & mask) != 0); } - return ((field & mask) == mask); -} - -// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically. -// Returns `true` if successful when all previous `count` bits were 0. -bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - const size_t idx = mi_bitmap_index_field(bitmap_idx); - const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const size_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); - size_t expected = mi_atomic_load_relaxed(&bitmap[idx]); - do { - if ((expected & mask) != 0) return false; - } - while (!mi_atomic_cas_strong_acq_rel(&bitmap[idx], &expected, expected | mask)); - mi_assert_internal((expected & mask) == 0); + + +// ------- mi_bchunk_clear_once_set --------------------------------------- + +static inline void mi_bchunk_clear_once_set(mi_bchunk_t* chunk, size_t cidx) { + mi_assert_internal(cidx < MI_BCHUNK_BITS); + const size_t i = cidx / MI_BFIELD_BITS; + const size_t idx = cidx % MI_BFIELD_BITS; + mi_bfield_atomic_clear_once_set(&chunk->bfields[i], idx); +} + + +// ------- mi_bitmap_all_are_clear --------------------------------------- + + +// are all bits in a bitmap chunk clear? +static inline bool mi_bchunk_all_are_clear_relaxed(mi_bchunk_t* chunk) { + #if MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==256) + const __m256i vec = _mm256_load_si256((const __m256i*)chunk->bfields); + return mi_mm256_is_zero(vec); + #elif MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==512) + // a 64b cache-line contains the entire chunk anyway so load both at once + const __m256i vec1 = _mm256_load_si256((const __m256i*)chunk->bfields); + const __m256i vec2 = _mm256_load_si256(((const __m256i*)chunk->bfields)+1); + return (mi_mm256_is_zero(_mm256_or_si256(vec1,vec2))); + #elif MI_OPT_SIMD && (MI_BCHUNK_BITS==512) && MI_ARCH_ARM64 + const uint64x2_t v0 = vld1q_u64((uint64_t*)chunk->bfields); + const uint64x2_t v1 = vld1q_u64((uint64_t*)chunk->bfields + 2); + const uint64x2_t v2 = vld1q_u64((uint64_t*)chunk->bfields + 4); + const uint64x2_t v3 = vld1q_u64((uint64_t*)chunk->bfields + 6); + const uint64x2_t v = vorrq_u64(vorrq_u64(v0,v1),vorrq_u64(v2,v3)); + return (vmaxvq_u32(vreinterpretq_u32_u64(v)) == 0); + #else + for (int i = 0; i < MI_BCHUNK_FIELDS; i++) { + if (mi_atomic_load_relaxed(&chunk->bfields[i]) != 0) return false; + } + return true; + #endif +} + +// are all bits in a bitmap chunk set? +static inline bool mi_bchunk_all_are_set_relaxed(mi_bchunk_t* chunk) { +#if MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==256) + const __m256i vec = _mm256_load_si256((const __m256i*)chunk->bfields); + return mi_mm256_is_ones(vec); +#elif MI_OPT_SIMD && defined(__AVX2__) && (MI_BCHUNK_BITS==512) + // a 64b cache-line contains the entire chunk anyway so load both at once + const __m256i vec1 = _mm256_load_si256((const __m256i*)chunk->bfields); + const __m256i vec2 = _mm256_load_si256(((const __m256i*)chunk->bfields)+1); + return (mi_mm256_is_ones(_mm256_and_si256(vec1, vec2))); +#elif MI_OPT_SIMD && (MI_BCHUNK_BITS==512) && MI_ARCH_ARM64 + const uint64x2_t v0 = vld1q_u64((uint64_t*)chunk->bfields); + const uint64x2_t v1 = vld1q_u64((uint64_t*)chunk->bfields + 2); + const uint64x2_t v2 = vld1q_u64((uint64_t*)chunk->bfields + 4); + const uint64x2_t v3 = vld1q_u64((uint64_t*)chunk->bfields + 6); + const uint64x2_t v = vandq_u64(vandq_u64(v0,v1),vandq_u64(v2,v3)); + return (vminvq_u32(vreinterpretq_u32_u64(v)) == 0xFFFFFFFFUL); +#else + for (int i = 0; i < MI_BCHUNK_FIELDS; i++) { + if (~mi_atomic_load_relaxed(&chunk->bfields[i]) != 0) return false; + } return true; +#endif +} + + +static bool mi_bchunk_bsr(mi_bchunk_t* chunk, size_t* pidx) { + for (size_t i = MI_BCHUNK_FIELDS; i > 0; ) { + i--; + mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[i]); + size_t idx; + if (mi_bsr(b, &idx)) { + *pidx = (i*MI_BFIELD_BITS) + idx; + return true; + } + } + return false; +} + +static bool mi_bchunk_bsr_inv(mi_bchunk_t* chunk, size_t* pidx) { + for (size_t i = MI_BCHUNK_FIELDS; i > 0; ) { + i--; + mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[i]); + size_t idx; + if (mi_bsr(~b, &idx)) { + *pidx = (i*MI_BFIELD_BITS) + idx; + return true; + } + } + return false; +} + +static size_t mi_bchunk_popcount(mi_bchunk_t* chunk) { + size_t popcount = 0; + for (size_t i = 0; i < MI_BCHUNK_FIELDS; i++) { + const mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[i]); + popcount += mi_bfield_popcount(b); + } + return popcount; } -bool _mi_bitmap_is_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - return mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, NULL); +/* -------------------------------------------------------------------------------- + bitmap chunkmap +-------------------------------------------------------------------------------- */ + +static void mi_bitmap_chunkmap_set(mi_bitmap_t* bitmap, size_t chunk_idx) { + mi_assert(chunk_idx < mi_bitmap_chunk_count(bitmap)); + mi_bchunk_set(&bitmap->chunkmap, chunk_idx, NULL); } -bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - bool any_ones; - mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, &any_ones); - return any_ones; +static bool mi_bitmap_chunkmap_try_clear(mi_bitmap_t* bitmap, size_t chunk_idx) { + mi_assert(chunk_idx < mi_bitmap_chunk_count(bitmap)); + // check if the corresponding chunk is all clear + if (!mi_bchunk_all_are_clear_relaxed(&bitmap->chunks[chunk_idx])) return false; + // clear the chunkmap bit + mi_bchunk_clear(&bitmap->chunkmap, chunk_idx, NULL); + // .. but a concurrent set may have happened in between our all-clear test and the clearing of the + // bit in the mask. We check again to catch this situation. + if (!mi_bchunk_all_are_clear_relaxed(&bitmap->chunks[chunk_idx])) { + mi_bchunk_set(&bitmap->chunkmap, chunk_idx, NULL); + return false; + } + return true; } -//-------------------------------------------------------------------------- -// the `_across` functions work on bitmaps where sequences can cross over -// between the fields. This is used in arena allocation -//-------------------------------------------------------------------------- +/* -------------------------------------------------------------------------------- + bitmap +-------------------------------------------------------------------------------- */ + +size_t mi_bitmap_size(size_t bit_count, size_t* pchunk_count) { + mi_assert_internal((bit_count % MI_BCHUNK_BITS) == 0); + bit_count = _mi_align_up(bit_count, MI_BCHUNK_BITS); + mi_assert_internal(bit_count <= MI_BITMAP_MAX_BIT_COUNT); + mi_assert_internal(bit_count > 0); + const size_t chunk_count = bit_count / MI_BCHUNK_BITS; + mi_assert_internal(chunk_count >= 1); + const size_t size = offsetof(mi_bitmap_t,chunks) + (chunk_count * MI_BCHUNK_SIZE); + mi_assert_internal( (size%MI_BCHUNK_SIZE) == 0 ); + if (pchunk_count != NULL) { *pchunk_count = chunk_count; } + return size; +} + -// Try to atomically claim a sequence of `count` bits starting from the field -// at `idx` in `bitmap` and crossing into subsequent fields. Returns `true` on success. -// Only needs to consider crossing into the next fields (see `mi_bitmap_try_find_from_claim_across`) -static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t idx, const size_t count, const size_t retries, mi_bitmap_index_t* bitmap_idx, mi_stats_t* stats) +// initialize a bitmap to all unset; avoid a mem_zero if `already_zero` is true +// returns the size of the bitmap +size_t mi_bitmap_init(mi_bitmap_t* bitmap, size_t bit_count, bool already_zero) { + size_t chunk_count; + const size_t size = mi_bitmap_size(bit_count, &chunk_count); + if (!already_zero) { + _mi_memzero_aligned(bitmap, size); + } + mi_atomic_store_release(&bitmap->chunk_count, chunk_count); + mi_assert_internal(mi_atomic_load_relaxed(&bitmap->chunk_count) <= MI_BITMAP_MAX_CHUNK_COUNT); + return size; +} + + +// Set a sequence of `n` bits in the bitmap (and can cross chunks). Not atomic so only use if local to a thread. +static void mi_bchunks_unsafe_setN(mi_bchunk_t* chunks, mi_bchunkmap_t* cmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + + // start chunk and index + size_t chunk_idx = idx / MI_BCHUNK_BITS; + const size_t cidx = idx % MI_BCHUNK_BITS; + const size_t ccount = _mi_divide_up(n, MI_BCHUNK_BITS); + + // first update the chunkmap + mi_bchunk_setN(cmap, chunk_idx, ccount, NULL); + + // first chunk + size_t m = MI_BCHUNK_BITS - cidx; + if (m > n) { m = n; } + mi_bchunk_setN(&chunks[chunk_idx], cidx, m, NULL); + + // n can be large so use memset for efficiency for all in-between chunks + chunk_idx++; + n -= m; + const size_t mid_chunks = n / MI_BCHUNK_BITS; + if (mid_chunks > 0) { + _mi_memset(&chunks[chunk_idx], ~0, mid_chunks * MI_BCHUNK_SIZE); + chunk_idx += mid_chunks; + n -= (mid_chunks * MI_BCHUNK_BITS); + } + + // last chunk + if (n > 0) { + mi_assert_internal(n < MI_BCHUNK_BITS); + mi_bchunk_setN(&chunks[chunk_idx], 0, n, NULL); + } +} + +// Set a sequence of `n` bits in the bitmap (and can cross chunks). Not atomic so only use if local to a thread. +void mi_bitmap_unsafe_setN(mi_bitmap_t* bitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + mi_assert_internal(idx + n <= mi_bitmap_max_bits(bitmap)); + mi_bchunks_unsafe_setN(&bitmap->chunks[0], &bitmap->chunkmap, idx, n); +} + + + + +// ------- mi_bitmap_xset --------------------------------------- + +// Set a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from 0's to 1's (or 1's to 0's). +bool mi_bitmap_setN(mi_bitmap_t* bitmap, size_t idx, size_t n, size_t* palready_set) { + mi_assert_internal(n>0); + const size_t maxbits = mi_bitmap_max_bits(bitmap); + mi_assert_internal(idx + n <= maxbits); + if (idx+n > maxbits) { // paranoia + if (idx >= maxbits) return false; + n = maxbits - idx; + } + + // iterate through the chunks + size_t chunk_idx = idx / MI_BCHUNK_BITS; + size_t cidx = idx % MI_BCHUNK_BITS; + bool were_allclear = true; + size_t already_set = 0; + while (n > 0) { + const size_t m = (cidx + n > MI_BCHUNK_BITS ? MI_BCHUNK_BITS - cidx : n); + size_t _already_set = 0; + were_allclear = mi_bchunk_setN(&bitmap->chunks[chunk_idx], cidx, m, &_already_set) && were_allclear; + already_set += _already_set; + mi_bitmap_chunkmap_set(bitmap, chunk_idx); // set afterwards + mi_assert_internal(m <= n); + n -= m; + cidx = 0; + chunk_idx++; + } + if (palready_set != NULL) { *palready_set = already_set; } + return were_allclear; +} + +// Clear a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from 1's to 0's. +bool mi_bitmap_clearN(mi_bitmap_t* bitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + const size_t maxbits = mi_bitmap_max_bits(bitmap); + mi_assert_internal(idx + n <= maxbits); + if (idx+n > maxbits) { // paranoia + if (idx >= maxbits) return false; + n = maxbits - idx; + } + + // iterate through the chunks + size_t chunk_idx = idx / MI_BCHUNK_BITS; + size_t cidx = idx % MI_BCHUNK_BITS; + bool were_allset = true; + while (n > 0) { + const size_t m = (cidx + n > MI_BCHUNK_BITS ? MI_BCHUNK_BITS - cidx : n); + bool maybe_all_clear = false; + were_allset = mi_bchunk_clearN(&bitmap->chunks[chunk_idx], cidx, m, &maybe_all_clear) && were_allset; + if (maybe_all_clear) { mi_bitmap_chunkmap_try_clear(bitmap, chunk_idx); } + mi_assert_internal(m <= n); + n -= m; + cidx = 0; + chunk_idx++; + } + return were_allset; +} + +// Count bits set in a range of `n` bits. +size_t mi_bitmap_popcountN( mi_bitmap_t* bitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + const size_t maxbits = mi_bitmap_max_bits(bitmap); + mi_assert_internal(idx + n <= maxbits); + if (idx+n > maxbits) { // paranoia + if (idx >= maxbits) return 0; + n = maxbits - idx; + } + + // iterate through the chunks + size_t chunk_idx = idx / MI_BCHUNK_BITS; + size_t cidx = idx % MI_BCHUNK_BITS; + size_t popcount = 0; + while (n > 0) { + const size_t m = (cidx + n > MI_BCHUNK_BITS ? MI_BCHUNK_BITS - cidx : n); + popcount += mi_bchunk_popcountN(&bitmap->chunks[chunk_idx], cidx, m); + mi_assert_internal(m <= n); + n -= m; + cidx = 0; + chunk_idx++; + } + return popcount; +} + + +// Set/clear a bit in the bitmap; returns `true` if atomically transitioned from 0 to 1 (or 1 to 0) +bool mi_bitmap_set(mi_bitmap_t* bitmap, size_t idx) { + return mi_bitmap_setN(bitmap, idx, 1, NULL); +} + +bool mi_bitmap_clear(mi_bitmap_t* bitmap, size_t idx) { + return mi_bitmap_clearN(bitmap, idx, 1); +} + + + +// ------- mi_bitmap_is_xset --------------------------------------- + +// Is a sequence of n bits already all set/cleared? +bool mi_bitmap_is_xsetN(mi_xset_t set, mi_bitmap_t* bitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + const size_t maxbits = mi_bitmap_max_bits(bitmap); + mi_assert_internal(idx + n <= maxbits); + if (idx+n > maxbits) { // paranoia + if (idx >= maxbits) return false; + n = maxbits - idx; + } + + // iterate through the chunks + size_t chunk_idx = idx / MI_BCHUNK_BITS; + size_t cidx = idx % MI_BCHUNK_BITS; + bool xset = true; + while (n > 0 && xset) { + const size_t m = (cidx + n > MI_BCHUNK_BITS ? MI_BCHUNK_BITS - cidx : n); + xset = mi_bchunk_is_xsetN(set, &bitmap->chunks[chunk_idx], cidx, m) && xset; + mi_assert_internal(m <= n); + n -= m; + cidx = 0; + chunk_idx++; + } + return xset; +} + +bool mi_bitmap_is_all_clear(mi_bitmap_t* bitmap) { + return mi_bitmap_is_xsetN(MI_BIT_CLEAR, bitmap, 0, mi_bitmap_max_bits(bitmap)); +} + +/* -------------------------------------------------------------------------------- + Iterate through a bfield +-------------------------------------------------------------------------------- */ + +// Cycle iteration through a bitfield. This is used to space out threads +// so there is less chance of contention. When searching for a free page we +// like to first search only the accessed part (so we reuse better). This +// high point is called the `cycle`. +// +// We then iterate through the bitfield as: +// first: [start, cycle> +// then : [0, start> +// then : [cycle, MI_BFIELD_BITS> +// +// The start is determined usually as `tseq % cycle` to have each thread +// start at a different spot. +// - We use `popcount` to improve branch prediction (maybe not needed? can we simplify?) +// - The `cycle_mask` is the part `[start, cycle>`. +#define mi_bfield_iterate(bfield,start,cycle,name_idx,SUF) { \ + mi_assert_internal(start <= cycle); \ + mi_assert_internal(start < MI_BFIELD_BITS); \ + mi_assert_internal(cycle <= MI_BFIELD_BITS); \ + const mi_bfield_t _cycle_mask##SUF = mi_bfield_mask(cycle - start, start); \ + size_t _bcount##SUF = mi_bfield_popcount(bfield); \ + mi_bfield_t _b##SUF = bfield & _cycle_mask##SUF; /* process [start, cycle> first*/\ + while(_bcount##SUF > 0) { \ + _bcount##SUF--;\ + if (_b##SUF==0) { _b##SUF = bfield & ~_cycle_mask##SUF; } /* process [0,start> + [cycle, MI_BFIELD_BITS> next */ \ + /* size_t name_idx; */ \ + const bool _found##SUF = mi_bfield_find_least_bit(_b##SUF,&name_idx); \ + _b##SUF = mi_bfield_clear_least_bit(_b##SUF); /* clear early so `continue` works */ \ + mi_assert_internal(_found##SUF); MI_UNUSED(_found##SUF); \ + { \ + +#define mi_bfield_iterate_end(SUF) \ + } \ + } \ +} + + +#define mi_bfield_cycle_iterate(bfield,tseq,cycle,name_idx,SUF) { \ + const size_t _start##SUF = (uint32_t)(tseq) % (uint32_t)(cycle); /* or: 0 to always search from the start? */\ + mi_bfield_iterate(bfield,_start##SUF,cycle,name_idx,SUF) + +#define mi_bfield_cycle_iterate_end(SUF) \ + mi_bfield_iterate_end(SUF); \ +} + + +/* -------------------------------------------------------------------------------- + mi_bitmap_find + (used to find free pages) +-------------------------------------------------------------------------------- */ + +typedef bool (mi_bitmap_visit_fun_t)(mi_bitmap_t* bitmap, size_t chunk_idx, size_t n, size_t* idx, void* arg1, void* arg2); + +// Go through the bitmap and for every sequence of `n` set bits, call the visitor function. +// If it returns `true` stop the search. +static inline bool mi_bitmap_find(mi_bitmap_t* bitmap, size_t tseq, size_t n, size_t* pidx, mi_bitmap_visit_fun_t* on_find, void* arg1, void* arg2) { - mi_assert_internal(bitmap_idx != NULL); - - // check initial trailing zeros - mi_bitmap_field_t* field = &bitmap[idx]; - size_t map = mi_atomic_load_relaxed(field); - const size_t initial = mi_clz(map); // count of initial zeros starting at idx - mi_assert_internal(initial <= MI_BITMAP_FIELD_BITS); - if (initial == 0) return false; - if (initial >= count) return _mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx); // no need to cross fields (this case won't happen for us) - if (_mi_divide_up(count - initial, MI_BITMAP_FIELD_BITS) >= (bitmap_fields - idx)) return false; // not enough entries - - // scan ahead - size_t found = initial; - size_t mask = 0; // mask bits for the final field - while(found < count) { - field++; - map = mi_atomic_load_relaxed(field); - const size_t mask_bits = (found + MI_BITMAP_FIELD_BITS <= count ? MI_BITMAP_FIELD_BITS : (count - found)); - mi_assert_internal(mask_bits > 0 && mask_bits <= MI_BITMAP_FIELD_BITS); - mask = mi_bitmap_mask_(mask_bits, 0); - if ((map & mask) != 0) return false; // some part is already claimed - found += mask_bits; - } - mi_assert_internal(field < &bitmap[bitmap_fields]); - - // we found a range of contiguous zeros up to the final field; mask contains mask in the final field - // now try to claim the range atomically - mi_bitmap_field_t* const final_field = field; - const size_t final_mask = mask; - mi_bitmap_field_t* const initial_field = &bitmap[idx]; - const size_t initial_idx = MI_BITMAP_FIELD_BITS - initial; - const size_t initial_mask = mi_bitmap_mask_(initial, initial_idx); - - // initial field - size_t newmap; - field = initial_field; - map = mi_atomic_load_relaxed(field); - do { - newmap = (map | initial_mask); - if ((map & initial_mask) != 0) { goto rollback; }; - } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)); + const size_t chunkmap_max = _mi_divide_up(mi_bitmap_chunk_count(bitmap), MI_BFIELD_BITS); + for (size_t i = 0; i < chunkmap_max; i++) { + // and for each chunkmap entry we iterate over its bits to find the chunks + const mi_bfield_t cmap_entry = mi_atomic_load_relaxed(&bitmap->chunkmap.bfields[i]); + size_t hi; + if (mi_bfield_find_highest_bit(cmap_entry, &hi)) { + size_t eidx = 0; + mi_bfield_cycle_iterate(cmap_entry, tseq%8, hi+1, eidx, Y) // reduce the tseq to 8 bins to reduce using extra memory (see `mstress`) + { + mi_assert_internal(eidx <= MI_BFIELD_BITS); + const size_t chunk_idx = i*MI_BFIELD_BITS + eidx; + mi_assert_internal(chunk_idx < mi_bitmap_chunk_count(bitmap)); + if ((*on_find)(bitmap, chunk_idx, n, pidx, arg1, arg2)) { + return true; + } + } + mi_bfield_cycle_iterate_end(Y); + } + } + return false; +} + - // intermediate fields - while (++field < final_field) { - newmap = MI_BITMAP_FIELD_FULL; - map = 0; - if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { goto rollback; } +/* -------------------------------------------------------------------------------- + Bitmap: try_find_and_claim -- used to allocate abandoned pages + note: the compiler will fully inline the indirect function call +-------------------------------------------------------------------------------- */ + +typedef struct mi_claim_fun_data_s { + mi_arena_t* arena; +} mi_claim_fun_data_t; + +static bool mi_bitmap_try_find_and_claim_visit(mi_bitmap_t* bitmap, size_t chunk_idx, size_t n, size_t* pidx, void* arg1, void* arg2) +{ + mi_assert_internal(n==1); MI_UNUSED(n); + mi_claim_fun_t* claim_fun = (mi_claim_fun_t*)arg1; + mi_claim_fun_data_t* claim_data = (mi_claim_fun_data_t*)arg2; + size_t cidx; + if mi_likely(mi_bchunk_try_find_and_clear(&bitmap->chunks[chunk_idx], &cidx)) { + const size_t slice_index = (chunk_idx * MI_BCHUNK_BITS) + cidx; + mi_assert_internal(slice_index < mi_bitmap_max_bits(bitmap)); + bool keep_set = true; + if ((*claim_fun)(slice_index, claim_data->arena, &keep_set)) { + // success! + mi_assert_internal(!keep_set); + *pidx = slice_index; + return true; + } + else { + // failed to claim it, set abandoned mapping again (unless the page was freed) + if (keep_set) { + const bool wasclear = mi_bchunk_set(&bitmap->chunks[chunk_idx], cidx, NULL); + mi_assert_internal(wasclear); MI_UNUSED(wasclear); + } + } + } + else { + // we may find that all are cleared only on a second iteration but that is ok as + // the chunkmap is a conservative approximation. + mi_bitmap_chunkmap_try_clear(bitmap, chunk_idx); } + return false; +} + +// Find a set bit in the bitmap and try to atomically clear it and claim it. +// (Used to find pages in the pages_abandoned bitmaps.) +mi_decl_nodiscard bool mi_bitmap_try_find_and_claim(mi_bitmap_t* bitmap, size_t tseq, size_t* pidx, + mi_claim_fun_t* claim, mi_arena_t* arena ) +{ + mi_claim_fun_data_t claim_data = { arena }; + return mi_bitmap_find(bitmap, tseq, 1, pidx, &mi_bitmap_try_find_and_claim_visit, (void*)claim, &claim_data); +} + + +bool mi_bitmap_bsr(mi_bitmap_t* bitmap, size_t* idx) { + const size_t chunkmap_max = _mi_divide_up(mi_bitmap_chunk_count(bitmap), MI_BFIELD_BITS); + for (size_t i = chunkmap_max; i > 0; ) { + i--; + mi_bfield_t cmap = mi_atomic_load_relaxed(&bitmap->chunkmap.bfields[i]); + size_t cmap_idx; + if (mi_bsr(cmap,&cmap_idx)) { + // from highest chunk to lowest (scan all in case the cmap entry was stale) + for (size_t j = cmap_idx+1; j>0; ) { + j--; + const size_t chunk_idx = (i*MI_BFIELD_BITS) + j; + size_t cidx; + if (mi_bchunk_bsr(&bitmap->chunks[chunk_idx], &cidx)) { + *idx = (chunk_idx * MI_BCHUNK_BITS) + cidx; + return true; + } + } + } + } + return false; +} + +// Return count of all set bits in a bitmap. +size_t mi_bitmap_popcount(mi_bitmap_t* bitmap) { + // for all chunkmap entries + size_t popcount = 0; + const size_t chunkmap_max = _mi_divide_up(mi_bitmap_chunk_count(bitmap), MI_BFIELD_BITS); + for (size_t i = 0; i < chunkmap_max; i++) { + mi_bfield_t cmap_entry = mi_atomic_load_relaxed(&bitmap->chunkmap.bfields[i]); + size_t cmap_idx; + // for each chunk (corresponding to a set bit in a chunkmap entry) + while (mi_bfield_foreach_bit(&cmap_entry, &cmap_idx)) { + const size_t chunk_idx = i*MI_BFIELD_BITS + cmap_idx; + // count bits in a chunk + popcount += mi_bchunk_popcount(&bitmap->chunks[chunk_idx]); + } + } + return popcount; +} + + + +// Clear a bit once it is set. +void mi_bitmap_clear_once_set(mi_bitmap_t* bitmap, size_t idx) { + mi_assert_internal(idx < mi_bitmap_max_bits(bitmap)); + const size_t chunk_idx = idx / MI_BCHUNK_BITS; + const size_t cidx = idx % MI_BCHUNK_BITS; + mi_assert_internal(chunk_idx < mi_bitmap_chunk_count(bitmap)); + mi_bchunk_clear_once_set(&bitmap->chunks[chunk_idx], cidx); +} - // final field - mi_assert_internal(field == final_field); - map = mi_atomic_load_relaxed(field); - do { - newmap = (map | final_mask); - if ((map & final_mask) != 0) { goto rollback; } - } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)); - // claimed! - mi_stat_counter_increase(stats->arena_crossover_count,1); - *bitmap_idx = mi_bitmap_index_create(idx, initial_idx); +// Visit all set bits in a bitmap. +// todo: optimize further? maybe use avx512 to directly get all indices using a mask_compressstore? +bool _mi_bitmap_forall_set(mi_bitmap_t* bitmap, mi_forall_set_fun_t* visit, mi_arena_t* arena, void* arg) { + // for all chunkmap entries + const size_t chunkmap_max = _mi_divide_up(mi_bitmap_chunk_count(bitmap), MI_BFIELD_BITS); + for(size_t i = 0; i < chunkmap_max; i++) { + mi_bfield_t cmap_entry = mi_atomic_load_relaxed(&bitmap->chunkmap.bfields[i]); + size_t cmap_idx; + // for each chunk (corresponding to a set bit in a chunkmap entry) + while (mi_bfield_foreach_bit(&cmap_entry, &cmap_idx)) { + const size_t chunk_idx = i*MI_BFIELD_BITS + cmap_idx; + // for each chunk field + mi_bchunk_t* const chunk = &bitmap->chunks[chunk_idx]; + for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) { + const size_t base_idx = (chunk_idx*MI_BCHUNK_BITS) + (j*MI_BFIELD_BITS); + mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[j]); + size_t bidx; + while (mi_bfield_foreach_bit(&b, &bidx)) { + const size_t idx = base_idx + bidx; + if (!visit(idx, 1, arena, arg)) return false; + } + } + } + } return true; +} -rollback: - // roll back intermediate fields - // (we just failed to claim `field` so decrement first) - while (--field > initial_field) { - newmap = 0; - map = MI_BITMAP_FIELD_FULL; - mi_assert_internal(mi_atomic_load_relaxed(field) == map); - mi_atomic_store_release(field, newmap); - } - if (field == initial_field) { // (if we failed on the initial field, `field + 1 == initial_field`) - map = mi_atomic_load_relaxed(field); - do { - mi_assert_internal((map & initial_mask) == initial_mask); - newmap = (map & ~initial_mask); - } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)); +// Visit all set bits in a bitmap but try to return ranges (within bfields) if possible. +// Also clear those ranges atomically. +// Used by purging to purge larger ranges when possible +// todo: optimize further? maybe use avx512 to directly get all indices using a mask_compressstore? +bool _mi_bitmap_forall_setc_ranges(mi_bitmap_t* bitmap, mi_forall_set_fun_t* visit, mi_arena_t* arena, void* arg) { + // for all chunkmap entries + const size_t chunkmap_max = _mi_divide_up(mi_bitmap_chunk_count(bitmap), MI_BFIELD_BITS); + for (size_t i = 0; i < chunkmap_max; i++) { + mi_bfield_t cmap_entry = mi_atomic_load_relaxed(&bitmap->chunkmap.bfields[i]); + size_t cmap_idx; + // for each chunk (corresponding to a set bit in a chunkmap entry) + while (mi_bfield_foreach_bit(&cmap_entry, &cmap_idx)) { + const size_t chunk_idx = i*MI_BFIELD_BITS + cmap_idx; + // for each chunk field + mi_bchunk_t* const chunk = &bitmap->chunks[chunk_idx]; + for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) { + const size_t base_idx = (chunk_idx*MI_BCHUNK_BITS) + (j*MI_BFIELD_BITS); + mi_bfield_t b = mi_atomic_exchange_relaxed(&chunk->bfields[j], (mi_bfield_t)0); + #if MI_DEBUG > 1 + const size_t bpopcount = mi_popcount(b); + size_t rngcount = 0; + #endif + size_t bidx; + while (mi_bfield_find_least_bit(b, &bidx)) { + size_t rng = mi_ctz(~(b>>bidx)); // all the set bits from bidx + #if MI_DEBUG > 1 + rngcount += rng; + #endif + const size_t idx = base_idx + bidx; + mi_assert_internal(rng>=1 && rng<=MI_BFIELD_BITS); + mi_assert_internal((idx % MI_BFIELD_BITS) + rng <= MI_BFIELD_BITS); + mi_assert_internal((idx / MI_BCHUNK_BITS) < mi_bitmap_chunk_count(bitmap)); + if (!visit(idx, rng, arena, arg)) { + // break early: reset the non-visited bits + if (b!=0) { + mi_atomic_or_relaxed(&chunk->bfields[j], b); + } + return false; + } + // clear rng bits in b + b = b & ~mi_bfield_mask(rng, bidx); + } + mi_assert_internal(rngcount == bpopcount); + } + } } - mi_stat_counter_increase(stats->arena_rollback_count,1); - // retry? (we make a recursive call instead of goto to be able to use const declarations) - if (retries <= 2) { - return mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, retries+1, bitmap_idx, stats); + return true; +} + +// Visit all set bits in a bitmap but try to return ranges (within bfields) if possible, +// but only in chunks of at least `rngslices` slices (that are also aligned at `rngslices`) +// and clear those ranges atomically. +// However, the `rngslices` are capped at `MI_BFIELD_BITS` at most. +// Used by purging to purge larger ranges when possible. With transparent huge pages we only +// want to purge whole huge pages (2 MiB) at a time which is what the `rngslices` parameter achieves. +bool _mi_bitmap_forall_setc_rangesn(mi_bitmap_t* bitmap, size_t rngslices, mi_forall_set_fun_t* visit, mi_arena_t* arena, void* arg) +{ + // use the generic routine for `rngslices<=1` (as that one finds longest ranges at a time) + if (rngslices<=1) { + return _mi_bitmap_forall_setc_ranges(bitmap, visit, arena, arg); } - else { + // mi_assert_internal(rngslices <= MI_BFIELD_BITS); + if (rngslices > MI_BFIELD_BITS) { rngslices = MI_BFIELD_BITS; } // cap at MI_BFIELD_BITS at most + + // for all chunkmap entries + const size_t chunkmap_max = _mi_divide_up(mi_bitmap_chunk_count(bitmap), MI_BFIELD_BITS); + for (size_t i = 0; i < chunkmap_max; i++) { + mi_bfield_t cmap_entry = mi_atomic_load_relaxed(&bitmap->chunkmap.bfields[i]); + size_t cmap_idx; + // for each chunk (corresponding to a set bit in a chunkmap entry) + while (mi_bfield_foreach_bit(&cmap_entry, &cmap_idx)) { + const size_t chunk_idx = i*MI_BFIELD_BITS + cmap_idx; + // for each chunk field + mi_bchunk_t* const chunk = &bitmap->chunks[chunk_idx]; + for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) { + const size_t base_idx = (chunk_idx*MI_BCHUNK_BITS) + (j*MI_BFIELD_BITS); + mi_bfield_t b = mi_atomic_exchange_relaxed(&chunk->bfields[j], (mi_bfield_t)0); // atomic clear + mi_bfield_t skipped = 0; // but track which bits we skip so we can restore them + for(size_t shift = 0; rngslices + shift <= MI_BFIELD_BITS; shift += rngslices) { // per `rngslices` to keep alignment + const mi_bfield_t rngmask = mi_bfield_mask(rngslices, shift); + if ((b & rngmask) == rngmask) { + const size_t idx = base_idx + shift; + if (!visit(idx, rngslices, arena, arg)) { + // break early: restore non-visited entries + mi_bfield_t notyet_visited = 0; + if (shift + rngslices < MI_BFIELD_BITS) { + notyet_visited = (b & (~(mi_bfield_t)0 << (shift + rngslices))); + } + mi_assert_internal((notyet_visited & skipped) == 0); + if ((notyet_visited | skipped) != 0) { + mi_atomic_or_relaxed(&chunk->bfields[j], notyet_visited | skipped); + } + return false; + } + } + else { + skipped = skipped | (b & rngmask); + } + } + + if (skipped != 0) { + mi_atomic_or_relaxed(&chunk->bfields[j], skipped); + } + } + } + } + return true; +} + + +/* -------------------------------------------------------------------------------- + binned bitmap's +-------------------------------------------------------------------------------- */ + + +size_t mi_bbitmap_size(size_t bit_count, size_t* pchunk_count) { + // mi_assert_internal((bit_count % MI_BCHUNK_BITS) == 0); + bit_count = _mi_align_up(bit_count, MI_BCHUNK_BITS); + mi_assert_internal(bit_count <= MI_BITMAP_MAX_BIT_COUNT); + mi_assert_internal(bit_count > 0); + const size_t chunk_count = bit_count / MI_BCHUNK_BITS; + mi_assert_internal(chunk_count >= 1); + const size_t size = offsetof(mi_bbitmap_t,chunks) + (chunk_count * MI_BCHUNK_SIZE); + mi_assert_internal( (size%MI_BCHUNK_SIZE) == 0 ); + if (pchunk_count != NULL) { *pchunk_count = chunk_count; } + return size; +} + +// initialize a bitmap to all unset; avoid a mem_zero if `already_zero` is true +// returns the size of the bitmap +size_t mi_bbitmap_init(mi_bbitmap_t* bbitmap, size_t bit_count, bool already_zero) { + size_t chunk_count; + const size_t size = mi_bbitmap_size(bit_count, &chunk_count); + if (!already_zero) { + _mi_memzero_aligned(bbitmap, size); + } + mi_atomic_store_release(&bbitmap->chunk_count, chunk_count); + mi_assert_internal(mi_atomic_load_relaxed(&bbitmap->chunk_count) <= MI_BITMAP_MAX_CHUNK_COUNT); + return size; +} + +void mi_bbitmap_unsafe_setN(mi_bbitmap_t* bbitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + mi_assert_internal(idx + n <= mi_bbitmap_max_bits(bbitmap)); + mi_bchunks_unsafe_setN(&bbitmap->chunks[0], &bbitmap->chunkmap, idx, n); +} + +bool mi_bbitmap_bsr_inv(mi_bbitmap_t* bbitmap, size_t* idx) { + // scan for highest zero bit in the bitmap + // note: we cannot use the chunkmap since that only conservatively denotes if there might be a set bit in a chuck + const size_t chunk_count = mi_bbitmap_chunk_count(bbitmap); + for(size_t i = chunk_count; i > 0; ) { + i--; + size_t cidx; + if (mi_bchunk_bsr_inv(&bbitmap->chunks[i], &cidx)) { + *idx = (i * MI_BCHUNK_BITS) + cidx; + return true; + } + } + return false; +} + + +/* -------------------------------------------------------------------------------- + binned bitmap used to track free slices +-------------------------------------------------------------------------------- */ + +// Assign a specific size bin to a chunk +static void mi_bbitmap_set_chunk_bin(mi_bbitmap_t* bbitmap, size_t chunk_idx, mi_chunkbin_t bin) { + mi_assert_internal(chunk_idx < mi_bbitmap_chunk_count(bbitmap)); + for (mi_chunkbin_t ibin = MI_CBIN_SMALL; ibin < MI_CBIN_NONE; ibin = mi_chunkbin_inc(ibin)) { + if (ibin == bin) { + const bool was_clear = mi_bchunk_set(& bbitmap->chunkmap_bins[ibin], chunk_idx, NULL); + if (was_clear) { mi_os_stat_increase(chunk_bins[ibin],1); } + } + else { + const bool was_set = mi_bchunk_clear(&bbitmap->chunkmap_bins[ibin], chunk_idx, NULL); + if (was_set) { mi_os_stat_decrease(chunk_bins[ibin],1); } + } + } +} + +mi_chunkbin_t mi_bbitmap_debug_get_bin(const mi_bchunkmap_t* chunkmap_bins, size_t chunk_idx) { + for (mi_chunkbin_t ibin = MI_CBIN_SMALL; ibin < MI_CBIN_NONE; ibin = mi_chunkbin_inc(ibin)) { + if (mi_bchunk_is_xsetN(MI_BIT_SET, &chunkmap_bins[ibin], chunk_idx, 1)) { + return ibin; + } + } + return MI_CBIN_NONE; +} + +// Track the index of the highest chunk that is accessed. +static void mi_bbitmap_chunkmap_set_max(mi_bbitmap_t* bbitmap, size_t chunk_idx) { + size_t oldmax = mi_atomic_load_relaxed(&bbitmap->chunk_max_accessed); + if mi_unlikely(chunk_idx > oldmax) { + mi_atomic_cas_strong_relaxed(&bbitmap->chunk_max_accessed, &oldmax, chunk_idx); + } +} + +// Set a bit in the chunkmap +static void mi_bbitmap_chunkmap_set(mi_bbitmap_t* bbitmap, size_t chunk_idx, bool check_all_set) { + mi_assert(chunk_idx < mi_bbitmap_chunk_count(bbitmap)); + if (check_all_set) { + if (mi_bchunk_all_are_set_relaxed(&bbitmap->chunks[chunk_idx])) { + // all slices are free in this chunk: return back to the NONE bin + mi_bbitmap_set_chunk_bin(bbitmap, chunk_idx, MI_CBIN_NONE); + } + } + mi_bchunk_set(&bbitmap->chunkmap, chunk_idx, NULL); + mi_bbitmap_chunkmap_set_max(bbitmap, chunk_idx); +} + +static bool mi_bbitmap_chunkmap_try_clear(mi_bbitmap_t* bbitmap, size_t chunk_idx) { + mi_assert(chunk_idx < mi_bbitmap_chunk_count(bbitmap)); + // check if the corresponding chunk is all clear + if (!mi_bchunk_all_are_clear_relaxed(&bbitmap->chunks[chunk_idx])) return false; + // clear the chunkmap bit + mi_bchunk_clear(&bbitmap->chunkmap, chunk_idx, NULL); + // .. but a concurrent set may have happened in between our all-clear test and the clearing of the + // bit in the mask. We check again to catch this situation. (note: mi_bchunk_clear must be acq-rel) + if (!mi_bchunk_all_are_clear_relaxed(&bbitmap->chunks[chunk_idx])) { + mi_bchunk_set(&bbitmap->chunkmap, chunk_idx, NULL); return false; } + mi_bbitmap_chunkmap_set_max(bbitmap, chunk_idx); + return true; +} + + +/* -------------------------------------------------------------------------------- + mi_bbitmap_setN, try_clearN, and is_xsetN + (used to find free pages) +-------------------------------------------------------------------------------- */ + +// Set a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from 0's to 1's (or 1's to 0's). +bool mi_bbitmap_setN(mi_bbitmap_t* bbitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + const size_t maxbits = mi_bbitmap_max_bits(bbitmap); + mi_assert_internal(idx + n <= maxbits); + if (idx+n > maxbits) { // paranoia + if (idx >= maxbits) return false; + n = maxbits - idx; + } + + // iterate through the chunks + size_t chunk_idx = idx / MI_BCHUNK_BITS; + size_t cidx = idx % MI_BCHUNK_BITS; + bool were_allclear = true; + while (n > 0) { + const size_t m = (cidx + n > MI_BCHUNK_BITS ? MI_BCHUNK_BITS - cidx : n); + were_allclear = mi_bchunk_setN(&bbitmap->chunks[chunk_idx], cidx, m, NULL) && were_allclear; + mi_bbitmap_chunkmap_set(bbitmap, chunk_idx, true); // set afterwards + mi_assert_internal(m <= n); + n -= m; + cidx = 0; + chunk_idx++; + } + return were_allclear; +} + +// ------- mi_bbitmap_try_clearNC --------------------------------------- + +// Try to clear `n` bits at `idx` where `n <= MI_BCHUNK_BITS`. +bool mi_bbitmap_try_clearNC(mi_bbitmap_t* bbitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + mi_assert_internal(n<=MI_BCHUNK_BITS); + mi_assert_internal(idx + n <= mi_bbitmap_max_bits(bbitmap)); + + const size_t chunk_idx = idx / MI_BCHUNK_BITS; + const size_t cidx = idx % MI_BCHUNK_BITS; + mi_assert_internal(cidx + n <= MI_BCHUNK_BITS); // don't cross chunks (for now) + mi_assert_internal(chunk_idx < mi_bbitmap_chunk_count(bbitmap)); + if (cidx + n > MI_BCHUNK_BITS) return false; + bool maybe_all_clear = false; + const bool cleared = mi_bchunk_try_clearN(&bbitmap->chunks[chunk_idx], cidx, n, &maybe_all_clear); + if (cleared && maybe_all_clear) { mi_bbitmap_chunkmap_try_clear(bbitmap, chunk_idx); } + // note: we don't set the size class for an explicit try_clearN (only used by purging) + return cleared; } -// Find `count` bits of zeros and set them to 1 atomically; returns `true` on success. -// Starts at idx, and wraps around to search in all `bitmap_fields` fields. -bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx, mi_stats_t* stats) { - mi_assert_internal(count > 0); - if (count <= 2) { - // we don't bother with crossover fields for small counts - return _mi_bitmap_try_find_from_claim(bitmap, bitmap_fields, start_field_idx, count, bitmap_idx); + +// ------- mi_bbitmap_is_xset --------------------------------------- + +// Is a sequence of n bits already all set/cleared? +bool mi_bbitmap_is_xsetN(mi_xset_t set, mi_bbitmap_t* bbitmap, size_t idx, size_t n) { + mi_assert_internal(n>0); + const size_t maxbits = mi_bbitmap_max_bits(bbitmap); + mi_assert_internal(idx + n <= maxbits); + if (idx+n > maxbits) { // paranoia + if (idx >= maxbits) return false; + n = maxbits - idx; } - // visit the fields - size_t idx = start_field_idx; - for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) { - if (idx >= bitmap_fields) { idx = 0; } // wrap - // first try to claim inside a field - /* - if (count <= MI_BITMAP_FIELD_BITS) { - if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) { - return true; + // iterate through the chunks + size_t chunk_idx = idx / MI_BCHUNK_BITS; + size_t cidx = idx % MI_BCHUNK_BITS; + bool xset = true; + while (n > 0 && xset) { + const size_t m = (cidx + n > MI_BCHUNK_BITS ? MI_BCHUNK_BITS - cidx : n); + xset = mi_bchunk_is_xsetN(set, &bbitmap->chunks[chunk_idx], cidx, m) && xset; + mi_assert_internal(m <= n); + n -= m; + cidx = 0; + chunk_idx++; + } + return xset; +} + + + + +/* -------------------------------------------------------------------------------- + mi_bbitmap_find + (used to find free pages) +-------------------------------------------------------------------------------- */ + +typedef bool (mi_bchunk_try_find_and_clear_fun_t)(mi_bchunk_t* chunk, size_t n, size_t* idx); + +// Go through the bbitmap and for every sequence of `n` set bits, call the visitor function. +// If it returns `true` stop the search. +// +// This is used for finding free blocks and it is important to be efficient (with 2-level bitscan) +// but also reduce fragmentation (through size bins). +static inline bool mi_bbitmap_try_find_and_clear_generic(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx, mi_bchunk_try_find_and_clear_fun_t* on_find) +{ + // we space out threads to reduce contention + const size_t cmap_max_count = _mi_divide_up(mi_bbitmap_chunk_count(bbitmap),MI_BFIELD_BITS); + const size_t chunk_acc = mi_atomic_load_relaxed(&bbitmap->chunk_max_accessed); + const size_t cmap_acc = chunk_acc / MI_BFIELD_BITS; + const size_t cmap_acc_bits = 1 + (chunk_acc % MI_BFIELD_BITS); + + // create a mask over the chunkmap entries to iterate over them efficiently + mi_assert_internal(MI_BFIELD_BITS >= MI_BCHUNK_FIELDS); + const mi_bfield_t cmap_mask = mi_bfield_mask(cmap_max_count,0); + const size_t cmap_cycle = cmap_acc+1; + const mi_chunkbin_t bbin = mi_chunkbin_of(n); + // visit each cmap entry + size_t cmap_idx = 0; + mi_bfield_cycle_iterate(cmap_mask, tseq, cmap_cycle, cmap_idx, X) + { + // and for each chunkmap entry we iterate over its bits to find the chunks + const mi_bfield_t cmap_entry = mi_atomic_load_relaxed(&bbitmap->chunkmap.bfields[cmap_idx]); + const size_t cmap_entry_cycle = (cmap_idx != cmap_acc ? MI_BFIELD_BITS : cmap_acc_bits); + if (cmap_entry == 0) { + continue; + } + + // get size bin masks + mi_bfield_t cmap_bins[MI_CBIN_COUNT] = { 0 }; + cmap_bins[MI_CBIN_NONE] = cmap_entry; + for (mi_chunkbin_t ibin = MI_CBIN_SMALL; ibin < MI_CBIN_NONE; ibin = mi_chunkbin_inc(ibin)) { + const mi_bfield_t cmap_bin = mi_atomic_load_relaxed(&bbitmap->chunkmap_bins[ibin].bfields[cmap_idx]); + cmap_bins[ibin] = cmap_bin & cmap_entry; + cmap_bins[MI_CBIN_NONE] &= ~cmap_bin; // clear bits that are in an assigned size bin + } + + // consider only chunks for a particular size bin at a time + // this picks the best bin only within a cmap entry (~ 1GiB address space), but avoids multiple + // iterations through all entries. + mi_assert_internal(bbin < MI_CBIN_NONE); + for (mi_chunkbin_t ibin = MI_CBIN_SMALL; ibin <= MI_CBIN_NONE; + // skip from bbin to NONE (so, say, a SMALL will never be placed in a OTHER, MEDIUM, or LARGE chunk to reduce fragmentation) + ibin = (ibin == bbin ? MI_CBIN_NONE : mi_chunkbin_inc(ibin))) + { + mi_assert_internal(ibin < MI_CBIN_COUNT); + const mi_bfield_t cmap_bin = cmap_bins[ibin]; + size_t eidx = 0; + mi_bfield_cycle_iterate(cmap_bin, tseq, cmap_entry_cycle, eidx, Y) + { + // assertion doesn't quite hold as the max_accessed may be out-of-date + // mi_assert_internal(cmap_entry_cycle > eidx || ibin == MI_CBIN_NONE); + + // get the chunk + const size_t chunk_idx = cmap_idx*MI_BFIELD_BITS + eidx; + mi_bchunk_t* chunk = &bbitmap->chunks[chunk_idx]; + + size_t cidx; + if ((*on_find)(chunk, n, &cidx)) { + if (cidx==0 && ibin == MI_CBIN_NONE) { // only the first block determines the size bin + // this chunk is now reserved for the `bbin` size class + mi_bbitmap_set_chunk_bin(bbitmap, chunk_idx, bbin); + } + *pidx = (chunk_idx * MI_BCHUNK_BITS) + cidx; + mi_assert_internal(*pidx + n <= mi_bbitmap_max_bits(bbitmap)); + return true; + } + else { + // todo: should _on_find_ return a boolean if there is a chance all are clear to avoid calling `try_clear?` + // we may find that all are cleared only on a second iteration but that is ok as the chunkmap is a conservative approximation. + mi_bbitmap_chunkmap_try_clear(bbitmap, chunk_idx); + } } + mi_bfield_cycle_iterate_end(Y); } - */ - // if that fails, then try to claim across fields - if (mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, 0, bitmap_idx, stats)) { - return true; + } + mi_bfield_cycle_iterate_end(X); + return false; +} + +/* -------------------------------------------------------------------------------- + mi_bbitmap_try_find_and_clear -- used to find free pages + note: the compiler will fully inline the indirect function calls +-------------------------------------------------------------------------------- */ + +bool mi_bbitmap_try_find_and_clear(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx) { + return mi_bbitmap_try_find_and_clear_generic(bbitmap, tseq, 1, pidx, &mi_bchunk_try_find_and_clear_1); +} + +bool mi_bbitmap_try_find_and_clear8(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx) { + return mi_bbitmap_try_find_and_clear_generic(bbitmap, tseq, 8, pidx, &mi_bchunk_try_find_and_clear_8); +} + +// bool mi_bbitmap_try_find_and_clearX(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx) { +// return mi_bbitmap_try_find_and_clear_generic(bbitmap, tseq, MI_BFIELD_BITS, pidx, &mi_bchunk_try_find_and_clear_X); +// } + +bool mi_bbitmap_try_find_and_clearNX(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx) { + mi_assert_internal(n<=MI_BFIELD_BITS); + return mi_bbitmap_try_find_and_clear_generic(bbitmap, tseq, n, pidx, &mi_bchunk_try_find_and_clearNX); +} + +bool mi_bbitmap_try_find_and_clearNC(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx) { + mi_assert_internal(n<=MI_BCHUNK_BITS); + return mi_bbitmap_try_find_and_clear_generic(bbitmap, tseq, n, pidx, &mi_bchunk_try_find_and_clearNC); +} + + +/* -------------------------------------------------------------------------------- + mi_bbitmap_try_find_and_clear for huge objects spanning multiple chunks +-------------------------------------------------------------------------------- */ + +// Try to atomically clear `n` bits starting at `chunk_idx` where `n` can span over multiple chunks +static bool mi_bchunk_try_clearN_(mi_bbitmap_t* bbitmap, size_t chunk_idx, size_t n) { + mi_assert_internal((chunk_idx * MI_BCHUNK_BITS) + n <= mi_bbitmap_max_bits(bbitmap)); + + size_t m = n; // bits to go + size_t count = 0; // chunk count + while (m > 0) { + mi_bchunk_t* chunk = &bbitmap->chunks[chunk_idx + count]; + if (!mi_bchunk_try_clearN(chunk, 0, (m > MI_BCHUNK_BITS ? MI_BCHUNK_BITS : m), NULL)) { + goto rollback; } + m = (m <= MI_BCHUNK_BITS ? 0 : m - MI_BCHUNK_BITS); + count++; + } + return true; + +rollback: + // we only need to reset chunks the we just fully cleared + while (count > 0) { + count--; + mi_bchunk_t* chunk = &bbitmap->chunks[chunk_idx + count]; + mi_bchunk_setN(chunk, 0, MI_BCHUNK_BITS, NULL); } return false; } -// Helper for masks across fields; returns the mid count, post_mask may be 0 -static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, size_t* pre_mask, size_t* mid_mask, size_t* post_mask) { - MI_UNUSED(bitmap_fields); - const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - if mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS) { - *pre_mask = mi_bitmap_mask_(count, bitidx); - *mid_mask = 0; - *post_mask = 0; - mi_assert_internal(mi_bitmap_index_field(bitmap_idx) < bitmap_fields); - return 0; +// Go through the bbitmap to find a sequence of `n` bits and clear them atomically where `n > MI_ARENA_MAX_CHUNK_OBJ_SIZE` +// Since these are very large object allocations we always search from the start and only consider starting at the start +// of a chunk (for fragmentation and efficiency). +// Todo: for now we try to find full empty chunks to cover `n` but we can allow a partial chunk at the end +// Todo: This scans directly through the chunks -- we might want to consult the cmap as well? +bool mi_bbitmap_try_find_and_clearN_(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx) { + MI_UNUSED(tseq); + mi_assert(n > 0); if (n==0) { return false; } + + const size_t chunk_max = mi_bbitmap_chunk_count(bbitmap); + const size_t chunk_req = _mi_divide_up(n, MI_BCHUNK_BITS); // minimal number of chunks needed + if (chunk_max < chunk_req) { return false; } + + // iterate through the chunks + size_t chunk_idx = 0; + while (chunk_idx <= chunk_max - chunk_req) + { + size_t count = 0; // chunk count + do { + mi_assert_internal(chunk_idx + count < chunk_max); + mi_bchunk_t* const chunk = &bbitmap->chunks[chunk_idx + count]; + if (!mi_bchunk_all_are_set_relaxed(chunk)) { + break; + } + else { + count++; + } + } + while (count < chunk_req); + + // did we find a suitable range? + if (count == chunk_req) { + // now try to claim it! + if (mi_bchunk_try_clearN_(bbitmap, chunk_idx, n)) { + *pidx = (chunk_idx * MI_BCHUNK_BITS); + for (size_t i = 0; i < count; i++) { + mi_bbitmap_set_chunk_bin(bbitmap, chunk_idx + i, MI_CBIN_HUGE); + } + mi_assert_internal(*pidx + n <= mi_bbitmap_max_bits(bbitmap)); + return true; + } + } + + // keep searching but skip the scanned range + chunk_idx += count+1; } - else { - const size_t pre_bits = MI_BITMAP_FIELD_BITS - bitidx; - mi_assert_internal(pre_bits < count); - *pre_mask = mi_bitmap_mask_(pre_bits, bitidx); - count -= pre_bits; - const size_t mid_count = (count / MI_BITMAP_FIELD_BITS); - *mid_mask = MI_BITMAP_FIELD_FULL; - count %= MI_BITMAP_FIELD_BITS; - *post_mask = (count==0 ? 0 : mi_bitmap_mask_(count, 0)); - mi_assert_internal(mi_bitmap_index_field(bitmap_idx) + mid_count + (count==0 ? 0 : 1) < bitmap_fields); - return mid_count; - } -} - -// Set `count` bits at `bitmap_idx` to 0 atomically -// Returns `true` if all `count` bits were 1 previously. -bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - size_t idx = mi_bitmap_index_field(bitmap_idx); - size_t pre_mask; - size_t mid_mask; - size_t post_mask; - size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); - bool all_one = true; - mi_bitmap_field_t* field = &bitmap[idx]; - size_t prev = mi_atomic_and_acq_rel(field++, ~pre_mask); // clear first part - if ((prev & pre_mask) != pre_mask) all_one = false; - while(mid_count-- > 0) { - prev = mi_atomic_and_acq_rel(field++, ~mid_mask); // clear mid part - if ((prev & mid_mask) != mid_mask) all_one = false; - } - if (post_mask!=0) { - prev = mi_atomic_and_acq_rel(field, ~post_mask); // clear end part - if ((prev & post_mask) != post_mask) all_one = false; - } - return all_one; -} - -// Set `count` bits at `bitmap_idx` to 1 atomically -// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit. -bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero) { - size_t idx = mi_bitmap_index_field(bitmap_idx); - size_t pre_mask; - size_t mid_mask; - size_t post_mask; - size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); - bool all_zero = true; - bool any_zero = false; - _Atomic(size_t)*field = &bitmap[idx]; - size_t prev = mi_atomic_or_acq_rel(field++, pre_mask); - if ((prev & pre_mask) != 0) all_zero = false; - if ((prev & pre_mask) != pre_mask) any_zero = true; - while (mid_count-- > 0) { - prev = mi_atomic_or_acq_rel(field++, mid_mask); - if ((prev & mid_mask) != 0) all_zero = false; - if ((prev & mid_mask) != mid_mask) any_zero = true; - } - if (post_mask!=0) { - prev = mi_atomic_or_acq_rel(field, post_mask); - if ((prev & post_mask) != 0) all_zero = false; - if ((prev & post_mask) != post_mask) any_zero = true; - } - if (pany_zero != NULL) { *pany_zero = any_zero; } - return all_zero; -} - - -// Returns `true` if all `count` bits were 1. -// `any_ones` is `true` if there was at least one bit set to one. -static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones) { - size_t idx = mi_bitmap_index_field(bitmap_idx); - size_t pre_mask; - size_t mid_mask; - size_t post_mask; - size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); - bool all_ones = true; - bool any_ones = false; - mi_bitmap_field_t* field = &bitmap[idx]; - size_t prev = mi_atomic_load_relaxed(field++); - if ((prev & pre_mask) != pre_mask) all_ones = false; - if ((prev & pre_mask) != 0) any_ones = true; - while (mid_count-- > 0) { - prev = mi_atomic_load_relaxed(field++); - if ((prev & mid_mask) != mid_mask) all_ones = false; - if ((prev & mid_mask) != 0) any_ones = true; - } - if (post_mask!=0) { - prev = mi_atomic_load_relaxed(field); - if ((prev & post_mask) != post_mask) all_ones = false; - if ((prev & post_mask) != 0) any_ones = true; - } - if (pany_ones != NULL) { *pany_ones = any_ones; } - return all_ones; -} - -bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - return mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, NULL); -} - -bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { - bool any_ones; - mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, &any_ones); - return any_ones; + return false; } + + + + + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.h index d8316b83..ca51a4e0 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/bitmap.h @@ -1,115 +1,343 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2019-2023 Microsoft Research, Daan Leijen +Copyright (c) 2019-2024 Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ /* ---------------------------------------------------------------------------- -Concurrent bitmap that can set/reset sequences of bits atomically, -represented as an array of fields where each field is a machine word (`size_t`) - -There are two api's; the standard one cannot have sequences that cross -between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS). -(this is used in region allocation) - -The `_across` postfixed functions do allow sequences that can cross over -between the fields. (This is used in arena allocation) +Concurrent bitmap that can set/reset sequences of bits atomically ---------------------------------------------------------------------------- */ #pragma once #ifndef MI_BITMAP_H #define MI_BITMAP_H -/* ----------------------------------------------------------- - Bitmap definition ------------------------------------------------------------ */ +/* -------------------------------------------------------------------------------- + Atomic bitmaps with release/acquire guarantees: + + `mi_bfield_t`: is a single machine word that can efficiently be bit counted (usually `size_t`) + each bit usually represents a single MI_ARENA_SLICE_SIZE in an arena (64 KiB). + We need 16K bits to represent a 1GiB arena. + + `mi_bchunk_t`: a chunk of bfield's of a total of MI_BCHUNK_BITS (= 512 on 64-bit, 256 on 32-bit) + allocations never span across chunks -- so MI_ARENA_MAX_OBJ_SIZE is the number + of bits in a chunk times the MI_ARENA_SLICE_SIZE (512 * 64KiB = 32 MiB). + These chunks are cache-aligned and we can use AVX2/AVX512/NEON/SVE/SVE2/etc. instructions + to scan for bits (perhaps) more efficiently. + + We allocate byte-sized ranges aligned to bytes in the bfield, and bfield-sized + ranges aligned to a bfield. + + Searching linearly through the chunks would be too slow (16K bits per GiB). + Instead we add a "chunkmap" to do a two-level search (more or less a btree of depth 2). + + `mi_bchunkmap_t` (== `mi_bchunk_t`): for each chunk we track if it has (potentially) any bit set. + The chunkmap has 1 bit per chunk that is set if the chunk potentially has a bit set. + This is used to avoid scanning every chunk. (and thus strictly an optimization) + It is conservative: it is fine to set a bit in the chunk map even if the chunk turns out + to have no bits set. It is also allowed to briefly have a clear bit even if the + chunk has bits set -- as long as we guarantee that the bit will be set later on; + (this allows us to set the chunkmap bit right after we set a bit in the corresponding chunk). + + However, when we clear a bit in a chunk, and the chunk is indeed all clear, we + cannot safely clear the bit corresponding to the chunk in the chunkmap since it + may race with another thread setting a bit in the same chunk. Therefore, when + clearing, we first test if a chunk is clear, then clear the chunkmap bit, and + then test again to catch any set bits that we may have missed. + + Since the chunkmap may thus be briefly out-of-sync, this means that we may sometimes + not find a free page even though it's there (but we accept this as we avoid taking + full locks). (Another way to do this is to use an epoch but we like to avoid that complexity + for now). + + `mi_bitmap_t`: a bitmap with N chunks. A bitmap has a chunkmap of MI_BCHUNK_BITS (512) + and thus has at most 512 chunks (=2^18 bits x 64 KiB slices = 16 GiB max arena size). + The minimum is 1 chunk which is a 32 MiB arena. + + For now, the implementation assumes MI_HAS_FAST_BITSCAN and uses trailing-zero-count + and pop-count (but we think it can be adapted work reasonably well on older hardware too) +--------------------------------------------------------------------------------------------- */ + +// A word-size bit field. +typedef size_t mi_bfield_t; + +#define MI_BFIELD_BITS_SHIFT (MI_SIZE_SHIFT+3) +#define MI_BFIELD_BITS (1 << MI_BFIELD_BITS_SHIFT) +#define MI_BFIELD_SIZE (MI_BFIELD_BITS/8) +#define MI_BFIELD_LO_BIT8 (((~(mi_bfield_t)0))/0xFF) // 0x01010101 .. +#define MI_BFIELD_HI_BIT8 (MI_BFIELD_LO_BIT8 << 7) // 0x80808080 .. + +#define MI_BCHUNK_SIZE (MI_BCHUNK_BITS / 8) +#define MI_BCHUNK_FIELDS (MI_BCHUNK_BITS / MI_BFIELD_BITS) // 8 on both 64- and 32-bit + + +// some compiler (msvc in C mode) cannot have expressions in the alignment attribute +#if MI_BCHUNK_SIZE==64 +#define mi_decl_bchunk_align mi_decl_align(64) +#elif MI_BCHUNK_SIZE==32 +#define mi_decl_bchunk_align mi_decl_align(32) +#else +#define mi_decl_bchunk_align mi_decl_align(MI_BCHUNK_SIZE) +#endif + + +// A bitmap chunk contains 512 bits on 64-bit (256 on 32-bit) +typedef mi_decl_bchunk_align struct mi_bchunk_s { + _Atomic(mi_bfield_t) bfields[MI_BCHUNK_FIELDS]; +} mi_bchunk_t; + + +// The chunkmap has one bit per corresponding chunk that is set if the chunk potentially has bits set. +// The chunkmap is itself a chunk. +typedef mi_bchunk_t mi_bchunkmap_t; + +#define MI_BCHUNKMAP_BITS MI_BCHUNK_BITS + +#define MI_BITMAP_MAX_CHUNK_COUNT (MI_BCHUNKMAP_BITS) +#define MI_BITMAP_MIN_CHUNK_COUNT (1) +#if MI_SIZE_BITS > 32 +#define MI_BITMAP_DEFAULT_CHUNK_COUNT (64) // 2 GiB on 64-bit -- this is for the page map +#else +#define MI_BITMAP_DEFAULT_CHUNK_COUNT (1) +#endif +#define MI_BITMAP_MAX_BIT_COUNT (MI_BITMAP_MAX_CHUNK_COUNT * MI_BCHUNK_BITS) // 16 GiB arena +#define MI_BITMAP_MIN_BIT_COUNT (MI_BITMAP_MIN_CHUNK_COUNT * MI_BCHUNK_BITS) // 32 MiB arena +#define MI_BITMAP_DEFAULT_BIT_COUNT (MI_BITMAP_DEFAULT_CHUNK_COUNT * MI_BCHUNK_BITS) // 2 GiB arena + + +// An atomic bitmap +typedef mi_decl_bchunk_align struct mi_bitmap_s { + _Atomic(size_t) chunk_count; // total count of chunks (0 < N <= MI_BCHUNKMAP_BITS) + size_t _padding[MI_BCHUNK_SIZE/MI_SIZE_SIZE - 1]; // suppress warning on msvc + mi_bchunkmap_t chunkmap; + mi_bchunk_t chunks[MI_BITMAP_DEFAULT_CHUNK_COUNT]; // usually dynamic MI_BITMAP_MAX_CHUNK_COUNT +} mi_bitmap_t; + + +static inline size_t mi_bitmap_chunk_count(const mi_bitmap_t* bitmap) { + return mi_atomic_load_relaxed(&((mi_bitmap_t*)bitmap)->chunk_count); +} + +static inline size_t mi_bitmap_max_bits(const mi_bitmap_t* bitmap) { + return (mi_bitmap_chunk_count(bitmap) * MI_BCHUNK_BITS); +} + + + +/* -------------------------------------------------------------------------------- + Atomic bitmap operations +-------------------------------------------------------------------------------- */ + +// Many operations are generic over setting or clearing the bit sequence: we use `mi_xset_t` for this (true if setting, false if clearing) +typedef bool mi_xset_t; +#define MI_BIT_SET (true) +#define MI_BIT_CLEAR (false) + + +// Required size of a bitmap to represent `bit_count` bits. +size_t mi_bitmap_size(size_t bit_count, size_t* chunk_count); + +// Initialize a bitmap to all clear; avoid a mem_zero if `already_zero` is true +// returns the size of the bitmap. +size_t mi_bitmap_init(mi_bitmap_t* bitmap, size_t bit_count, bool already_zero); + +// Set/clear a sequence of `n` bits in the bitmap (and can cross chunks). +// Not atomic so only use if still local to a thread. +void mi_bitmap_unsafe_setN(mi_bitmap_t* bitmap, size_t idx, size_t n); -#define MI_BITMAP_FIELD_BITS (8*MI_SIZE_SIZE) -#define MI_BITMAP_FIELD_FULL (~((size_t)0)) // all bits set -// An atomic bitmap of `size_t` fields -typedef _Atomic(size_t) mi_bitmap_field_t; -typedef mi_bitmap_field_t* mi_bitmap_t; +// Set a bit in the bitmap; returns `true` if it atomically transitioned from 0 to 1 +bool mi_bitmap_set(mi_bitmap_t* bitmap, size_t idx); -// A bitmap index is the index of the bit in a bitmap. -typedef size_t mi_bitmap_index_t; +// Clear a bit in the bitmap; returns `true` if it atomically transitioned from 1 to 0 +bool mi_bitmap_clear(mi_bitmap_t* bitmap, size_t idx); -// Create a bit index. -static inline mi_bitmap_index_t mi_bitmap_index_create(size_t idx, size_t bitidx) { - mi_assert_internal(bitidx < MI_BITMAP_FIELD_BITS); - return (idx*MI_BITMAP_FIELD_BITS) + bitidx; +// Set a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from all 0's to 1's +// If `already_set` is not NULL, it is set to count of bits were already all set. +// (this is used for correct statistics if commiting over a partially committed area) +bool mi_bitmap_setN(mi_bitmap_t* bitmap, size_t idx, size_t n, size_t* already_set); + +// Clear a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from all 1's to 0's +bool mi_bitmap_clearN(mi_bitmap_t* bitmap, size_t idx, size_t n); + + +// Is a sequence of n bits already all set/cleared? +bool mi_bitmap_is_xsetN(mi_xset_t set, mi_bitmap_t* bitmap, size_t idx, size_t n); + +// Is the bitmap completely clear? +bool mi_bitmap_is_all_clear(mi_bitmap_t* bitmap); + +// Is a sequence of n bits already set? +// (Used to check if a memory range is already committed) +static inline bool mi_bitmap_is_setN(mi_bitmap_t* bitmap, size_t idx, size_t n) { + return mi_bitmap_is_xsetN(MI_BIT_SET, bitmap, idx, n); } -// Create a bit index. -static inline mi_bitmap_index_t mi_bitmap_index_create_from_bit(size_t full_bitidx) { - return mi_bitmap_index_create(full_bitidx / MI_BITMAP_FIELD_BITS, full_bitidx % MI_BITMAP_FIELD_BITS); +// Is a sequence of n bits already clear? +static inline bool mi_bitmap_is_clearN(mi_bitmap_t* bitmap, size_t idx, size_t n) { + return mi_bitmap_is_xsetN(MI_BIT_CLEAR, bitmap, idx, n); } -// Get the field index from a bit index. -static inline size_t mi_bitmap_index_field(mi_bitmap_index_t bitmap_idx) { - return (bitmap_idx / MI_BITMAP_FIELD_BITS); +static inline bool mi_bitmap_is_set(mi_bitmap_t* bitmap, size_t idx) { + return mi_bitmap_is_setN(bitmap, idx, 1); } -// Get the bit index in a bitmap field -static inline size_t mi_bitmap_index_bit_in_field(mi_bitmap_index_t bitmap_idx) { - return (bitmap_idx % MI_BITMAP_FIELD_BITS); +static inline bool mi_bitmap_is_clear(mi_bitmap_t* bitmap, size_t idx) { + return mi_bitmap_is_clearN(bitmap, idx, 1); } -// Get the full bit index -static inline size_t mi_bitmap_index_bit(mi_bitmap_index_t bitmap_idx) { - return bitmap_idx; +// Called once a bit is cleared to see if the memory slice can be claimed. +typedef bool (mi_claim_fun_t)(size_t slice_index, mi_arena_t* arena, bool* keep_set); + +// Find a set bits in the bitmap, atomically clear it, and check if `claim` returns true. +// If not claimed, continue on (potentially setting the bit again depending on `keep_set`). +// Returns true on success, and in that case sets the index: `0 <= *pidx <= MI_BITMAP_MAX_BITS-n`. +mi_decl_nodiscard bool mi_bitmap_try_find_and_claim(mi_bitmap_t* bitmap, size_t tseq, size_t* pidx, + mi_claim_fun_t* claim, mi_arena_t* arena ); + + +// Atomically clear a bit but only if it is set. Will block otherwise until the bit is set. +// This is used to delay free-ing a page that it at the same time being considered to be +// allocated from `mi_arena_try_abandoned` (and is in the `claim` function of `mi_bitmap_try_find_and_claim`). +void mi_bitmap_clear_once_set(mi_bitmap_t* bitmap, size_t idx); + + +// If a bit is set in the bitmap, return `true` and set `idx` to the index of the highest bit. +// Otherwise return `false` (and `*idx` is undefined). +// Used for unloading arena's +bool mi_bitmap_bsr(mi_bitmap_t* bitmap, size_t* idx); + +// Return count of all set bits in a bitmap. +size_t mi_bitmap_popcount(mi_bitmap_t* bitmap); + + +typedef bool (mi_forall_set_fun_t)(size_t slice_index, size_t slice_count, mi_arena_t* arena, void* arg2); + +// Visit all set bits in a bitmap (`slice_count == 1`) +bool _mi_bitmap_forall_set(mi_bitmap_t* bitmap, mi_forall_set_fun_t* visit, mi_arena_t* arena, void* arg); + +// Visit all set bits in a bitmap with larger ranges if possible (`slice_count >= 1`) +// Ranges will never cross chunk boundaries though (and `slice_count <= MI_BCHUNK_BITS`) +bool _mi_bitmap_forall_setc_ranges(mi_bitmap_t* bitmap, mi_forall_set_fun_t* visit, mi_arena_t* arena, void* arg); + +// Visit all set bits in a bitmap with at least `rngslices` at a time (and aligned to `rngslices`). +// This is used by purging to not break up transparent huge pages for example. +// Ranges will never cross chunk boundaries (and `slice_count <= MI_BCHUNK_BITS`). +bool _mi_bitmap_forall_setc_rangesn(mi_bitmap_t* bitmap, size_t rngslices, mi_forall_set_fun_t* visit, mi_arena_t* arena, void* arg); + +// Count all set bits in given range in the bitmap. +size_t mi_bitmap_popcountN( mi_bitmap_t* bitmap, size_t idx, size_t n); + +/* ---------------------------------------------------------------------------- + Binned concurrent bitmap + Assigns a size class to each chunk such that small blocks don't cause too + much fragmentation since we keep chunks for larger blocks separate. +---------------------------------------------------------------------------- */ + +// mi_chunkbin_t is defined in mimalloc-stats.h + +static inline mi_chunkbin_t mi_chunkbin_inc(mi_chunkbin_t bbin) { + mi_assert_internal(bbin < MI_CBIN_COUNT); + return (mi_chunkbin_t)((int)bbin + 1); +} + +static inline mi_chunkbin_t mi_chunkbin_dec(mi_chunkbin_t bbin) { + mi_assert_internal(bbin > MI_CBIN_NONE); + return (mi_chunkbin_t)((int)bbin - 1); +} + +static inline mi_chunkbin_t mi_chunkbin_of(size_t slice_count) { + if (slice_count==1) return MI_CBIN_SMALL; + if (slice_count==8) return MI_CBIN_MEDIUM; + #if MI_ENABLE_LARGE_PAGES + if (slice_count==MI_BFIELD_BITS) return MI_CBIN_LARGE; + #endif + if (slice_count > MI_BCHUNK_BITS) return MI_CBIN_HUGE; + return MI_CBIN_OTHER; } -/* ----------------------------------------------------------- - Claim a bit sequence atomically ------------------------------------------------------------ */ +// An atomic "binned" bitmap for the free slices where we keep chunks reserved for particular size classes +typedef mi_decl_bchunk_align struct mi_bbitmap_s { + _Atomic(size_t) chunk_count; // total count of chunks (0 < N <= MI_BCHUNKMAP_BITS) + _Atomic(size_t) chunk_max_accessed; // max chunk index that was once cleared or set + #if (MI_BCHUNK_SIZE / MI_SIZE_SIZE) > 2 + size_t _padding[MI_BCHUNK_SIZE/MI_SIZE_SIZE - 2]; // suppress warning on msvc by aligning manually + #endif + mi_bchunkmap_t chunkmap; + mi_bchunkmap_t chunkmap_bins[MI_CBIN_COUNT - 1]; // chunkmaps with bit set if the chunk is in that size class (excluding MI_CBIN_NONE) + mi_bchunk_t chunks[MI_BITMAP_DEFAULT_CHUNK_COUNT]; // usually dynamic MI_BITMAP_MAX_CHUNK_COUNT +} mi_bbitmap_t; -// Try to atomically claim a sequence of `count` bits in a single -// field at `idx` in `bitmap`. Returns `true` on success. -bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx); -// Starts at idx, and wraps around to search in all `bitmap_fields` fields. -// For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields. -bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx); +static inline size_t mi_bbitmap_chunk_count(const mi_bbitmap_t* bbitmap) { + return mi_atomic_load_relaxed(&((mi_bbitmap_t*)bbitmap)->chunk_count); +} -// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled -typedef bool (mi_cdecl *mi_bitmap_pred_fun_t)(mi_bitmap_index_t bitmap_idx, void* pred_arg); -bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_pred_fun_t pred_fun, void* pred_arg, mi_bitmap_index_t* bitmap_idx); +static inline size_t mi_bbitmap_max_bits(const mi_bbitmap_t* bbitmap) { + return (mi_bbitmap_chunk_count(bbitmap) * MI_BCHUNK_BITS); +} -// Set `count` bits at `bitmap_idx` to 0 atomically -// Returns `true` if all `count` bits were 1 previously. -bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); +mi_chunkbin_t mi_bbitmap_debug_get_bin(const mi_bchunk_t* chunkmap_bins, size_t chunk_idx); -// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically. -// Returns `true` if successful when all previous `count` bits were 0. -bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); +size_t mi_bbitmap_size(size_t bit_count, size_t* chunk_count); -// Set `count` bits at `bitmap_idx` to 1 atomically -// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit. -bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero); +// If a bit is clear in the bitmap, return `true` and set `idx` to the index of the highest bit that was clear. +// Otherwise return `false` (and `*idx` is undefined). +// Used for debug output. +bool mi_bbitmap_bsr_inv(mi_bbitmap_t* bbitmap, size_t* idx); -bool _mi_bitmap_is_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); -bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); +// Initialize a bitmap to all clear; avoid a mem_zero if `already_zero` is true +// returns the size of the bitmap. +size_t mi_bbitmap_init(mi_bbitmap_t* bbitmap, size_t bit_count, bool already_zero); +// Set/clear a sequence of `n` bits in the bitmap (and can cross chunks). +// Not atomic so only use if still local to a thread. +void mi_bbitmap_unsafe_setN(mi_bbitmap_t* bbitmap, size_t idx, size_t n); -//-------------------------------------------------------------------------- -// the `_across` functions work on bitmaps where sequences can cross over -// between the fields. This is used in arena allocation -//-------------------------------------------------------------------------- -// Find `count` bits of zeros and set them to 1 atomically; returns `true` on success. -// Starts at idx, and wraps around to search in all `bitmap_fields` fields. -bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx, mi_stats_t* stats); +// Set a sequence of `n` bits in the bbitmap; returns `true` if atomically transitioned from all 0's to 1's +bool mi_bbitmap_setN(mi_bbitmap_t* bbitmap, size_t idx, size_t n); -// Set `count` bits at `bitmap_idx` to 0 atomically -// Returns `true` if all `count` bits were 1 previously. -bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); -// Set `count` bits at `bitmap_idx` to 1 atomically -// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit. -bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero); +// Is a sequence of n bits already all set/cleared? +bool mi_bbitmap_is_xsetN(mi_xset_t set, mi_bbitmap_t* bbitmap, size_t idx, size_t n); -bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); -bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); +// Is a sequence of n bits already set? +// (Used to check if a memory range is already committed) +static inline bool mi_bbitmap_is_setN(mi_bbitmap_t* bbitmap, size_t idx, size_t n) { + return mi_bbitmap_is_xsetN(MI_BIT_SET, bbitmap, idx, n); +} -#endif +// Is a sequence of n bits already clear? +static inline bool mi_bbitmap_is_clearN(mi_bbitmap_t* bbitmap, size_t idx, size_t n) { + return mi_bbitmap_is_xsetN(MI_BIT_CLEAR, bbitmap, idx, n); +} + + +// Try to atomically transition `n` bits from all set to all clear. Returns `true` on succes. +// `n` cannot cross chunk boundaries, where `n <= MI_CHUNK_BITS`. +bool mi_bbitmap_try_clearNC(mi_bbitmap_t* bbitmap, size_t idx, size_t n); + + +// Specialized versions for common bit sequence sizes +bool mi_bbitmap_try_find_and_clear(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx); // 1-bit +bool mi_bbitmap_try_find_and_clear8(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx); // 8-bits +// bool mi_bbitmap_try_find_and_clearX(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx); // MI_BFIELD_BITS +bool mi_bbitmap_try_find_and_clearNX(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx); // < MI_BFIELD_BITS +bool mi_bbitmap_try_find_and_clearNC(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx); // > MI_BFIELD_BITS <= MI_BCHUNK_BITS +bool mi_bbitmap_try_find_and_clearN_(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx); // > MI_BCHUNK_BITS + +// Find a sequence of `n` bits in the bbitmap with all bits set, and try to atomically clear all. +// Returns true on success, and in that case sets the index: `0 <= *pidx <= MI_BITMAP_MAX_BITS-n`. +mi_decl_nodiscard static inline bool mi_bbitmap_try_find_and_clearN(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx) { + if (n==1) return mi_bbitmap_try_find_and_clear(bbitmap, tseq, pidx); // small pages + if (n==8) return mi_bbitmap_try_find_and_clear8(bbitmap, tseq, pidx); // medium pages + // if (n==MI_BFIELD_BITS) return mi_bbitmap_try_find_and_clearX(bbitmap, tseq, pidx); // large pages + if (n==0) return false; + if (n<=MI_BFIELD_BITS) return mi_bbitmap_try_find_and_clearNX(bbitmap, tseq, n, pidx); + if (n<=MI_BCHUNK_BITS) return mi_bbitmap_try_find_and_clearNC(bbitmap, tseq, n, pidx); + return mi_bbitmap_try_find_and_clearN_(bbitmap, tseq, n, pidx); +} + + +#endif // MI_BITMAP_H diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/free.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/free.c index b9cb6346..aa7366dc 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/free.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/free.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2024, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -9,14 +9,13 @@ terms of the MIT license. A copy of the license can be found in the file // add includes help an IDE #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/atomic.h" #include "mimalloc/prim.h" // _mi_prim_thread_id() #endif // forward declarations static void mi_check_padding(const mi_page_t* page, const mi_block_t* block); static bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block); -static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block); +static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block, bool was_guarded); static void mi_stat_free(const mi_page_t* page, const mi_block_t* block); @@ -24,268 +23,370 @@ static void mi_stat_free(const mi_page_t* page, const mi_block_t* block); // Free // ------------------------------------------------------ -// forward declaration of multi-threaded free (`_mt`) (or free in huge block if compiled with MI_HUGE_PAGE_ABANDON) -static mi_decl_noinline void mi_free_block_mt(mi_page_t* page, mi_segment_t* segment, mi_block_t* block); - // regular free of a (thread local) block pointer // fast path written carefully to prevent spilling on the stack -static inline void mi_free_block_local(mi_page_t* page, mi_block_t* block, bool track_stats, bool check_full) +static inline void mi_free_block_local(mi_page_t* page, mi_block_t* block, bool was_guarded, bool track_stats, bool check_full) { - // checks + MI_UNUSED(was_guarded); + // checks if mi_unlikely(mi_check_is_double_free(page, block)) return; - mi_check_padding(page, block); + if (!was_guarded) { mi_check_padding(page, block); } if (track_stats) { mi_stat_free(page, block); } #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN - if (!mi_page_is_huge(page)) { // huge page content may be already decommitted - memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); - } + memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); #endif - if (track_stats) { mi_track_free_size(block, mi_page_usable_size_of(page, block)); } // faster then mi_usable_size as we already know the page and that p is unaligned + if (track_stats) { mi_track_free_size(block, mi_page_usable_size_of(page, block, was_guarded)); } // faster then mi_usable_size as we already know the page and that p is unaligned // actual free: push on the local free list mi_block_set_next(page, block, page->local_free); page->local_free = block; if mi_unlikely(--page->used == 0) { - _mi_page_retire(page); + if (page->retire_expire==0) { // no need to re-retire retired pages (happens when we alloc/free one block repeatedly in an empty page) + _mi_page_retire(page); + } } else if mi_unlikely(check_full && mi_page_is_in_full(page)) { _mi_page_unfull(page); } } +// Forward declaration for multi-threaded collect +static void mi_decl_noinline mi_free_try_collect_mt(mi_page_t* page, mi_block_t* mt_free) mi_attr_noexcept; + +// Free a block multi-threaded +static inline void mi_free_block_mt(mi_page_t* page, mi_block_t* block, bool was_guarded) mi_attr_noexcept +{ + MI_UNUSED(was_guarded); + // adjust stats (after padding check and potentially recursive `mi_free` above) + mi_stat_free(page, block); // stat_free may access the padding + mi_track_free_size(block, mi_page_usable_size_of(page, block, was_guarded)); + + // _mi_padding_shrink(page, block, sizeof(mi_block_t)); +#if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN // note: when tracking, cannot use mi_usable_size with multi-threading + if (!was_guarded) { + size_t dbgsize = mi_usable_size(block); + if (dbgsize > MI_MiB) { dbgsize = MI_MiB; } + _mi_memset_aligned(block, MI_DEBUG_FREED, dbgsize); + } +#endif + + // push atomically on the page thread free list + mi_thread_free_t tf_new; + mi_thread_free_t tf_old = mi_atomic_load_relaxed(&page->xthread_free); + do { + mi_block_set_next(page, block, mi_tf_block(tf_old)); + tf_new = mi_tf_create(block, true /* always use owned: try to claim it if the page is abandoned */); + } while (!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tf_old, tf_new)); // todo: release is enough? + + // and atomically try to collect the page if it was abandoned + const bool is_owned_now = !mi_tf_is_owned(tf_old); + if (is_owned_now) { + mi_assert_internal(mi_page_is_abandoned(page)); + mi_free_try_collect_mt(page,block); + } +} + + // Adjust a block that was allocated aligned, to the actual start of the block in the page. -// note: this can be called from `mi_free_generic_mt` where a non-owning thread accesses the -// `page_start` and `block_size` fields; however these are constant and the page won't be +// note: this can be called from `mi_free_generic_mt` where a non-owning thread accesses the +// `page_woffset` and `block_size` fields; however these are constant and the page won't be // deallocated (as the block we are freeing keeps it alive) and thus safe to read concurrently. mi_block_t* _mi_page_ptr_unalign(const mi_page_t* page, const void* p) { mi_assert_internal(page!=NULL && p!=NULL); - size_t diff = (uint8_t*)p - page->page_start; - size_t adjust; - if mi_likely(page->block_size_shift != 0) { - adjust = diff & (((size_t)1 << page->block_size_shift) - 1); + const size_t diff = (uint8_t*)p - mi_page_start(page); + const size_t block_size = mi_page_block_size(page); + const size_t adjust = (_mi_is_power_of_two(block_size) ? diff & (block_size - 1) : diff % block_size); + return (mi_block_t*)((uintptr_t)p - adjust); +} + +// forward declaration for a MI_GUARDED build +#if MI_GUARDED +static void mi_block_unguard(mi_page_t* page, mi_block_t* block, void* p); // forward declaration +static inline bool mi_block_check_unguard(mi_page_t* page, mi_block_t* block, void* p) { + if (mi_block_ptr_is_guarded(block, p)) { + mi_block_unguard(page, block, p); + return true; } else { - adjust = diff % mi_page_block_size(page); + return false; } +} +#else +static inline bool mi_block_check_unguard(mi_page_t* page, mi_block_t* block, void* p) { + MI_UNUSED(page); MI_UNUSED(block); MI_UNUSED(p); + return false; +} +#endif - return (mi_block_t*)((uintptr_t)p - adjust); +static inline mi_block_t* mi_validate_block_from_ptr( const mi_page_t* page, void* p ) { + mi_assert(_mi_page_ptr_unalign(page,p) == (mi_block_t*)p); // should never be an interior pointer + #if MI_SECURE > 0 + // in secure mode we always unalign to guard against free-ing interior pointers + return _mi_page_ptr_unalign(page,p); + #else + MI_UNUSED(page); + return (mi_block_t*)p; + #endif } + // free a local pointer (page parameter comes first for better codegen) -static void mi_decl_noinline mi_free_generic_local(mi_page_t* page, mi_segment_t* segment, void* p) mi_attr_noexcept { - MI_UNUSED(segment); - mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(page, p) : (mi_block_t*)p); - mi_free_block_local(page, block, true /* track stats */, true /* check for a full page */); +static void mi_decl_noinline mi_free_generic_local(mi_page_t* page, void* p) mi_attr_noexcept { + mi_assert_internal(p!=NULL && page != NULL); + mi_block_t* const block = (mi_page_has_interior_pointers(page) ? _mi_page_ptr_unalign(page, p) : mi_validate_block_from_ptr(page,p)); + const bool was_guarded = mi_block_check_unguard(page, block, p); + mi_free_block_local(page, block, was_guarded, true /* track stats */, true /* check for a full page */); } // free a pointer owned by another thread (page parameter comes first for better codegen) -static void mi_decl_noinline mi_free_generic_mt(mi_page_t* page, mi_segment_t* segment, void* p) mi_attr_noexcept { - mi_block_t* const block = _mi_page_ptr_unalign(page, p); // don't check `has_aligned` flag to avoid a race (issue #865) - mi_free_block_mt(page, segment, block); +static void mi_decl_noinline mi_free_generic_mt(mi_page_t* page, void* p) mi_attr_noexcept { + mi_assert_internal(p!=NULL && page != NULL); + mi_block_t* const block = (mi_page_has_interior_pointers(page) ? _mi_page_ptr_unalign(page, p) : mi_validate_block_from_ptr(page,p)); + const bool was_guarded = mi_block_check_unguard(page, block, p); + mi_free_block_mt(page, block, was_guarded); } // generic free (for runtime integration) -void mi_decl_noinline _mi_free_generic(mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept { - if (is_local) mi_free_generic_local(page,segment,p); - else mi_free_generic_mt(page,segment,p); +void mi_decl_noinline _mi_free_generic(mi_page_t* page, bool is_local, void* p) mi_attr_noexcept { + if (is_local) mi_free_generic_local(page,p); + else mi_free_generic_mt(page,p); } -// Get the segment data belonging to a pointer -// This is just a single `and` in release mode but does further checks in debug mode -// (and secure mode) to see if this was a valid pointer. -static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* msg) -{ - MI_UNUSED(msg); -#if (MI_DEBUG>0) - if mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) { +// Get the page belonging to a pointer +// Does further checks in debug mode to see if this was a valid pointer. +static inline mi_page_t* mi_validate_ptr_page(const void* p, const char* msg) +{ + MI_UNUSED_RELEASE(msg); + #if MI_DEBUG + if mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0 && !mi_option_is_enabled(mi_option_guarded_precise)) { _mi_error_message(EINVAL, "%s: invalid (unaligned) pointer: %p\n", msg, p); return NULL; } -#endif - - mi_segment_t* const segment = _mi_ptr_segment(p); - if mi_unlikely(segment==NULL) return segment; - -#if (MI_DEBUG>0) - if mi_unlikely(!mi_is_in_heap_region(p)) { - #if (MI_INTPTR_SIZE == 8 && defined(__linux__)) - if (((uintptr_t)p >> 40) != 0x7F) { // linux tends to align large blocks above 0x7F000000000 (issue #640) + mi_page_t* page = _mi_safe_ptr_page(p); + if (p != NULL && page == NULL) { + _mi_error_message(EINVAL, "%s: invalid pointer: %p\n", msg, p); + } + return page; #else - { + return _mi_ptr_page(p); #endif - _mi_warning_message("%s: pointer might not point to a valid heap region: %p\n" - "(this may still be a valid very large allocation (over 64MiB))\n", msg, p); - if mi_likely(_mi_ptr_cookie(segment) == segment->cookie) { - _mi_warning_message("(yes, the previous pointer %p was valid after all)\n", p); - } - } - } -#endif -#if (MI_DEBUG>0 || MI_SECURE>=4) - if mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie) { - _mi_error_message(EINVAL, "%s: pointer does not point to a valid heap space: %p\n", msg, p); - return NULL; - } -#endif - - return segment; } // Free a block // Fast path written carefully to prevent register spilling on the stack -void mi_free(void* p) mi_attr_noexcept +static mi_decl_forceinline void mi_free_ex(void* p, size_t* usable, mi_page_t* page) { - mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free"); - if mi_unlikely(segment==NULL) return; - - const bool is_local = (_mi_prim_thread_id() == mi_atomic_load_relaxed(&segment->thread_id)); - mi_page_t* const page = _mi_segment_page_of(segment, p); - - if mi_likely(is_local) { // thread-local free? - if mi_likely(page->flags.full_aligned == 0) { // and it is not a full page (full pages need to move from the full bin), nor has aligned blocks (aligned blocks need to be unaligned) - // thread-local, aligned, and not a full page - mi_block_t* const block = (mi_block_t*)p; - mi_free_block_local(page, block, true /* track stats */, false /* no need to check if the page is full */); - } - else { - // page is full or contains (inner) aligned blocks; use generic path - mi_free_generic_local(page, segment, p); - } + if mi_unlikely(page==NULL) return; // page will be NULL if p==NULL + mi_assert_internal(p!=NULL && page!=NULL); + if (usable!=NULL) { *usable = mi_page_usable_block_size(page); } + + const mi_threadid_t xtid = (_mi_prim_thread_id() ^ mi_page_xthread_id(page)); + if mi_likely(xtid == 0) { // `tid == mi_page_thread_id(page) && mi_page_flags(page) == 0` + // thread-local, aligned, and not a full page + mi_block_t* const block = mi_validate_block_from_ptr(page,p); + mi_free_block_local(page, block, false /* was guarded */, true /* track stats */, false /* no need to check if the page is full */); + } + else if (xtid <= MI_PAGE_FLAG_MASK) { // `tid == mi_page_thread_id(page) && mi_page_flags(page) != 0` + // page is local, but is full or contains (inner) aligned blocks; use generic path + mi_free_generic_local(page, p); + } + // free-ing in a page owned by a theap in another thread, or an abandoned page (not belonging to a theap) + else if ((xtid & MI_PAGE_FLAG_MASK) == 0) { // `tid != mi_page_thread_id(page) && mi_page_flags(page) == 0` + // blocks are aligned (and not a full page); push on the thread_free list + mi_block_t* const block = mi_validate_block_from_ptr(page,p); + mi_free_block_mt(page,block,false /* was_guarded */); } else { - // not thread-local; use generic path - mi_free_generic_mt(page, segment, p); - } -} - -// return true if successful -bool _mi_free_delayed_block(mi_block_t* block) { - // get segment and page - mi_assert_internal(block!=NULL); - const mi_segment_t* const segment = _mi_ptr_segment(block); - mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie); - mi_assert_internal(_mi_thread_id() == segment->thread_id); - mi_page_t* const page = _mi_segment_page_of(segment, block); - - // Clear the no-delayed flag so delayed freeing is used again for this page. - // This must be done before collecting the free lists on this page -- otherwise - // some blocks may end up in the page `thread_free` list with no blocks in the - // heap `thread_delayed_free` list which may cause the page to be never freed! - // (it would only be freed if we happen to scan it in `mi_page_queue_find_free_ex`) - if (!_mi_page_try_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */)) { - return false; + // page is full or contains (inner) aligned blocks; use generic multi-thread path + mi_free_generic_mt(page, p); } +} - // collect all other non-local frees (move from `thread_free` to `free`) to ensure up-to-date `used` count - _mi_page_free_collect(page, false); +void mi_free(void* p) mi_attr_noexcept { + mi_page_t* const page = mi_validate_ptr_page(p,"mi_free"); + mi_free_ex(p, NULL, page); +} - // and free the block (possibly freeing the page as well since `used` is updated) - mi_free_block_local(page, block, false /* stats have already been adjusted */, true /* check for a full page */); - return true; +void mi_ufree(void* p, size_t* usable) mi_attr_noexcept { + mi_page_t* const page = mi_validate_ptr_page(p,"mi_ufree"); + mi_free_ex(p, usable, page); } -// ------------------------------------------------------ -// Multi-threaded Free (`_mt`) -// ------------------------------------------------------ +void mi_free_small(void* p) mi_attr_noexcept { + // We can only call `mi_free_small` for pointers allocated with `mi_(heap_)malloc_small`. + // If we keep page info in front of the page area for small objects, we can find the info + // just by aligning down the pointer instead of looking it up in the page map. + #if MI_PAGE_META_ALIGNED_FREE_SMALL + #if MI_GUARDED + #warning "MI_PAGE_META_ALIGNED_FREE_SMALL ignored as MI_GUARDED is defined" + mi_free(p); + #elif MI_ARENA_SLICE_ALIGN < MI_SMALL_PAGE_SIZE + #warning "MI_PAGE_META_ALIGNED_FREE_SMALL ignored as the MI_ARENA_SLICE_ALIGN is less than the small page size" + mi_free(p); + #else + mi_page_t* const page = (mi_page_t*)_mi_align_down_ptr(p,MI_SMALL_PAGE_SIZE); + mi_assert(page == mi_validate_ptr_page(p,"mi_free_small")); + mi_assert((void*)page == _mi_align_down_ptr(mi_page_start(page),MI_SMALL_PAGE_SIZE)); + mi_assert(page->block_size <= MI_SMALL_SIZE_MAX); // note: not `MI_SMALL_MAX_OBJ_SIZE` as we need to match `mi_(heap_)malloc_small` + mi_free_ex(p, NULL, page); + #endif + #else + mi_free(p); + #endif +} + + +// -------------------------------------------------------------------------------------------- +// `mi_free_try_collect_mt`: Potentially collect a page in a free in an abandoned page. +// 1. if the page becomes empty, free it +// 2. if it can be reclaimed, reclaim it in our theap +// 3. if it went to < 7/8th used, re-abandon to be mapped (so it can be found by theaps looking for free pages) +// -------------------------------------------------------------------------------------------- -// Push a block that is owned by another thread on its page-local thread free -// list or it's heap delayed free list. Such blocks are later collected by -// the owning thread in `_mi_free_delayed_block`. -static void mi_decl_noinline mi_free_block_delayed_mt( mi_page_t* page, mi_block_t* block ) +// Helper for mi_free_try_collect_mt: free if the page has no more used blocks (this is updated by `_mi_page_free_collect(_partly)`) +static bool mi_abandoned_page_try_free(mi_page_t* page) { - // Try to put the block on either the page-local thread free list, - // or the heap delayed free list (if this is the first non-local free in that page) - mi_thread_free_t tfreex; - bool use_delayed; - mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free); - do { - use_delayed = (mi_tf_delayed(tfree) == MI_USE_DELAYED_FREE); - if mi_unlikely(use_delayed) { - // unlikely: this only happens on the first concurrent free in a page that is in the full list - tfreex = mi_tf_set_delayed(tfree,MI_DELAYED_FREEING); - } - else { - // usual: directly add to page thread_free list - mi_block_set_next(page, block, mi_tf_block(tfree)); - tfreex = mi_tf_set_block(tfree,block); - } - } while (!mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); - - // If this was the first non-local free, we need to push it on the heap delayed free list instead - if mi_unlikely(use_delayed) { - // racy read on `heap`, but ok because MI_DELAYED_FREEING is set (see `mi_heap_delete` and `mi_heap_collect_abandon`) - mi_heap_t* const heap = (mi_heap_t*)(mi_atomic_load_acquire(&page->xheap)); //mi_page_heap(page); - mi_assert_internal(heap != NULL); - if (heap != NULL) { - // add to the delayed free list of this heap. (do this atomically as the lock only protects heap memory validity) - mi_block_t* dfree = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); - do { - mi_block_set_nextx(heap,block,dfree, heap->keys); - } while (!mi_atomic_cas_ptr_weak_release(mi_block_t,&heap->thread_delayed_free, &dfree, block)); - } + if (!mi_page_all_free(page)) return false; + // first remove it from the abandoned pages in the arena (if mapped, this might wait for any readers to finish) + _mi_arenas_page_unabandon(page,NULL); + _mi_arenas_page_free(page,NULL); // we can now free the page directly + return true; +} + +// Helper for mi_free_try_collect_mt: try if we can reabandon a previously abandoned mostly full page to be mapped +static bool mi_abandoned_page_try_reabandon_to_mapped(mi_page_t* page) +{ + // if the page is unmapped, try to reabandon so it can possibly be mapped and found for allocations + // We only reabandon if a full page starts to have enough blocks available to prevent immediate re-abandon of a full page + if (mi_page_is_mostly_used(page)) return false; // not too full + if (page->memid.memkind != MI_MEM_ARENA || mi_page_is_abandoned_mapped(page)) return false; // and not already mapped (or unmappable) - // and reset the MI_DELAYED_FREEING flag - tfree = mi_atomic_load_relaxed(&page->xthread_free); - do { - tfreex = tfree; - mi_assert_internal(mi_tf_delayed(tfree) == MI_DELAYED_FREEING); - tfreex = mi_tf_set_delayed(tfree,MI_NO_DELAYED_FREE); - } while (!mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); + mi_assert(!mi_page_is_full(page)); + return _mi_arenas_page_try_reabandon_to_mapped(page); +} + +// Release ownership of a page. This may free or reabandoned the page if other blocks are concurrently +// freed in the meantime. Returns `true` if the page was freed. +// By passing the captured `expected_thread_free`, we can often avoid calling `mi_page_free_collect`. +static void mi_abandoned_page_unown_from_free(mi_page_t* page, mi_block_t* expected_thread_free) { + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(!mi_page_all_free(page)); + // try to cas atomically the original free list (`mt_free`) back with the ownership cleared. + mi_thread_free_t tf_expect = mi_tf_create(expected_thread_free, true); + mi_thread_free_t tf_new = mi_tf_create(expected_thread_free, false); + while mi_unlikely(!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tf_expect, tf_new)) { + mi_assert_internal(mi_tf_is_owned(tf_expect)); + // while the xthread_free list is not empty.. + while (mi_tf_block(tf_expect) != NULL) { + // if there were concurrent updates to the thread-free list, we retry to free or reabandon to mapped (if it became !mosty_used). + _mi_page_free_collect(page,false); // update used count + if (mi_abandoned_page_try_free(page)) return; + if (mi_abandoned_page_try_reabandon_to_mapped(page)) return; + // otherwise continue un-owning + tf_expect = mi_atomic_load_relaxed(&page->xthread_free); + } + // and try again to release ownership + mi_assert_internal(mi_tf_block(tf_expect)==NULL); + tf_new = mi_tf_create(NULL, false); } } -// Multi-threaded free (`_mt`) (or free in huge block if compiled with MI_HUGE_PAGE_ABANDON) -static void mi_decl_noinline mi_free_block_mt(mi_page_t* page, mi_segment_t* segment, mi_block_t* block) +static inline bool mi_page_queue_len_is_atmost( mi_theap_t* theap, size_t block_size, long atmost) { + if (atmost < 0) return false; + mi_page_queue_t* const pq = mi_page_queue(theap,block_size); + mi_assert_internal(pq!=NULL); + return (pq->count <= (size_t)atmost); +} + +// Helper for mi_free_try_collect_mt: try to reclaim the page for ourselves +static mi_decl_noinline bool mi_abandoned_page_try_reclaim(mi_page_t* page, long reclaim_on_free) mi_attr_noexcept { - // first see if the segment was abandoned and if we can reclaim it into our thread - if (mi_option_is_enabled(mi_option_abandoned_reclaim_on_free) && - #if MI_HUGE_PAGE_ABANDON - segment->page_kind != MI_PAGE_HUGE && - #endif - mi_atomic_load_relaxed(&segment->thread_id) == 0) - { - // the segment is abandoned, try to reclaim it into our heap - if (_mi_segment_attempt_reclaim(mi_heap_get_default(), segment)) { - mi_assert_internal(_mi_prim_thread_id() == mi_atomic_load_relaxed(&segment->thread_id)); - mi_free(block); // recursively free as now it will be a local free in our heap - return; - } + // note: reclaiming can improve benchmarks like `larson` or `rbtree-ck` a lot even in the single-threaded case, + // since free-ing from an owned page avoids atomic operations. However, if we reclaim too eagerly in + // a multi-threaded scenario we may start to hold on to too much memory and reduce reuse among threads. + // If the current theap is where the page originally came from, we reclaim much more eagerly while + // 'cross-thread' reclaiming on free is by default off (and we only 'reclaim' these by finding the abandoned + // pages when we allocate a fresh page). + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(!mi_page_all_free(page)); + mi_assert_internal(page->block_size <= MI_MEDIUM_MAX_OBJ_SIZE); + mi_assert_internal(reclaim_on_free >= 0); + + // dont reclaim if we just have terminated this thread and we should + // not reinitialize the theap for this thread. (can happen due to thread-local destructors for example -- issue #944) + if (!_mi_thread_is_initialized()) return false; + + // get our theap + mi_theap_t* const theap = _mi_page_associated_theap_peek(page); + if (theap==NULL || theap->tld==NULL || !theap->allow_page_reclaim) return false; // see issue #1289 + + // todo: cache `is_in_threadpool` and `exclusive_arena` directly in the theap for performance? + // set max_reclaim limit + long max_reclaim = 0; + if mi_likely(theap == page->theap) { // did this page originate from the current theap? (and thus allocated from this thread) + // originating theap + max_reclaim = _mi_option_get_fast(theap->tld->is_in_threadpool ? mi_option_page_cross_thread_max_reclaim : mi_option_page_max_reclaim); + } + else if (reclaim_on_free == 1 && // if cross-thread is allowed + !theap->tld->is_in_threadpool && // and we are not part of a threadpool + !mi_page_is_mostly_used(page) && // and the page is not too full + _mi_arena_memid_is_suitable(page->memid, _mi_theap_heap(theap)->exclusive_arena)) { // and it fits our memory + // across threads + max_reclaim = _mi_option_get_fast(mi_option_page_cross_thread_max_reclaim); } - // The padding check may access the non-thread-owned page for the key values. - // that is safe as these are constant and the page won't be freed (as the block is not freed yet). - mi_check_padding(page, block); + // are we within the reclaim limit? + if (max_reclaim >= 0 && !mi_page_queue_len_is_atmost(theap, page->block_size, max_reclaim)) { + return false; + } - // adjust stats (after padding check and potentially recursive `mi_free` above) - mi_stat_free(page, block); // stat_free may access the padding - mi_track_free_size(block, mi_page_usable_size_of(page,block)); + // reclaim the page into this theap + // first remove it from the abandoned pages in the arena -- this might wait for any readers to finish + _mi_arenas_page_unabandon(page, theap); + _mi_theap_page_reclaim(theap, page); + mi_theap_stat_counter_increase(theap, pages_reclaim_on_free, 1); + return true; +} - // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection - _mi_padding_shrink(page, block, sizeof(mi_block_t)); - if (segment->kind == MI_SEGMENT_HUGE) { - #if MI_HUGE_PAGE_ABANDON - // huge page segments are always abandoned and can be freed immediately - _mi_segment_huge_page_free(segment, page, block); - return; - #else - // huge pages are special as they occupy the entire segment - // as these are large we reset the memory occupied by the page so it is available to other threads - // (as the owning thread needs to actually free the memory later). - _mi_segment_huge_page_reset(segment, page, block); - #endif +// We freed a block in an abandoned page (that was not owned). Try to collect +static void mi_decl_noinline mi_free_try_collect_mt(mi_page_t* page, mi_block_t* mt_free) mi_attr_noexcept +{ + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + mi_assert_internal(mt_free != NULL); + // we own the page now, and it is safe to collect the thread atomic free list + if (page->block_size <= MI_SMALL_SIZE_MAX) { + // use the `_partly` version to avoid atomic operations since we already have the `mt_free` pointing into the thread free list + // (after this the `used` count might be too high (as some blocks may have been concurrently added to the thread free list and are yet uncounted). + // however, if the page became completely free, the used count is guaranteed to be 0.) + mi_assert_internal(page->reserved>=16); // below this even one freed block goes from full to no longer mostly used. + _mi_page_free_collect_partly(page, mt_free); } else { - #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN // note: when tracking, cannot use mi_usable_size with multi-threading - memset(block, MI_DEBUG_FREED, mi_usable_size(block)); - #endif + // for larger blocks we use the regular collect + _mi_page_free_collect(page,false /* no force */); + mt_free = NULL; // expected page->xthread_free value after collection } + const long reclaim_on_free = _mi_option_get_fast(mi_option_page_reclaim_on_free); + #if MI_DEBUG > 1 + if (mi_page_is_singleton(page)) { mi_assert_internal(mi_page_all_free(page)); } + if (mi_page_is_full(page)) { mi_assert(mi_page_is_mostly_used(page)); } + #endif - // and finally free the actual block by pushing it on the owning heap - // thread_delayed free list (or heap delayed free list) - mi_free_block_delayed_mt(page,block); + // try to: 1. free it, 2. reclaim it, or 3. reabandon it to be mapped + if (mi_abandoned_page_try_free(page)) return; + if (page->block_size <= MI_MEDIUM_MAX_OBJ_SIZE && reclaim_on_free >= 0) { // early test for better codegen + if (mi_abandoned_page_try_reclaim(page, reclaim_on_free)) return; + } + if (mi_abandoned_page_try_reabandon_to_mapped(page)) return; + + // otherwise unown the page again + mi_abandoned_page_unown_from_free(page, mt_free); } @@ -296,19 +397,19 @@ static void mi_decl_noinline mi_free_block_mt(mi_page_t* page, mi_segment_t* seg // Bytes available in a block static size_t mi_decl_noinline mi_page_usable_aligned_size_of(const mi_page_t* page, const void* p) mi_attr_noexcept { const mi_block_t* block = _mi_page_ptr_unalign(page, p); - const size_t size = mi_page_usable_size_of(page, block); + const bool is_guarded = mi_block_ptr_is_guarded(block,p); + const size_t size = mi_page_usable_size_of(page, block, is_guarded); const ptrdiff_t adjust = (uint8_t*)p - (uint8_t*)block; mi_assert_internal(adjust >= 0 && (size_t)adjust <= size); - return (size - adjust); + const size_t aligned_size = (size - adjust); + return aligned_size; } -static inline size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noexcept { - const mi_segment_t* const segment = mi_checked_ptr_segment(p, msg); - if mi_unlikely(segment==NULL) return 0; - const mi_page_t* const page = _mi_segment_page_of(segment, p); - if mi_likely(!mi_page_has_aligned(page)) { +static inline size_t _mi_usable_size(const void* p, const mi_page_t* page) mi_attr_noexcept { + if mi_unlikely(page==NULL) return 0; + if mi_likely(!mi_page_has_interior_pointers(page)) { const mi_block_t* block = (const mi_block_t*)p; - return mi_page_usable_size_of(page, block); + return mi_page_usable_size_of(page, block, false /* is guarded */); } else { // split out to separate routine for improved code generation @@ -317,7 +418,8 @@ static inline size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noe } mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept { - return _mi_usable_size(p, "mi_usable_size"); + const mi_page_t* const page = mi_validate_ptr_page(p,"mi_usable_size"); + return _mi_usable_size(p,page); } @@ -327,7 +429,11 @@ mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept { void mi_free_size(void* p, size_t size) mi_attr_noexcept { MI_UNUSED_RELEASE(size); - mi_assert(p == NULL || size <= _mi_usable_size(p,"mi_free_size")); + #if MI_DEBUG + const mi_page_t* const page = mi_validate_ptr_page(p,"mi_free_size"); + const size_t available = _mi_usable_size(p,page); + mi_assert(p == NULL || size <= available || available == 0 /* invalid pointer */ ); + #endif mi_free(p); } @@ -396,7 +502,7 @@ static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block // --------------------------------------------------------------------------- -// Check for heap block overflow by setting up padding at the end of the block +// Check for theap block overflow by setting up padding at the end of the block // --------------------------------------------------------------------------- #if MI_PADDING // && !MI_TRACK_ENABLED @@ -409,22 +515,28 @@ static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* bloc uintptr_t keys[2]; keys[0] = page->keys[0]; keys[1] = page->keys[1]; - bool ok = ((uint32_t)mi_ptr_encode(page,block,keys) == canary && *delta <= *bsize); + bool ok = (mi_ptr_encode_canary(page,block,keys) == canary && *delta <= *bsize); mi_track_mem_noaccess(padding,sizeof(mi_padding_t)); return ok; } // Return the exact usable size of a block. -static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) { - size_t bsize; - size_t delta; - bool ok = mi_page_decode_padding(page, block, &delta, &bsize); - mi_assert_internal(ok); mi_assert_internal(delta <= bsize); - return (ok ? bsize - delta : 0); +static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block, bool is_guarded) { + if mi_unlikely(is_guarded) { + const size_t bsize = mi_page_block_size(page); + return (bsize - _mi_os_page_size()); + } + else { + size_t bsize; + size_t delta; + bool ok = mi_page_decode_padding(page, block, &delta, &bsize); + mi_assert_internal(ok); mi_assert_internal(delta <= bsize); + return (ok ? bsize - delta : 0); + } } // When a non-thread-local block is freed, it becomes part of the thread delayed free -// list that is freed later by the owning heap. If the exact usable size is too small to +// list that is freed later by the owning theap. If the exact usable size is too small to // contain the pointer for the delayed list, then shrink the padding (by decreasing delta) // so it will later not trigger an overflow error in `mi_free_block`. void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) { @@ -443,15 +555,19 @@ void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const si mi_track_mem_noaccess(padding,sizeof(mi_padding_t)); } #else -static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) { +static inline size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block, bool is_guarded) { MI_UNUSED(block); - return mi_page_usable_block_size(page); + if mi_unlikely(is_guarded) { + const size_t bsize = mi_page_block_size(page); + return (bsize - _mi_os_page_size()); + } + else { + return mi_page_usable_block_size(page); + } } void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) { - MI_UNUSED(page); - MI_UNUSED(block); - MI_UNUSED(min_size); + MI_UNUSED(page); MI_UNUSED(block); MI_UNUSED(min_size); } #endif @@ -485,7 +601,7 @@ static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { size_t size; size_t wrong; if (!mi_verify_padding(page,block,&size,&wrong)) { - _mi_error_message(EFAULT, "buffer overflow in heap block %p of size %zu: write after %zu bytes\n", block, size, wrong ); + _mi_error_message(EFAULT, "buffer overflow in theap block %p of size %zu: write after %zu bytes\n", block, size, wrong ); } } @@ -501,30 +617,61 @@ static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { // only maintain stats for smaller objects if requested #if (MI_STAT>0) static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) { - #if (MI_STAT < 2) MI_UNUSED(block); - #endif - mi_heap_t* const heap = mi_heap_get_default(); + mi_theap_t* const theap = _mi_theap_default(); + if (!mi_theap_is_initialized(theap)) return; // (for now) skip statistics if free'd after thread_done was called (usually a thread cleanup call by the OS) + const size_t bsize = mi_page_usable_block_size(page); - #if (MI_STAT>1) - const size_t usize = mi_page_usable_size_of(page, block); - mi_heap_stat_decrease(heap, malloc, usize); - #endif - if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) { - mi_heap_stat_decrease(heap, normal, bsize); + // #if (MI_STAT>1) + // const size_t usize = mi_page_usable_size_of(page, block); + // mi_theap_stat_decrease(theap, malloc_requested, usize); + // #endif + if (bsize <= MI_LARGE_MAX_OBJ_SIZE) { + mi_theap_stat_decrease(theap, malloc_normal, bsize); #if (MI_STAT > 1) - mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], 1); + mi_theap_stat_decrease(theap, malloc_bins[_mi_bin(bsize)], 1); #endif } - else if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { - mi_heap_stat_decrease(heap, large, bsize); - } else { - mi_heap_stat_decrease(heap, huge, bsize); + const size_t bpsize = mi_page_block_size(page); // match stat in page.c:mi_huge_page_alloc + mi_theap_stat_decrease(theap, malloc_huge, bpsize); } } #else -static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) { +void mi_stat_free(const mi_page_t* page, const mi_block_t* block) { MI_UNUSED(page); MI_UNUSED(block); } #endif + + +// Remove guard page when building with MI_GUARDED +#if MI_GUARDED +static void mi_block_unguard(mi_page_t* page, mi_block_t* block, void* p) { + MI_UNUSED(p); + mi_assert_internal(mi_block_ptr_is_guarded(block, p)); + mi_assert_internal(mi_page_has_interior_pointers(page)); + mi_assert_internal((uint8_t*)p - (uint8_t*)block >= (ptrdiff_t)sizeof(mi_block_t)); + mi_assert_internal(block->next == MI_BLOCK_TAG_GUARDED); + + const size_t bsize = mi_page_block_size(page); + const size_t psize = _mi_os_page_size(); + mi_assert_internal(bsize > psize); + mi_assert_internal(!page->memid.is_pinned); + void* gpage = (uint8_t*)block + bsize - psize; + mi_assert_internal(_mi_is_aligned(gpage, psize)); + _mi_os_unprotect(gpage, psize); +} + +// unguard a whole page (called from `mi_heap_destroy`) +void _mi_page_unguard_all(mi_page_t* page) { + if mi_likely(!mi_page_has_interior_pointers(page)) return; + uint8_t* const start = mi_page_start(page); + const size_t psize = mi_page_committed(page); + _mi_os_unprotect(start,psize); // unprotect all at once as we cannot know which blocks are guarded +} +#else +void _mi_page_unguard_all(mi_page_t* page) { + MI_UNUSED(page); + // nothing to do +} +#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/heap.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/heap.c index e498fdb2..f0a01652 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/heap.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/heap.c @@ -1,5 +1,5 @@ /*---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,647 +7,267 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/atomic.h" -#include "mimalloc/prim.h" // mi_prim_get_default_heap +#include "mimalloc/prim.h" // _mi_theap_default -#include // memset, memcpy - -#if defined(_MSC_VER) && (_MSC_VER < 1920) -#pragma warning(disable:4204) // non-constant aggregate initializer -#endif /* ----------------------------------------------------------- - Helpers + Heap's ----------------------------------------------------------- */ -// return `true` if ok, `false` to break -typedef bool (heap_page_visitor_fun)(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2); - -// Visit all pages in a heap; returns `false` if break was called. -static bool mi_heap_visit_pages(mi_heap_t* heap, heap_page_visitor_fun* fn, void* arg1, void* arg2) -{ - if (heap==NULL || heap->page_count==0) return 0; - - // visit all pages - #if MI_DEBUG>1 - size_t total = heap->page_count; - size_t count = 0; - #endif - - for (size_t i = 0; i <= MI_BIN_FULL; i++) { - mi_page_queue_t* pq = &heap->pages[i]; - mi_page_t* page = pq->first; - while(page != NULL) { - mi_page_t* next = page->next; // save next in case the page gets removed from the queue - mi_assert_internal(mi_page_heap(page) == heap); - #if MI_DEBUG>1 - count++; - #endif - if (!fn(heap, pq, page, arg1, arg2)) return false; - page = next; // and continue - } - } - mi_assert_internal(count == total); - return true; +mi_theap_t* mi_heap_theap(mi_heap_t* heap) { + return _mi_heap_theap(heap); } - -#if MI_DEBUG>=2 -static bool mi_heap_page_is_valid(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { - MI_UNUSED(arg1); - MI_UNUSED(arg2); - MI_UNUSED(pq); - mi_assert_internal(mi_page_heap(page) == heap); - mi_segment_t* segment = _mi_page_segment(page); - mi_assert_internal(segment->thread_id == heap->thread_id); - mi_assert_expensive(_mi_page_is_valid(page)); - return true; -} -#endif -#if MI_DEBUG>=3 -static bool mi_heap_is_valid(mi_heap_t* heap) { - mi_assert_internal(heap!=NULL); - mi_heap_visit_pages(heap, &mi_heap_page_is_valid, NULL, NULL); - return true; +void mi_heap_set_numa_affinity(mi_heap_t* heap, int numa_node) { + if (heap==NULL) { heap = mi_heap_main(); } + heap->numa_node = (numa_node < 0 ? -1 : numa_node % _mi_os_numa_node_count()); } -#endif - - - -/* ----------------------------------------------------------- - "Collect" pages by migrating `local_free` and `thread_free` - lists and freeing empty pages. This is done when a thread - stops (and in that case abandons pages if there are still - blocks alive) ------------------------------------------------------------ */ - -typedef enum mi_collect_e { - MI_NORMAL, - MI_FORCE, - MI_ABANDON -} mi_collect_t; - - -static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg_collect, void* arg2 ) { - MI_UNUSED(arg2); - MI_UNUSED(heap); - mi_assert_internal(mi_heap_page_is_valid(heap, pq, page, NULL, NULL)); - mi_collect_t collect = *((mi_collect_t*)arg_collect); - _mi_page_free_collect(page, collect >= MI_FORCE); - if (collect == MI_FORCE) { - // note: call before a potential `_mi_page_free` as the segment may be freed if this was the last used page in that segment. - mi_segment_t* segment = _mi_page_segment(page); - _mi_segment_collect(segment, true /* force? */, &heap->tld->segments); - } - if (mi_page_all_free(page)) { - // no more used blocks, free the page. - // note: this will free retired pages as well. - _mi_page_free(page, pq, collect >= MI_FORCE); - } - else if (collect == MI_ABANDON) { - // still used blocks but the thread is done; abandon the page - _mi_page_abandon(page, pq); - } - return true; // don't break +void mi_heap_stats_merge_to_subproc(mi_heap_t* heap) { + if (heap==NULL) { heap = mi_heap_main(); } + _mi_stats_merge_into(&heap->subproc->stats, &heap->stats); } -static bool mi_heap_page_never_delayed_free(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { - MI_UNUSED(arg1); - MI_UNUSED(arg2); - MI_UNUSED(heap); - MI_UNUSED(pq); - _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false); - return true; // don't break +void mi_heap_stats_merge_to_main(mi_heap_t* heap) { + if (heap==NULL) return; + _mi_stats_merge_into(&mi_heap_main()->stats, &heap->stats); } -static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect) +static mi_decl_noinline mi_theap_t* mi_heap_init_theap(const mi_heap_t* const_heap) { - if (heap==NULL || !mi_heap_is_initialized(heap)) return; - - const bool force = (collect >= MI_FORCE); - _mi_deferred_free(heap, force); - - // python/cpython#112532: we may be called from a thread that is not the owner of the heap - const bool is_main_thread = (_mi_is_main_thread() && heap->thread_id == _mi_thread_id()); - - // note: never reclaim on collect but leave it to threads that need storage to reclaim - const bool force_main = - #ifdef NDEBUG - collect == MI_FORCE - #else - collect >= MI_FORCE - #endif - && is_main_thread && mi_heap_is_backing(heap) && !heap->no_reclaim; - - if (force_main) { - // the main thread is abandoned (end-of-program), try to reclaim all abandoned segments. - // if all memory is freed by now, all segments should be freed. - _mi_abandoned_reclaim_all(heap, &heap->tld->segments); - } + mi_heap_t* heap = (mi_heap_t*)const_heap; + mi_assert_internal(heap!=NULL); - // if abandoning, mark all pages to no longer add to delayed_free - if (collect == MI_ABANDON) { - mi_heap_visit_pages(heap, &mi_heap_page_never_delayed_free, NULL, NULL); + if (_mi_is_heap_main(heap)) { + // this can be called if the (main) thread is not yet initialized (as no allocation happened) + // but `theap_main_init_get()` will call `mi_thread_init()` + mi_theap_t* const theap = _mi_theap_main_safe(); + mi_assert_internal(theap!=NULL && _mi_is_heap_main(_mi_theap_heap(theap))); + return theap; } - // free all current thread delayed blocks. - // (if abandoning, after this there are no more thread-delayed references into the pages.) - _mi_heap_delayed_free_all(heap); - - // collect retired pages - _mi_heap_collect_retired(heap, force); - - // collect all pages owned by this thread - mi_heap_visit_pages(heap, &mi_heap_page_collect, &collect, NULL); - mi_assert_internal( collect != MI_ABANDON || mi_atomic_load_ptr_acquire(mi_block_t,&heap->thread_delayed_free) == NULL ); - - // collect abandoned segments (in particular, purge expired parts of segments in the abandoned segment list) - // note: forced purge can be quite expensive if many threads are created/destroyed so we do not force on abandonment - _mi_abandoned_collect(heap, collect == MI_FORCE /* force? */, &heap->tld->segments); - - // if forced, collect thread data cache on program-exit (or shared library unload) - if (force && is_main_thread && mi_heap_is_backing(heap)) { - _mi_thread_data_collect(); // collect thread data cache + // otherwise initialize the theap for this heap + // get the thread local + mi_assert_internal(heap->theap != 0); + if (heap->theap==0) { // paranoia + _mi_error_message(EFAULT, "no thread-local reserved for heap (%p)\n", heap); + return NULL; } - - // collect arenas (this is program wide so don't force purges on abandonment of threads) - _mi_arenas_collect(collect == MI_FORCE /* force purge? */, &heap->tld->stats); -} - -void _mi_heap_collect_abandon(mi_heap_t* heap) { - mi_heap_collect_ex(heap, MI_ABANDON); -} - -void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept { - mi_heap_collect_ex(heap, (force ? MI_FORCE : MI_NORMAL)); -} - -void mi_collect(bool force) mi_attr_noexcept { - mi_heap_collect(mi_prim_get_default_heap(), force); -} - - -/* ----------------------------------------------------------- - Heap new ------------------------------------------------------------ */ - -mi_heap_t* mi_heap_get_default(void) { - mi_thread_init(); - return mi_prim_get_default_heap(); -} - -static bool mi_heap_is_default(const mi_heap_t* heap) { - return (heap == mi_prim_get_default_heap()); + mi_theap_t* theap = (mi_theap_t*)_mi_thread_local_get(heap->theap); + + // create a fresh theap? + if (theap==NULL) { + // set first an invalid value to ensure the thread local storage is allocated + if (!_mi_thread_local_set(heap->theap, (mi_theap_t*)1)) { + _mi_error_message(EFAULT, "unable to allocate memory for thread local storage\n"); + return NULL; + } + // then allocate the theap + theap = _mi_theap_create(heap, _mi_theap_default_safe()->tld); + _mi_thread_local_set(heap->theap, theap); // Cannot fail now as it was set before. Always set so the local is valid or NULL (and not 1) + if (theap==NULL) { + _mi_error_message(EFAULT, "unable to allocate memory for a thread local heap\n"); + return NULL; + } + } + return theap; } -mi_heap_t* mi_heap_get_backing(void) { - mi_heap_t* heap = mi_heap_get_default(); - mi_assert_internal(heap!=NULL); - mi_heap_t* bheap = heap->tld->heap_backing; - mi_assert_internal(bheap!=NULL); - mi_assert_internal(bheap->thread_id == _mi_thread_id()); - return bheap; -} - -void _mi_heap_init(mi_heap_t* heap, mi_tld_t* tld, mi_arena_id_t arena_id, bool noreclaim, uint8_t tag) { - _mi_memcpy_aligned(heap, &_mi_heap_empty, sizeof(mi_heap_t)); - heap->tld = tld; - heap->thread_id = _mi_thread_id(); - heap->arena_id = arena_id; - heap->no_reclaim = noreclaim; - heap->tag = tag; - if (heap == tld->heap_backing) { - _mi_random_init(&heap->random); +// get the theap for a heap without initializing (and return NULL in that case) +mi_theap_t* _mi_heap_theap_get_peek(const mi_heap_t* heap) { + if (heap==NULL || _mi_is_heap_main(heap)) { + return _mi_theap_main_safe(); } else { - _mi_random_split(&tld->heap_backing->random, &heap->random); + return (mi_theap_t*)_mi_thread_local_get(heap->theap); } - heap->cookie = _mi_heap_random_next(heap) | 1; - heap->keys[0] = _mi_heap_random_next(heap); - heap->keys[1] = _mi_heap_random_next(heap); - // push on the thread local heaps list - heap->next = heap->tld->heaps; - heap->tld->heaps = heap; -} - -mi_decl_nodiscard mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t arena_id) { - mi_heap_t* bheap = mi_heap_get_backing(); - mi_heap_t* heap = mi_heap_malloc_tp(bheap, mi_heap_t); // todo: OS allocate in secure mode? - if (heap == NULL) return NULL; - // don't reclaim abandoned pages or otherwise destroy is unsafe - _mi_heap_init(heap, bheap->tld, arena_id, true /* no reclaim */, 0 /* default tag */); - return heap; } -mi_decl_nodiscard mi_heap_t* mi_heap_new(void) { - return mi_heap_new_in_arena(_mi_arena_id_none()); -} - -bool _mi_heap_memid_is_suitable(mi_heap_t* heap, mi_memid_t memid) { - return _mi_arena_memid_is_suitable(memid, heap->arena_id); +// get (and possibly create) the theap belonging to a heap +mi_theap_t* _mi_heap_theap_get_or_init(const mi_heap_t* heap) +{ + mi_theap_t* theap = _mi_heap_theap_peek(heap); + if mi_unlikely(theap==NULL) { + theap = mi_heap_init_theap(heap); + if (theap==NULL) { return (mi_theap_t*)&_mi_theap_empty_wrong; } // this will return NULL from page.c:_mi_malloc_generic + } + _mi_theap_cached_set(theap); + return theap; } -uintptr_t _mi_heap_random_next(mi_heap_t* heap) { - return _mi_random_next(&heap->random); -} -// zero out the page queues -static void mi_heap_reset_pages(mi_heap_t* heap) { - mi_assert_internal(heap != NULL); - mi_assert_internal(mi_heap_is_initialized(heap)); - // TODO: copy full empty heap instead? - memset(&heap->pages_free_direct, 0, sizeof(heap->pages_free_direct)); - _mi_memcpy_aligned(&heap->pages, &_mi_heap_empty.pages, sizeof(heap->pages)); - heap->thread_delayed_free = NULL; - heap->page_count = 0; -} +mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t exclusive_arena_id) { + // always allocate heap data in the (subprocess) main heap + mi_heap_t* const heap_main = mi_heap_main(); + // todo: allocate heap data in the exclusive arena ? + mi_heap_t* const heap = (mi_heap_t*)mi_heap_zalloc( heap_main, sizeof(mi_heap_t) ); + if (heap==NULL) return NULL; -// called from `mi_heap_destroy` and `mi_heap_delete` to free the internal heap resources. -static void mi_heap_free(mi_heap_t* heap) { - mi_assert(heap != NULL); - mi_assert_internal(mi_heap_is_initialized(heap)); - if (heap==NULL || !mi_heap_is_initialized(heap)) return; - if (mi_heap_is_backing(heap)) return; // dont free the backing heap - - // reset default - if (mi_heap_is_default(heap)) { - _mi_heap_set_default_direct(heap->tld->heap_backing); + // reserve a thread local slot for this heap (see also issue #1230) + const mi_thread_local_t theap_slot = _mi_thread_local_create(); + if (theap_slot == 0) { + _mi_error_message(EFAULT, "unable to dynamically create a thread local for a heap\n"); + mi_free(heap); + return NULL; } - // remove ourselves from the thread local heaps list - // linear search but we expect the number of heaps to be relatively small - mi_heap_t* prev = NULL; - mi_heap_t* curr = heap->tld->heaps; - while (curr != heap && curr != NULL) { - prev = curr; - curr = curr->next; + // init fields + heap->theap = theap_slot; + heap->subproc = heap_main->subproc; + heap->heap_seq = mi_atomic_increment_relaxed(&heap_main->subproc->heap_total_count); + heap->exclusive_arena = _mi_arena_from_id(exclusive_arena_id); + heap->numa_node = -1; // no initial affinity + mi_stats_header_init(&heap->stats); + mi_lock_init(&heap->theaps_lock); + mi_lock_init(&heap->os_abandoned_pages_lock); + mi_lock_init(&heap->arena_pages_lock); + + // push onto the subproc heaps + mi_lock(&heap->subproc->heaps_lock) { + mi_heap_t* head = heap->subproc->heaps; + heap->prev = NULL; + heap->next = head; + if (head!=NULL) { head->prev = heap; } + heap->subproc->heaps = heap; } - mi_assert_internal(curr == heap); - if (curr == heap) { - if (prev != NULL) { prev->next = heap->next; } - else { heap->tld->heaps = heap->next; } - } - mi_assert_internal(heap->tld->heaps != NULL); - - // and free the used memory - mi_free(heap); + mi_atomic_increment_relaxed(&heap_main->subproc->heap_count); + mi_subproc_stat_increase(heap_main->subproc, heaps, 1); + return heap; } -// return a heap on the same thread as `heap` specialized for the specified tag (if it exists) -mi_heap_t* _mi_heap_by_tag(mi_heap_t* heap, uint8_t tag) { - if (heap->tag == tag) { - return heap; - } - for (mi_heap_t *curr = heap->tld->heaps; curr != NULL; curr = curr->next) { - if (curr->tag == tag) { - return curr; +mi_heap_t* mi_heap_new(void) { + return mi_heap_new_in_arena(0); +} + +// free all theaps belonging to this heap (without deleting their pages as we do this arena wise for efficiency) +static void mi_heap_free_theaps(mi_heap_t* heap) { + // This can run concurrently with a thread that terminates (see `init.c:mi_thread_theaps_done`), + // and we need to ensure we free theaps atomically. + // We do this in a loop where we release the theaps_lock at every potential re-iteration to unblock + // potential concurrent thread termination which tries to remove the theap from our theaps list. + bool all_freed; + do { + all_freed = true; + mi_theap_t* theap = NULL; + mi_lock(&heap->theaps_lock) { + theap = heap->theaps; + while(theap != NULL) { + mi_theap_t* next = theap->hnext; + if (!_mi_theap_free(theap, false /* dont re-acquire the heap->theaps_lock */, true /* acquire the tld->theaps_lock though */ )) { + all_freed = false; + } + theap = next; + } } - } - return NULL; -} - -/* ----------------------------------------------------------- - Heap destroy ------------------------------------------------------------ */ - -static bool _mi_heap_page_destroy(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { - MI_UNUSED(arg1); - MI_UNUSED(arg2); - MI_UNUSED(heap); - MI_UNUSED(pq); - - // ensure no more thread_delayed_free will be added - _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false); - - // stats - const size_t bsize = mi_page_block_size(page); - if (bsize > MI_MEDIUM_OBJ_SIZE_MAX) { - if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { - mi_heap_stat_decrease(heap, large, bsize); + if (!all_freed) { + mi_heap_stat_counter_increase(heap,heaps_delete_wait,1); + _mi_prim_thread_yield(); } - else { - mi_heap_stat_decrease(heap, huge, bsize); - } - } -#if (MI_STAT) - _mi_page_free_collect(page, false); // update used count - const size_t inuse = page->used; - if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { - mi_heap_stat_decrease(heap, normal, bsize * inuse); -#if (MI_STAT>1) - mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], inuse); -#endif - } - mi_heap_stat_decrease(heap, malloc, bsize * inuse); // todo: off for aligned blocks... -#endif - - /// pretend it is all free now - mi_assert_internal(mi_page_thread_free(page) == NULL); - page->used = 0; - - // and free the page - // mi_page_free(page,false); - page->next = NULL; - page->prev = NULL; - _mi_segment_page_free(page,false /* no force? */, &heap->tld->segments); - - return true; // keep going -} - -void _mi_heap_destroy_pages(mi_heap_t* heap) { - mi_heap_visit_pages(heap, &_mi_heap_page_destroy, NULL, NULL); - mi_heap_reset_pages(heap); -} - -#if MI_TRACK_HEAP_DESTROY -static bool mi_cdecl mi_heap_track_block_free(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) { - MI_UNUSED(heap); MI_UNUSED(area); MI_UNUSED(arg); MI_UNUSED(block_size); - mi_track_free_size(block,mi_usable_size(block)); - return true; -} -#endif - -void mi_heap_destroy(mi_heap_t* heap) { - mi_assert(heap != NULL); - mi_assert(mi_heap_is_initialized(heap)); - mi_assert(heap->no_reclaim); - mi_assert_expensive(mi_heap_is_valid(heap)); - if (heap==NULL || !mi_heap_is_initialized(heap)) return; - if (!heap->no_reclaim) { - // don't free in case it may contain reclaimed pages - mi_heap_delete(heap); - } - else { - // track all blocks as freed - #if MI_TRACK_HEAP_DESTROY - mi_heap_visit_blocks(heap, true, mi_heap_track_block_free, NULL); - #endif - // free all pages - _mi_heap_destroy_pages(heap); - mi_heap_free(heap); + else { + mi_assert_internal(heap->theaps==NULL); + } } + while(!all_freed); } -// forcefully destroy all heaps in the current thread -void _mi_heap_unsafe_destroy_all(void) { - mi_heap_t* bheap = mi_heap_get_backing(); - mi_heap_t* curr = bheap->tld->heaps; - while (curr != NULL) { - mi_heap_t* next = curr->next; - if (curr->no_reclaim) { - mi_heap_destroy(curr); - } - else { - _mi_heap_destroy_pages(curr); +// free the heap resources (assuming the pages are already moved/destroyed, and all theaps have been freed) +static void mi_heap_free(mi_heap_t* heap) { + mi_assert_internal(heap!=NULL && !_mi_is_heap_main(heap)); + + // free all arena pages infos + mi_lock(&heap->arena_pages_lock) { + for (size_t i = 0; i < MI_MAX_ARENAS; i++) { + mi_arena_pages_t* arena_pages = mi_atomic_load_ptr_relaxed(mi_arena_pages_t, &heap->arena_pages[i]); + if (arena_pages!=NULL) { + mi_atomic_store_ptr_relaxed(mi_arena_pages_t, &heap->arena_pages[i], NULL); + mi_free(arena_pages); + } } - curr = next; } -} - -/* ----------------------------------------------------------- - Safe Heap delete ------------------------------------------------------------ */ -// Transfer the pages from one heap to the other -static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) { - mi_assert_internal(heap!=NULL); - if (from==NULL || from->page_count == 0) return; - - // reduce the size of the delayed frees - _mi_heap_delayed_free_partial(from); - - // transfer all pages by appending the queues; this will set a new heap field - // so threads may do delayed frees in either heap for a while. - // note: appending waits for each page to not be in the `MI_DELAYED_FREEING` state - // so after this only the new heap will get delayed frees - for (size_t i = 0; i <= MI_BIN_FULL; i++) { - mi_page_queue_t* pq = &heap->pages[i]; - mi_page_queue_t* append = &from->pages[i]; - size_t pcount = _mi_page_queue_append(heap, pq, append); - heap->page_count += pcount; - from->page_count -= pcount; + // remove the heap from the subproc + mi_heap_stats_merge_to_main(heap); + mi_atomic_decrement_relaxed(&heap->subproc->heap_count); + mi_subproc_stat_decrease(heap->subproc, heaps, 1); + mi_lock(&heap->subproc->heaps_lock) { + if (heap->next!=NULL) { heap->next->prev = heap->prev; } + if (heap->prev!=NULL) { heap->prev->next = heap->next; } + else { heap->subproc->heaps = heap->next; } } - mi_assert_internal(from->page_count == 0); - - // and do outstanding delayed frees in the `from` heap - // note: be careful here as the `heap` field in all those pages no longer point to `from`, - // turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a - // the regular `_mi_free_delayed_block` which is safe. - _mi_heap_delayed_free_all(from); - #if !defined(_MSC_VER) || (_MSC_VER > 1900) // somehow the following line gives an error in VS2015, issue #353 - mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_block_t,&from->thread_delayed_free) == NULL); - #endif - - // and reset the `from` heap - mi_heap_reset_pages(from); + + _mi_thread_local_free(heap->theap); + mi_lock_done(&heap->theaps_lock); + mi_lock_done(&heap->os_abandoned_pages_lock); + mi_lock_done(&heap->arena_pages_lock); + mi_free(heap); } -// Safe delete a heap without freeing any still allocated blocks in that heap. -void mi_heap_delete(mi_heap_t* heap) -{ - mi_assert(heap != NULL); - mi_assert(mi_heap_is_initialized(heap)); - mi_assert_expensive(mi_heap_is_valid(heap)); - if (heap==NULL || !mi_heap_is_initialized(heap)) return; - - if (!mi_heap_is_backing(heap)) { - // transfer still used pages to the backing heap - mi_heap_absorb(heap->tld->heap_backing, heap); +void mi_heap_delete(mi_heap_t* heap) { + if (heap==NULL) return; + if (_mi_is_heap_main(heap)) { + _mi_warning_message("cannot delete the main heap\n"); + return; } - else { - // the backing heap abandons its pages - _mi_heap_collect_abandon(heap); - } - mi_assert_internal(heap->page_count==0); + mi_heap_free_theaps(heap); + _mi_heap_move_pages(heap, mi_heap_main()); mi_heap_free(heap); } -mi_heap_t* mi_heap_set_default(mi_heap_t* heap) { - mi_assert(heap != NULL); - mi_assert(mi_heap_is_initialized(heap)); - if (heap==NULL || !mi_heap_is_initialized(heap)) return NULL; - mi_assert_expensive(mi_heap_is_valid(heap)); - mi_heap_t* old = mi_prim_get_default_heap(); - _mi_heap_set_default_direct(heap); - return old; -} - - - - -/* ----------------------------------------------------------- - Analysis ------------------------------------------------------------ */ - -// static since it is not thread safe to access heaps from other threads. -static mi_heap_t* mi_heap_of_block(const void* p) { - if (p == NULL) return NULL; - mi_segment_t* segment = _mi_ptr_segment(p); - bool valid = (_mi_ptr_cookie(segment) == segment->cookie); - mi_assert_internal(valid); - if mi_unlikely(!valid) return NULL; - return mi_page_heap(_mi_segment_page_of(segment,p)); -} - -bool mi_heap_contains_block(mi_heap_t* heap, const void* p) { - mi_assert(heap != NULL); - if (heap==NULL || !mi_heap_is_initialized(heap)) return false; - return (heap == mi_heap_of_block(p)); -} - - -static bool mi_heap_page_check_owned(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* p, void* vfound) { - MI_UNUSED(heap); - MI_UNUSED(pq); - bool* found = (bool*)vfound; - void* start = mi_page_start(page); - void* end = (uint8_t*)start + (page->capacity * mi_page_block_size(page)); - *found = (p >= start && p < end); - return (!*found); // continue if not found -} - -bool mi_heap_check_owned(mi_heap_t* heap, const void* p) { - mi_assert(heap != NULL); - if (heap==NULL || !mi_heap_is_initialized(heap)) return false; - if (((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) return false; // only aligned pointers - bool found = false; - mi_heap_visit_pages(heap, &mi_heap_page_check_owned, (void*)p, &found); - return found; -} - -bool mi_check_owned(const void* p) { - return mi_heap_check_owned(mi_prim_get_default_heap(), p); +void _mi_heap_force_destroy(mi_heap_t* heap) { + if (heap==NULL) return; + mi_heap_free_theaps(heap); + _mi_heap_destroy_pages(heap); + if (!_mi_is_heap_main(heap)) { mi_heap_free(heap); } // todo: release locks of the main heap? } -/* ----------------------------------------------------------- - Visit all heap blocks and areas - Todo: enable visiting abandoned pages, and - enable visiting all blocks of all heaps across threads ------------------------------------------------------------ */ - -// Separate struct to keep `mi_page_t` out of the public interface -typedef struct mi_heap_area_ex_s { - mi_heap_area_t area; - mi_page_t* page; -} mi_heap_area_ex_t; - -static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_visit_fun* visitor, void* arg) { - mi_assert(xarea != NULL); - if (xarea==NULL) return true; - const mi_heap_area_t* area = &xarea->area; - mi_page_t* page = xarea->page; - mi_assert(page != NULL); - if (page == NULL) return true; - - _mi_page_free_collect(page,true); - mi_assert_internal(page->local_free == NULL); - if (page->used == 0) return true; - - const size_t bsize = mi_page_block_size(page); - const size_t ubsize = mi_page_usable_block_size(page); // without padding - size_t psize; - uint8_t* pstart = _mi_segment_page_start(_mi_page_segment(page), page, &psize); - - if (page->capacity == 1) { - // optimize page with one block - mi_assert_internal(page->used == 1 && page->free == NULL); - return visitor(mi_page_heap(page), area, pstart, ubsize, arg); - } - - // create a bitmap of free blocks. - #define MI_MAX_BLOCKS (MI_SMALL_PAGE_SIZE / sizeof(void*)) - uintptr_t free_map[MI_MAX_BLOCKS / sizeof(uintptr_t)]; - memset(free_map, 0, sizeof(free_map)); - - #if MI_DEBUG>1 - size_t free_count = 0; - #endif - for (mi_block_t* block = page->free; block != NULL; block = mi_block_next(page,block)) { - #if MI_DEBUG>1 - free_count++; - #endif - mi_assert_internal((uint8_t*)block >= pstart && (uint8_t*)block < (pstart + psize)); - size_t offset = (uint8_t*)block - pstart; - mi_assert_internal(offset % bsize == 0); - size_t blockidx = offset / bsize; // Todo: avoid division? - mi_assert_internal( blockidx < MI_MAX_BLOCKS); - size_t bitidx = (blockidx / sizeof(uintptr_t)); - size_t bit = blockidx - (bitidx * sizeof(uintptr_t)); - free_map[bitidx] |= ((uintptr_t)1 << bit); - } - mi_assert_internal(page->capacity == (free_count + page->used)); - - // walk through all blocks skipping the free ones - #if MI_DEBUG>1 - size_t used_count = 0; - #endif - for (size_t i = 0; i < page->capacity; i++) { - size_t bitidx = (i / sizeof(uintptr_t)); - size_t bit = i - (bitidx * sizeof(uintptr_t)); - uintptr_t m = free_map[bitidx]; - if (bit == 0 && m == UINTPTR_MAX) { - i += (sizeof(uintptr_t) - 1); // skip a run of free blocks - } - else if ((m & ((uintptr_t)1 << bit)) == 0) { - #if MI_DEBUG>1 - used_count++; - #endif - uint8_t* block = pstart + (i * bsize); - if (!visitor(mi_page_heap(page), area, block, ubsize, arg)) return false; - } +void mi_heap_destroy(mi_heap_t* heap) { + if (heap==NULL) return; + if (_mi_is_heap_main(heap)) { + _mi_warning_message("cannot destroy the main heap\n"); + return; } - mi_assert_internal(page->used == used_count); - return true; + _mi_heap_force_destroy(heap); } -typedef bool (mi_heap_area_visit_fun)(const mi_heap_t* heap, const mi_heap_area_ex_t* area, void* arg); - - -static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* vfun, void* arg) { - MI_UNUSED(heap); - MI_UNUSED(pq); - mi_heap_area_visit_fun* fun = (mi_heap_area_visit_fun*)vfun; - mi_heap_area_ex_t xarea; - const size_t bsize = mi_page_block_size(page); - const size_t ubsize = mi_page_usable_block_size(page); - xarea.page = page; - xarea.area.reserved = page->reserved * bsize; - xarea.area.committed = page->capacity * bsize; - xarea.area.blocks = mi_page_start(page); - xarea.area.used = page->used; // number of blocks in use (#553) - xarea.area.block_size = ubsize; - xarea.area.full_block_size = bsize; - return fun(heap, &xarea, arg); +mi_heap_t* mi_heap_of(const void* p) { + mi_page_t* page = _mi_safe_ptr_page(p); + if (page==NULL) return NULL; + return mi_page_heap(page); } -// Visit all heap pages as areas -static bool mi_heap_visit_areas(const mi_heap_t* heap, mi_heap_area_visit_fun* visitor, void* arg) { - if (visitor == NULL) return false; - return mi_heap_visit_pages((mi_heap_t*)heap, &mi_heap_visit_areas_page, (void*)(visitor), arg); // note: function pointer to void* :-{ +bool mi_any_heap_contains(const void* p) { + return (mi_heap_of(p)!=NULL); } -// Just to pass arguments -typedef struct mi_visit_blocks_args_s { - bool visit_blocks; - mi_block_visit_fun* visitor; - void* arg; -} mi_visit_blocks_args_t; - -static bool mi_heap_area_visitor(const mi_heap_t* heap, const mi_heap_area_ex_t* xarea, void* arg) { - mi_visit_blocks_args_t* args = (mi_visit_blocks_args_t*)arg; - if (!args->visitor(heap, &xarea->area, NULL, xarea->area.block_size, args->arg)) return false; - if (args->visit_blocks) { - return mi_heap_area_visit_blocks(xarea, args->visitor, args->arg); - } - else { - return true; - } +bool mi_heap_contains(const mi_heap_t* heap, const void* p) { + if (heap==NULL) { heap = mi_heap_main(); } + return (heap==mi_heap_of(p)); } -// Visit all blocks in a heap -bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) { - mi_visit_blocks_args_t args = { visit_blocks, visitor, arg }; - return mi_heap_visit_areas(heap, &mi_heap_area_visitor, &args); +// deprecated +bool mi_check_owned(const void* p) { + return mi_any_heap_contains(p); +} + +// unsafe heap utilization function for DragonFly (see issue #1258) +// If the page of pointer `p` belongs to `heap` (or `heap==NULL`) and has less than `perc_threshold` used blocks in its used area return `true`. +// This function is unsafe in general as it assumes we are the only thread accessing the page of `p`. +bool mi_unsafe_heap_page_is_under_utilized(mi_heap_t* heap, void* p, size_t perc_threshold) mi_attr_noexcept { + if (p==NULL) return false; + const mi_page_t* const page = _mi_safe_ptr_page(p); // Get the page containing this pointer + if (page==NULL || page->used==page->capacity || page->capacity < page->reserved) return false; + // If the page is the head of the queue, it is currently being used for + // allocations; we skip it to avoid immediate thrashing. + if (page->prev == NULL) return false; + + // match heap? + const mi_heap_t* const page_heap = mi_page_heap(page); + if (page_heap==NULL) return false; + if (heap!=NULL && page_heap!=heap) return false; + + // check utilization + if (page->capacity==0) return false; + if (perc_threshold>=100) return true; + return (perc_threshold >= ((100UL*page->used) / page->capacity)); } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/init.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/init.c index 6f51ca89..39f4d03e 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/init.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/init.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2022, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -11,35 +11,34 @@ terms of the MIT license. A copy of the license can be found in the file #include // memcpy, memset #include // atexit +#define MI_MEMID_INIT(kind) {{{NULL,0}}, kind, true /* pinned */, true /* committed */, false /* zero */ } +#define MI_MEMID_STATIC MI_MEMID_INIT(MI_MEM_STATIC) // Empty page used to initialize the small free pages array const mi_page_t _mi_page_empty = { - 0, - false, false, false, false, - 0, // capacity - 0, // reserved capacity - { 0 }, // flags - false, // is_zero - 0, // retire_expire - NULL, // free - NULL, // local_free - 0, // used - 0, // block size shift - 0, // heap tag - 0, // block_size - NULL, // page_start + MI_ATOMIC_VAR_INIT(0), // xthread_id + NULL, // free + 0, // used + 0, // capacity + 0, // reserved capacity + 0, // retire_expire + false, // is_zero + NULL, // local_free + MI_ATOMIC_VAR_INIT(0), // xthread_free + 0, // block_size + 0, // page_woffset + MI_ARENA_SLICE_SIZE, // page_committed #if (MI_PADDING || MI_ENCODE_FREELIST) - { 0, 0 }, + { 0, 0 }, // keys #endif - MI_ATOMIC_VAR_INIT(0), // xthread_free - MI_ATOMIC_VAR_INIT(0), // xheap - NULL, NULL - , { 0 } // padding + NULL, // theap + NULL, // heap + NULL, NULL, // next, prev + MI_MEMID_STATIC // memid }; #define MI_PAGE_EMPTY() ((mi_page_t*)&_mi_page_empty) -#if (MI_SMALL_WSIZE_MAX==128) #if (MI_PADDING>0) && (MI_INTPTR_SIZE >= 8) #define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() } #elif (MI_PADDING>0) @@ -47,12 +46,10 @@ const mi_page_t _mi_page_empty = { #else #define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY() } #endif -#else -#error "define right initialization sizes corresponding to MI_SMALL_WSIZE_MAX" -#endif + // Empty page queues for every bin -#define QNULL(sz) { NULL, NULL, (sz)*sizeof(uintptr_t) } +#define QNULL(sz) { NULL, NULL, 0, (sz)*sizeof(uintptr_t) } #define MI_PAGE_QUEUES_EMPTY \ { QNULL(1), \ QNULL( 1), QNULL( 2), QNULL( 3), QNULL( 4), QNULL( 5), QNULL( 6), QNULL( 7), QNULL( 8), /* 8 */ \ @@ -64,313 +61,620 @@ const mi_page_t _mi_page_empty = { QNULL( 10240), QNULL( 12288), QNULL( 14336), QNULL( 16384), QNULL( 20480), QNULL( 24576), QNULL( 28672), QNULL( 32768), /* 56 */ \ QNULL( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), /* 64 */ \ QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), QNULL(393216), QNULL(458752), QNULL(524288), /* 72 */ \ - QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \ - QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 2) /* Full queue */ } + QNULL(MI_LARGE_MAX_OBJ_WSIZE + 1 /* 655360, Huge queue */), \ + QNULL(MI_LARGE_MAX_OBJ_WSIZE + 2) /* Full queue */ } -#define MI_STAT_COUNT_NULL() {0,0,0,0} +#define MI_STAT_COUNT_NULL() {0,0,0} // Empty statistics -#if MI_STAT>1 -#define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT32(MI_STAT_COUNT_NULL) } -#else -#define MI_STAT_COUNT_END_NULL() -#endif +#define MI_STAT_COUNT(stat) {0,0,0}, +#define MI_STAT_COUNTER(stat) {0}, #define MI_STATS_NULL \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ - MI_STAT_COUNT_NULL(), \ - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ - { 0, 0 } \ - MI_STAT_COUNT_END_NULL() - - -// Empty slice span queues for every bin -#define SQNULL(sz) { NULL, NULL, sz } -#define MI_SEGMENT_SPAN_QUEUES_EMPTY \ - { SQNULL(1), \ - SQNULL( 1), SQNULL( 2), SQNULL( 3), SQNULL( 4), SQNULL( 5), SQNULL( 6), SQNULL( 7), SQNULL( 10), /* 8 */ \ - SQNULL( 12), SQNULL( 14), SQNULL( 16), SQNULL( 20), SQNULL( 24), SQNULL( 28), SQNULL( 32), SQNULL( 40), /* 16 */ \ - SQNULL( 48), SQNULL( 56), SQNULL( 64), SQNULL( 80), SQNULL( 96), SQNULL( 112), SQNULL( 128), SQNULL( 160), /* 24 */ \ - SQNULL( 192), SQNULL( 224), SQNULL( 256), SQNULL( 320), SQNULL( 384), SQNULL( 448), SQNULL( 512), SQNULL( 640), /* 32 */ \ - SQNULL( 768), SQNULL( 896), SQNULL( 1024) /* 35 */ } - + MI_STAT_FIELDS() \ + \ + { MI_INIT4(MI_STAT_COUNT_NULL) }, \ + { { 0 }, { 0 }, { 0 }, { 0 } }, \ + \ + { MI_INIT74(MI_STAT_COUNT_NULL) }, \ + { MI_INIT74(MI_STAT_COUNT_NULL) }, \ + { MI_INIT5(MI_STAT_COUNT_NULL) } // -------------------------------------------------------- -// Statically allocate an empty heap as the initial -// thread local value for the default heap, -// and statically allocate the backing heap for the main +// Statically allocate an empty theap as the initial +// thread local value for the default theap, +// and statically allocate the backing theap for the main // thread so it can function without doing any allocation // itself (as accessing a thread local for the first time // may lead to allocation itself on some platforms) // -------------------------------------------------------- -mi_decl_cache_align const mi_heap_t _mi_heap_empty = { - NULL, - MI_ATOMIC_VAR_INIT(NULL), - 0, // tid - 0, // cookie - 0, // arena id - { 0, 0 }, // keys - { {0}, {0}, 0, true }, // random - 0, // page count - MI_BIN_FULL, 0, // page retired min/max - NULL, // next - false, // can reclaim - 0, // tag - MI_SMALL_PAGES_EMPTY, - MI_PAGE_QUEUES_EMPTY +static mi_decl_cache_align mi_subproc_t subproc_main +#if __cplusplus + = { }; // empty initializer to prevent running the constructor (with msvc) +#else + = { 0 }; // C zero initialize +#endif + +static mi_subproc_t* subprocs = &subproc_main; +static mi_lock_t subprocs_lock; + +static mi_decl_cache_align mi_tld_t tld_empty = { + 0, // thread_id + 0, // thread_seq + 0, // default numa node + &subproc_main, // subproc + NULL, // theaps list + MI_LOCK_INITIALIZER, // theaps lock + false, // recurse + false, // is_in_threadpool + MI_MEMID_STATIC // memid }; -#define tld_empty_stats ((mi_stats_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,stats))) -#define tld_empty_os ((mi_os_tld_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,os))) +mi_decl_cache_align const mi_theap_t _mi_theap_empty = { + &tld_empty, // tld + MI_ATOMIC_VAR_INIT(NULL), // heap + MI_ATOMIC_VAR_INIT(1), // refcount + MI_ATOMIC_VAR_INIT(0), // freed + 0, // heartbeat + 0, // cookie + { {0}, {0}, 0, true }, // random + 0, // page count + MI_BIN_FULL, 0, // page retired min/max + 0, // pages_full_size + 0, 0, // generic count + NULL, NULL, // tnext, tprev + NULL, NULL, // hnext, hprev + 0, // full page retain + false, // allow reclaim + true, // allow abandon + #if MI_GUARDED + 0, 0, 0, 1, // sample count is 1 so we never write to it (see `internal.h:mi_theap_malloc_use_guarded`) + #endif + MI_SMALL_PAGES_EMPTY, + MI_PAGE_QUEUES_EMPTY, + MI_MEMID_STATIC, + { sizeof(mi_stats_t), MI_STAT_VERSION, MI_STATS_NULL }, // stats +}; -mi_decl_cache_align static const mi_tld_t tld_empty = { - 0, - false, - NULL, NULL, - { MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, 0, tld_empty_stats, tld_empty_os }, // segments - { 0, tld_empty_stats }, // os - { MI_STATS_NULL } // stats +mi_decl_cache_align const mi_theap_t _mi_theap_empty_wrong = { + &tld_empty, // tld + MI_ATOMIC_VAR_INIT(NULL), // heap + MI_ATOMIC_VAR_INIT(1), // refcount + MI_ATOMIC_VAR_INIT(0), // freed + 0, // heartbeat + 0, // cookie + { {0}, {0}, 0, true }, // random + 0, // page count + MI_BIN_FULL, 0, // page retired min/max + 0, // pages_full_size + 0, 0, // generic count + NULL, NULL, // tnext, tprev + NULL, NULL, // hnext, hprev + 0, // full page retain + false, // allow reclaim + true, // allow abandon + #if MI_GUARDED + 0, 0, 0, 1, // sample count is 1 so we never write to it (see `internal.h:mi_theap_malloc_use_guarded`) + #endif + MI_SMALL_PAGES_EMPTY, + MI_PAGE_QUEUES_EMPTY, + MI_MEMID_STATIC, + { sizeof(mi_stats_t), MI_STAT_VERSION, MI_STATS_NULL }, // stats }; -mi_threadid_t _mi_thread_id(void) mi_attr_noexcept { - return _mi_prim_thread_id(); -} +// Heap for the main thread -// the thread-local default heap for allocation -mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty; +#define MI_THREADID_INVALID ((mi_threadid_t)(~0)) -extern mi_heap_t _mi_heap_main; +extern mi_decl_hidden mi_decl_cache_align mi_theap_t theap_main; +extern mi_decl_hidden mi_decl_cache_align mi_heap_t heap_main; -static mi_tld_t tld_main = { - 0, false, - &_mi_heap_main, & _mi_heap_main, - { MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, 0, &tld_main.stats, &tld_main.os }, // segments - { 0, &tld_main.stats }, // os - { MI_STATS_NULL } // stats +static mi_decl_cache_align mi_tld_t tld_main = { + 0, // thread_id + 0, // thread_seq + 0, // numa node + &subproc_main, // subproc + &theap_main, // theaps list + MI_LOCK_INITIALIZER, // theaps lock + false, // recurse + false, // is_in_threadpool + MI_MEMID_STATIC // memid }; -mi_heap_t _mi_heap_main = { - &tld_main, - MI_ATOMIC_VAR_INIT(NULL), - 0, // thread id - 0, // initial cookie - 0, // arena id - { 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!) +mi_decl_cache_align mi_theap_t theap_main = { + &tld_main, // thread local data + MI_ATOMIC_VAR_INIT(&heap_main), // main heap + MI_ATOMIC_VAR_INIT(1), // refcount + MI_ATOMIC_VAR_INIT(0), // freed + 0, // heartbeat + 0, // initial cookie { {0x846ca68b}, {0}, 0, true }, // random - 0, // page count - MI_BIN_FULL, 0, // page retired min/max - NULL, // next heap - false, // can reclaim - 0, // tag + 0, // page count + MI_BIN_FULL, 0, // page retired min/max + 0, // pages_full_size + 0, 0, // generic count + NULL, NULL, // tnext, tprev + NULL, NULL, // hnext, hprev + 2, // full page retain + true, // allow page reclaim + true, // allow page abandon + #if MI_GUARDED + 0, 0, 0, 0, + #endif MI_SMALL_PAGES_EMPTY, - MI_PAGE_QUEUES_EMPTY + MI_PAGE_QUEUES_EMPTY, + MI_MEMID_STATIC, + { sizeof(mi_stats_t), MI_STAT_VERSION, MI_STATS_NULL }, // stats }; +mi_decl_cache_align mi_heap_t heap_main +#if __cplusplus + = { }; // empty initializer to prevent running the constructor (with msvc) +#else + = { 0 }; // C zero initialize +#endif + +// the theap belonging to the main heap +mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_main = NULL; + +mi_threadid_t _mi_thread_id(void) mi_attr_noexcept { + const mi_threadid_t tid = _mi_prim_thread_id(); + mi_assert_internal( (tid & 0x03) == 0 ); // mimalloc reserves the bottom 2 bits + return tid; +} + +#if MI_TLS_MODEL_THREAD_LOCAL +// the thread-local main theap for allocation +mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_default = (mi_theap_t*)&_mi_theap_empty; +// the last used non-main theap +mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_cached = (mi_theap_t*)&_mi_theap_empty; +#endif + bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`. -mi_stats_t _mi_stats_main = { MI_STATS_NULL }; +mi_stats_t _mi_stats_main = { sizeof(mi_stats_t), MI_STAT_VERSION, MI_STATS_NULL }; +#undef MI_STAT_COUNT +#undef MI_STAT_COUNTER -static void mi_heap_main_init(void) { - if (_mi_heap_main.cookie == 0) { - _mi_heap_main.thread_id = _mi_thread_id(); - _mi_heap_main.cookie = 1; - #if defined(_WIN32) && !defined(MI_SHARED_LIB) - _mi_random_init_weak(&_mi_heap_main.random); // prevent allocation failure during bcrypt dll initialization with static linking - #else - _mi_random_init(&_mi_heap_main.random); - #endif - _mi_heap_main.cookie = _mi_heap_random_next(&_mi_heap_main); - _mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main); - _mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main); + +#if MI_GUARDED +mi_decl_export void mi_theap_guarded_set_sample_rate(mi_theap_t* theap, size_t sample_rate, size_t seed) { + theap->guarded_sample_rate = sample_rate; + theap->guarded_sample_count = sample_rate; // count down samples + if (theap->guarded_sample_rate > 1) { + if (seed == 0) { + seed = _mi_theap_random_next(theap); + } + theap->guarded_sample_count = (seed % theap->guarded_sample_rate) + 1; // start at random count between 1 and `sample_rate` } } -mi_heap_t* _mi_heap_main_get(void) { - mi_heap_main_init(); - return &_mi_heap_main; +mi_decl_export void mi_theap_guarded_set_size_bound(mi_theap_t* theap, size_t min, size_t max) { + theap->guarded_size_min = min; + theap->guarded_size_max = (min > max ? min : max); } +void _mi_theap_guarded_init(mi_theap_t* theap) { + mi_theap_guarded_set_sample_rate(theap, + (size_t)mi_option_get_clamp(mi_option_guarded_sample_rate, 0, LONG_MAX), + (size_t)mi_option_get(mi_option_guarded_sample_seed)); + mi_theap_guarded_set_size_bound(theap, + (size_t)mi_option_get_clamp(mi_option_guarded_min, 0, LONG_MAX), + (size_t)mi_option_get_clamp(mi_option_guarded_max, 0, LONG_MAX) ); +} +#else +mi_decl_export void mi_theap_guarded_set_sample_rate(mi_theap_t* theap, size_t sample_rate, size_t seed) { + MI_UNUSED(theap); MI_UNUSED(sample_rate); MI_UNUSED(seed); +} + +mi_decl_export void mi_theap_guarded_set_size_bound(mi_theap_t* theap, size_t min, size_t max) { + MI_UNUSED(theap); MI_UNUSED(min); MI_UNUSED(max); +} +void _mi_theap_guarded_init(mi_theap_t* theap) { + MI_UNUSED(theap); +} +#endif /* ----------------------------------------------------------- - Initialization and freeing of the thread local heaps + Initialization + Note: on some platforms lock_init or just a thread local access + can cause allocation and induce recursion during initialization. ----------------------------------------------------------- */ -// note: in x64 in release build `sizeof(mi_thread_data_t)` is under 4KiB (= OS page size). -typedef struct mi_thread_data_s { - mi_heap_t heap; // must come first due to cast in `_mi_heap_done` - mi_tld_t tld; - mi_memid_t memid; // must come last due to zero'ing -} mi_thread_data_t; - - -// Thread meta-data is allocated directly from the OS. For -// some programs that do not use thread pools and allocate and -// destroy many OS threads, this may causes too much overhead -// per thread so we maintain a small cache of recently freed metadata. - -#define TD_CACHE_SIZE (16) -static _Atomic(mi_thread_data_t*) td_cache[TD_CACHE_SIZE]; - -static mi_thread_data_t* mi_thread_data_zalloc(void) { - // try to find thread metadata in the cache - bool is_zero = false; - mi_thread_data_t* td = NULL; - for (int i = 0; i < TD_CACHE_SIZE; i++) { - td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]); - if (td != NULL) { - // found cached allocation, try use it - td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL); - if (td != NULL) { - break; - } - } + +// Initialize main subproc +static void mi_subproc_main_init(void) { + if (subproc_main.memid.memkind != MI_MEM_STATIC) { + subproc_main.memid = _mi_memid_create(MI_MEM_STATIC); + subproc_main.heaps = &heap_main; + subproc_main.heap_total_count = 1; + subproc_main.heap_count = 1; + mi_atomic_store_ptr_release(mi_heap_t, &subproc_main.heap_main, &heap_main); + __mi_stat_increase_mt(&subproc_main.stats.heaps, 1); + mi_stats_header_init(&subproc_main.stats); + mi_lock_init(&subproc_main.arena_reserve_lock); + mi_lock_init(&subproc_main.heaps_lock); + mi_lock_init(&subprocs_lock); + mi_lock_init(&tld_empty.theaps_lock); } +} - // if that fails, allocate as meta data - if (td == NULL) { - mi_memid_t memid; - td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid, &_mi_stats_main); - if (td == NULL) { - // if this fails, try once more. (issue #257) - td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid, &_mi_stats_main); - if (td == NULL) { - // really out of memory - _mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t)); - } - } - if (td != NULL) { - td->memid = memid; - is_zero = memid.initially_zero; - } +// Initialize main tld +static void mi_tld_main_init(void) { + if (tld_main.thread_id == 0) { + tld_main.thread_id = _mi_prim_thread_id(); + mi_lock_init(&tld_main.theaps_lock); } +} - if (td != NULL && !is_zero) { - _mi_memzero_aligned(td, offsetof(mi_thread_data_t,memid)); +void _mi_theap_options_init(mi_theap_t* theap) { + theap->allow_page_reclaim = (mi_option_get(mi_option_page_reclaim_on_free) >= 0); + theap->allow_page_abandon = (mi_option_get(mi_option_page_full_retain) >= 0); + theap->page_full_retain = mi_option_get_clamp(mi_option_page_full_retain, -1, 32); +} + +// Initialization of the (statically allocated) main theap, and the main tld and subproc. +static void mi_theap_main_init(void) { + if mi_unlikely(theap_main.memid.memkind != MI_MEM_STATIC) { + // theap + theap_main.memid = _mi_memid_create(MI_MEM_STATIC); + #if defined(__APPLE__) || defined(_WIN32) && !defined(MI_SHARED_LIB) + _mi_random_init_weak(&theap_main.random); // prevent allocation failure during bcrypt dll initialization with static linking (issue #1185) + #else + _mi_random_init(&theap_main.random); + #endif + theap_main.cookie = _mi_theap_random_next(&theap_main); + _mi_theap_options_init(&theap_main); + _mi_theap_guarded_init(&theap_main); } - return td; } -static void mi_thread_data_free( mi_thread_data_t* tdfree ) { - // try to add the thread metadata to the cache - for (int i = 0; i < TD_CACHE_SIZE; i++) { - mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]); - if (td == NULL) { - mi_thread_data_t* expected = NULL; - if (mi_atomic_cas_ptr_weak_acq_rel(mi_thread_data_t, &td_cache[i], &expected, tdfree)) { - return; - } +// Initialize main heap +static void mi_heap_main_init(void) { + if mi_unlikely(heap_main.subproc == NULL) { + heap_main.subproc = &subproc_main; + heap_main.theaps = &theap_main; + + mi_theap_main_init(); + mi_subproc_main_init(); + mi_tld_main_init(); + + mi_lock_init(&heap_main.theaps_lock); + mi_lock_init(&heap_main.os_abandoned_pages_lock); + mi_lock_init(&heap_main.arena_pages_lock); + } +} + + +/* ----------------------------------------------------------- + Thread local data +----------------------------------------------------------- */ + +// Allocate fresh tld +static mi_tld_t* mi_tld_alloc(void) { + if (_mi_is_main_thread()) { + mi_atomic_increment_relaxed(&tld_main.subproc->thread_count); + return &tld_main; + } + else { + // allocate tld meta-data + // note: we need to be careful to not access the tld from `_mi_meta_zalloc` + // (and in turn from `_mi_arena_alloc_aligned` and `_mi_os_alloc_aligned`). + mi_memid_t memid; + mi_tld_t* tld = (mi_tld_t*)_mi_meta_zalloc(sizeof(mi_tld_t), &memid); + if (tld==NULL) { + _mi_error_message(ENOMEM, "unable to allocate memory for thread local data\n"); + return NULL; } + tld->memid = memid; + tld->theaps = NULL; + mi_lock_init(&tld->theaps_lock); + tld->subproc = &subproc_main; + tld->numa_node = _mi_os_numa_node(); + tld->thread_id = _mi_prim_thread_id(); + tld->thread_seq = mi_atomic_increment_relaxed(&tld->subproc->thread_total_count); + tld->is_in_threadpool = _mi_prim_thread_is_in_threadpool(); + mi_atomic_increment_relaxed(&tld->subproc->thread_count); + return tld; } - // if that fails, just free it directly - _mi_os_free(tdfree, sizeof(mi_thread_data_t), tdfree->memid, &_mi_stats_main); } -void _mi_thread_data_collect(void) { - // free all thread metadata from the cache - for (int i = 0; i < TD_CACHE_SIZE; i++) { - mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]); - if (td != NULL) { - td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL); - if (td != NULL) { - _mi_os_free(td, sizeof(mi_thread_data_t), td->memid, &_mi_stats_main); - } +#define MI_TLD_INVALID ((mi_tld_t*)1) + +mi_decl_noinline static void mi_tld_free(mi_tld_t* tld) { + if (tld==NULL || tld==MI_TLD_INVALID) return; + mi_atomic_decrement_relaxed(&tld->subproc->thread_count); + tld->thread_id = MI_THREADID_INVALID; // note: not 0 as that would re-initialize tld_main + // we also need to set an invalid tid for tld_main as sometimes the same thread-id + // is reused by the OS after a thread has terminated. (see issue #1287) + mi_lock_done(&tld->theaps_lock); + _mi_meta_free(tld, sizeof(mi_tld_t), tld->memid); // note: safe for static tld_main +} + +// return the thread local heap ensuring it is initialized (and not `NULL` or `&_mi_theap_empty`); +mi_theap_t* _mi_theap_default_safe(void) { + mi_theap_t* theap = _mi_theap_default(); + if mi_likely(mi_theap_is_initialized(theap)) return theap; + mi_thread_init(); + mi_assert_internal(mi_theap_is_initialized(_mi_theap_default())); + return _mi_theap_default(); +} + +// return the main theap ensuring it is initialized. +mi_theap_t* _mi_theap_main_safe(void) { + mi_theap_t* theap = __mi_theap_main; + if mi_unlikely(theap==NULL) { // if thread_init or default_set was never called + mi_thread_init(); // sets the default slot to the main theap + theap = _mi_theap_default(); + mi_assert_internal(theap!=NULL); + mi_assert_internal(_mi_is_theap_main(theap)); + if (_mi_is_theap_main(theap)) { + __mi_theap_main = theap; } } + mi_assert_internal(theap!=NULL && _mi_is_theap_main(theap)); + return theap; } -// Initialize the thread local default heap, called from `mi_thread_init` -static bool _mi_thread_heap_init(void) { - if (mi_heap_is_initialized(mi_prim_get_default_heap())) return true; - if (_mi_is_main_thread()) { - // mi_assert_internal(_mi_heap_main.thread_id != 0); // can happen on freeBSD where alloc is called before any initialization - // the main heap is statically allocated - mi_heap_main_init(); - _mi_heap_set_default_direct(&_mi_heap_main); - //mi_assert_internal(_mi_heap_default->tld->heap_backing == mi_prim_get_default_heap()); + +mi_subproc_t* _mi_subproc_main(void) { + return &subproc_main; +} + +mi_subproc_t* _mi_subproc(void) { + // should work without doing initialization (as it may be called from `_mi_tld -> mi_tld_alloc ... -> os_alloc -> _mi_subproc()` + // todo: this will still fail on OS systems where the first access to a thread-local causes allocation. + // on such systems we can check for this with the _mi_prim_get_default_theap as those are protected (by being + // stored in a TLS slot for example) + mi_theap_t* theap = _mi_theap_default(); + if (theap == NULL || theap->tld == NULL) { // see issue #1289 + return _mi_subproc_main(); } else { - // use `_mi_os_alloc` to allocate directly from the OS - mi_thread_data_t* td = mi_thread_data_zalloc(); - if (td == NULL) return false; + return theap->tld->subproc; // avoid using thread local storage (`thread_tld`) + } +} - mi_tld_t* tld = &td->tld; - mi_heap_t* heap = &td->heap; - _mi_tld_init(tld, heap); // must be before `_mi_heap_init` - _mi_heap_init(heap, tld, _mi_arena_id_none(), false /* can reclaim */, 0 /* default tag */); - _mi_heap_set_default_direct(heap); +mi_heap_t* _mi_subproc_heap_main(mi_subproc_t* subproc) { + mi_heap_t* heap = mi_atomic_load_ptr_acquire(mi_heap_t,&subproc->heap_main); + if mi_likely(heap!=NULL) { + return heap; + } + else { + mi_heap_main_init(); + mi_assert_internal(mi_atomic_load_ptr_acquire(mi_heap_t,&subproc->heap_main) != NULL); + return mi_atomic_load_ptr_acquire(mi_heap_t,&subproc->heap_main); } - return false; } -// initialize thread local data -void _mi_tld_init(mi_tld_t* tld, mi_heap_t* bheap) { - _mi_memcpy_aligned(tld, &tld_empty, sizeof(mi_tld_t)); - tld->heap_backing = bheap; - tld->heaps = NULL; - tld->segments.stats = &tld->stats; - tld->segments.os = &tld->os; - tld->os.stats = &tld->stats; +mi_heap_t* mi_heap_main(void) { + return _mi_subproc_heap_main(_mi_subproc()); // don't use mi_theap_main_init_get() so this call works during process_init } -// Free the thread local default heap (called from `mi_thread_done`) -static bool _mi_thread_heap_done(mi_heap_t* heap) { - if (!mi_heap_is_initialized(heap)) return true; +bool _mi_is_heap_main(const mi_heap_t* heap) { + mi_assert_internal(heap!=NULL); + return (_mi_subproc_heap_main(heap->subproc) == heap); +} - // reset default heap - _mi_heap_set_default_direct(_mi_is_main_thread() ? &_mi_heap_main : (mi_heap_t*)&_mi_heap_empty); +bool _mi_is_theap_main(const mi_theap_t* theap) { + return (mi_theap_is_initialized(theap) && _mi_is_heap_main(_mi_theap_heap(theap))); +} - // switch to backing heap - heap = heap->tld->heap_backing; - if (!mi_heap_is_initialized(heap)) return false; +/* ----------------------------------------------------------- + Sub process +----------------------------------------------------------- */ + + +mi_subproc_t* _mi_subproc_from_id(mi_subproc_id_t subproc_id) { + return (mi_subproc_t*)(subproc_id._mi_subproc_id); +} - // delete all non-backing heaps in this thread - mi_heap_t* curr = heap->tld->heaps; - while (curr != NULL) { - mi_heap_t* next = curr->next; // save `next` as `curr` will be freed - if (curr != heap) { - mi_assert_internal(!mi_heap_is_backing(curr)); - mi_heap_delete(curr); +mi_subproc_id_t _mi_subproc_to_id(mi_subproc_t* subproc) { + mi_subproc_id_t id = { subproc }; + return id; +} + +mi_subproc_id_t mi_subproc_main(void) { + return _mi_subproc_to_id(_mi_subproc_main()); +} + +mi_subproc_id_t mi_subproc_current(void) { + return _mi_subproc_to_id(_mi_subproc()); +} + +mi_subproc_id_t mi_subproc_new(void) { + static _Atomic(size_t) subproc_total_count; + mi_memid_t memid; + mi_subproc_t* subproc = (mi_subproc_t*)_mi_meta_zalloc(sizeof(mi_subproc_t),&memid); + if (subproc == NULL) return _mi_subproc_to_id(NULL); + subproc->memid = memid; + subproc->subproc_seq = mi_atomic_increment_relaxed(&subproc_total_count) + 1; + mi_stats_header_init(&subproc->stats); + mi_lock_init(&subproc->arena_reserve_lock); + mi_lock_init(&subproc->heaps_lock); + mi_lock(&subprocs_lock) { + // push on subproc list + subproc->next = subprocs; + if (subprocs!=NULL) { subprocs->prev = subproc; } + subprocs = subproc; + } + return _mi_subproc_to_id(subproc); +} + +// destroy all subproc resources including arena's, heap's etc. +static void mi_subproc_unsafe_destroy(mi_subproc_t* subproc, bool acquire_subprocs_lock) +{ + if (subproc==NULL) return; + + // remove from the subproc list + mi_lock_maybe(&subprocs_lock, acquire_subprocs_lock) { + if (subproc->next!=NULL) { subproc->next->prev = subproc->prev; } + if (subproc->prev!=NULL) { subproc->prev->next = subproc->next; } + else { mi_assert_internal(subprocs==subproc); subprocs = subproc->next; } + } + + // destroy all subproc heaps + mi_lock(&subproc->heaps_lock) { + mi_heap_t* heap = subproc->heaps; + while (heap != NULL) { + mi_heap_t* next = heap->next; + if (heap!=subproc->heap_main) { mi_heap_destroy(heap); } + heap = next; } - curr = next; + mi_assert_internal(subproc->heaps == subproc->heap_main); + _mi_heap_force_destroy(subproc->heap_main); // no warning if destroying the main heap } - mi_assert_internal(heap->tld->heaps == heap && heap->next == NULL); - mi_assert_internal(mi_heap_is_backing(heap)); - // collect if not the main thread - if (heap != &_mi_heap_main) { - _mi_heap_collect_abandon(heap); + // remove associated arenas + _mi_arenas_unsafe_destroy_all(subproc); + + // merge stats back into the main subproc? + if (subproc!=&subproc_main) { + _mi_stats_merge_into(&subproc_main.stats, &subproc->stats); } - // merge stats - _mi_stats_done(&heap->tld->stats); + // safe to release + // todo: should we refcount subprocesses? + mi_lock_done(&subproc->arena_reserve_lock); + mi_lock_done(&subproc->heaps_lock); + if (subproc!=&subproc_main) { + _mi_meta_free(subproc, sizeof(mi_subproc_t), subproc->memid); + } + else { + // for the main subproc, also release the global page map + _mi_page_map_unsafe_destroy(&subproc_main); + } +} - // free if not the main thread - if (heap != &_mi_heap_main) { - // the following assertion does not always hold for huge segments as those are always treated - // as abondened: one may allocate it in one thread, but deallocate in another in which case - // the count can be too large or negative. todo: perhaps not count huge segments? see issue #363 - // mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id()); - mi_thread_data_free((mi_thread_data_t*)heap); +void mi_subproc_destroy(mi_subproc_id_t subproc_id) { + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc==NULL || subproc==&subproc_main) return; + mi_subproc_unsafe_destroy(subproc, true /* take lock */); +} + +static void mi_subprocs_unsafe_destroy_all(void) { + mi_lock(&subprocs_lock) { + mi_subproc_t* subproc = subprocs; + while (subproc!=NULL) { + mi_subproc_t* next = subproc->next; + if (subproc!=&subproc_main) { + mi_subproc_unsafe_destroy(subproc, false /* take subprocs lock */); + } + subproc = next; + } + } + mi_subproc_unsafe_destroy(&subproc_main, true /* take subprocs lock */); +} + + +void mi_subproc_add_current_thread(mi_subproc_id_t subproc_id) { + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + mi_tld_t* const tld = _mi_theap_default_safe()->tld; + mi_assert(tld->subproc== &subproc_main); + if (tld->subproc != &subproc_main) { + _mi_warning_message("unable to add thread to the subprocess as it was already in another subprocess (id: %p)\n", subproc); + return; + } + tld->subproc = subproc; + tld->thread_seq = mi_atomic_increment_relaxed(&subproc->thread_total_count); + mi_atomic_decrement_relaxed(&subproc_main.thread_count); + mi_atomic_increment_relaxed(&subproc->thread_count); +} + + +bool mi_subproc_visit_heaps(mi_subproc_id_t subproc_id, mi_heap_visit_fun* visitor, void* arg) { + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc==NULL) return false; + bool ok = true; + mi_lock(&subproc->heaps_lock) { + for (mi_heap_t* heap = subproc->heaps; heap!=NULL && ok; heap = heap->next) { + ok = (*visitor)(heap, arg); + } + } + return ok; +} + + +/* ----------------------------------------------------------- + Allocate theap data +----------------------------------------------------------- */ + +// Initialize the thread local default theap, called from `mi_thread_init` +static mi_theap_t* _mi_thread_init_theap_default(void) { + mi_theap_t* theap = _mi_theap_default(); + if (mi_theap_is_initialized(theap)) return theap; + if (_mi_is_main_thread()) { + mi_heap_main_init(); + theap = &theap_main; } else { - #if 0 - // never free the main thread even in debug mode; if a dll is linked statically with mimalloc, - // there may still be delete/free calls after the mi_fls_done is called. Issue #207 - _mi_heap_destroy_pages(heap); - mi_assert_internal(heap->tld->heap_backing == &_mi_heap_main); - #endif + // allocates tld data + // note: we cannot access thread-locals yet as that can cause (recursive) allocation + // (on macOS <= 14 for example where the loader allocates thread-local data on demand). + mi_tld_t* tld = mi_tld_alloc(); + if (tld==NULL) return NULL; // things are very wrong if this fails (out of memory) + // allocate and initialize the theap for the main heap + theap = _mi_theap_create(mi_heap_main(), tld); } - return false; + // associate the theap with this thread + // (this is safe, on macOS for example, the theap is set in a dedicated TLS slot and thus does not cause recursive allocation) + _mi_theap_default_set(theap); + return theap; +} + + +// Free the thread local theaps +static void mi_thread_theaps_done(mi_tld_t* tld) +{ + // reset the thread local theaps + _mi_theap_default_set((mi_theap_t*)&_mi_theap_empty); + _mi_theap_cached_set((mi_theap_t*)&_mi_theap_empty); + __mi_theap_main = NULL; + + // abandon the pages of all theaps in this thread + mi_lock(&tld->theaps_lock) { + mi_theap_t* theap = tld->theaps; + while (theap != NULL) { + mi_theap_t* next = theap->tnext; + // never destroy theaps; if a dll is linked statically with mimalloc, + // there may still be delete/free calls after the mi_fls_done is called. Issue #207 + _mi_theap_collect_abandon(theap); + mi_assert_internal(theap->page_count==0); + theap = next; + } + } + + // free the theaps of this thread. + // This can run concurrently with a `mi_heap_free_theaps` and we need to ensure we free theaps atomically. + // We do this in a loop where we release the theaps_lock at every potential re-iteration to unblock + // potential concurrent `mi_heap_free_theaps` which tries to remove the theap from our theaps list. + bool all_freed; + do { + all_freed = true; + mi_lock(&tld->theaps_lock) { + mi_theap_t* theap = tld->theaps; + while (theap != NULL) { + mi_theap_t* next = theap->tnext; + mi_assert_internal(theap->page_count==0); + if (!_mi_theap_free(theap, true /* acquire heap->theaps_lock */, false /* dont re-acquire the tld->theaps_lock*/ )) { + all_freed = false; + } + theap = next; + } + } + if (!all_freed) { + mi_subproc_stat_counter_increase(tld->subproc,heaps_delete_wait,1); + _mi_prim_thread_yield(); + } + else { + mi_assert_internal(tld->theaps==NULL); + } + } while (!all_freed); + + mi_assert(_mi_theap_default()==(mi_theap_t*)&_mi_theap_empty); // careful to not re-initialize the default theap during theap_delete + mi_assert(!mi_theap_is_initialized(_mi_theap_default())); } @@ -383,7 +687,7 @@ static bool _mi_thread_heap_done(mi_heap_t* heap) { // 1. windows dynamic library: // call from DllMain on DLL_THREAD_DETACH // 2. windows static library: -// use `FlsAlloc` to call a destructor when the thread is done +// use special linker section to call a destructor when the thread is done // 3. unix, pthreads: // use a pthread key to call a destructor when a pthread is done // @@ -393,217 +697,431 @@ static bool _mi_thread_heap_done(mi_heap_t* heap) { // Set up handlers so `mi_thread_done` is called automatically static void mi_process_setup_auto_thread_done(void) { - static bool tls_initialized = false; // fine if it races - if (tls_initialized) return; - tls_initialized = true; - _mi_prim_thread_init_auto_done(); - _mi_heap_set_default_direct(&_mi_heap_main); + mi_atomic_do_once { + _mi_prim_thread_init_auto_done(); + _mi_theap_default_set(&theap_main); + } } bool _mi_is_main_thread(void) { - return (_mi_heap_main.thread_id==0 || _mi_heap_main.thread_id == _mi_thread_id()); + return (tld_main.thread_id==0 || tld_main.thread_id == _mi_thread_id()); } -static _Atomic(size_t) thread_count = MI_ATOMIC_VAR_INIT(1); - -size_t _mi_current_thread_count(void) { - return mi_atomic_load_relaxed(&thread_count); -} -// This is called from the `mi_malloc_generic` +// Initialize thread void mi_thread_init(void) mi_attr_noexcept { // ensure our process has started already mi_process_init(); + // if the theap_default is already set we have already initialized + if (_mi_thread_is_initialized()) return; - // initialize the thread local default heap - // (this will call `_mi_heap_set_default_direct` and thus set the - // fiber/pthread key to a non-zero value, ensuring `_mi_thread_done` is called) - if (_mi_thread_heap_init()) return; // returns true if already initialized + // initialize the default theap + if (_mi_thread_init_theap_default() == NULL) return; // out-of-memory on tld/theap allocation - _mi_stat_increase(&_mi_stats_main.threads, 1); - mi_atomic_increment_relaxed(&thread_count); - //_mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id()); + mi_heap_stat_increase(mi_heap_main(), threads, 1); + // _mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id()); } void mi_thread_done(void) mi_attr_noexcept { _mi_thread_done(NULL); } -void _mi_thread_done(mi_heap_t* heap) +void _mi_thread_done(mi_theap_t* _theap_main) { - // calling with NULL implies using the default heap - if (heap == NULL) { - heap = mi_prim_get_default_heap(); - if (heap == NULL) return; + // NULL can be passed on some platforms + if (_theap_main==NULL) { + _theap_main = __mi_theap_main; // don't call `mi_theap_main_safe` as that re-initializes the thread + if (_theap_main==NULL) { // can happen if `mi_theap_main_safe` is never called; but then the default is main + _theap_main = _mi_theap_default(); + mi_assert_internal(_theap_main==NULL || _mi_is_theap_main(_theap_main)); + } } - // prevent re-entrancy through heap_done/heap_set_default_direct (issue #699) - if (!mi_heap_is_initialized(heap)) { + // prevent re-entrancy through theap_done/theap_set_default_direct (issue #699) + if (!mi_theap_is_initialized(_theap_main)) { return; } + // release dynamic thread_local's + _mi_thread_locals_thread_done(); + + // note: we store the tld as we should avoid reading `thread_tld` at this point (to avoid reinitializing the thread local storage) + mi_tld_t* const tld = _theap_main->tld; + // adjust stats - mi_atomic_decrement_relaxed(&thread_count); - _mi_stat_decrease(&_mi_stats_main.threads, 1); - - // check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local heaps... - if (heap->thread_id != _mi_thread_id()) return; - - // abandon the thread local heap - if (_mi_thread_heap_done(heap)) return; // returns true if already ran -} - -void _mi_heap_set_default_direct(mi_heap_t* heap) { - mi_assert_internal(heap != NULL); - #if defined(MI_TLS_SLOT) - mi_prim_tls_slot_set(MI_TLS_SLOT,heap); - #elif defined(MI_TLS_PTHREAD_SLOT_OFS) - *mi_prim_tls_pthread_heap_slot() = heap; - #elif defined(MI_TLS_PTHREAD) - // we use _mi_heap_default_key - #else - _mi_heap_default = heap; - #endif + mi_heap_stat_decrease(_mi_subproc_heap_main(tld->subproc), threads, 1); // todo: or `_theap_main->heap`? + + // check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local theaps... + if (tld->thread_id != _mi_prim_thread_id()) return; - // ensure the default heap is passed to `_mi_thread_done` - // setting to a non-NULL value also ensures `mi_thread_done` is called. - _mi_prim_thread_associate_default_heap(heap); + // delete the thread local theaps + mi_thread_theaps_done(tld); + + // free thread local data + mi_tld_free(tld); } -// -------------------------------------------------------- -// Run functions on process init/done, and thread init/done -// -------------------------------------------------------- -static void mi_cdecl mi_process_done(void); +mi_decl_cold mi_decl_noinline mi_theap_t* _mi_theap_empty_get(void) { + return (mi_theap_t*)&_mi_theap_empty; +} -static bool os_preloading = true; // true until this module is initialized -static bool mi_redirected = false; // true if malloc redirects to mi_malloc +#if MI_TLS_MODEL_DYNAMIC_WIN32 -// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false. -bool mi_decl_noinline _mi_preloading(void) { - return os_preloading; +// If we can, we use one of the 64 direct TLS slots (but fall back to expansion slots if needed) +// See for the offsets. +#if MI_SIZE_SIZE==4 +#define MI_TLS_DIRECT_FIRST (0x0E10 / MI_SIZE_SIZE) +#else +#define MI_TLS_DIRECT_FIRST (0x1480 / MI_SIZE_SIZE) +#endif +#define MI_TLS_DIRECT_SLOTS (64) +#define MI_TLS_EXPANSION_SLOTS (1024) + +#if !MI_WIN_DIRECT_TLS +// we initially use the last of the expansion slots as the default NULL. +// note: this will fail if the program allocates exactly 1024+64 slots with TlsAlloc :-( (but this is quite unlikely) +#define MI_TLS_INITIAL_SLOT MI_TLS_EXPANSION_SLOT +#define MI_TLS_INITIAL_EXPANSION_SLOT (MI_TLS_EXPANSION_SLOTS-1) +#else +// with only direct entries, use the "arbitrary user data" field +// and assume it is NULL (see also ) +#define MI_TLS_INITIAL_SLOT (5) +#define MI_TLS_INITIAL_EXPANSION_SLOT (0) +#endif + +mi_decl_hidden mi_decl_cache_align size_t _mi_theap_default_slot = MI_TLS_INITIAL_SLOT; +mi_decl_hidden size_t _mi_theap_default_expansion_slot = MI_TLS_INITIAL_EXPANSION_SLOT; +mi_decl_hidden size_t _mi_theap_cached_slot = MI_TLS_INITIAL_SLOT; +mi_decl_hidden size_t _mi_theap_cached_expansion_slot = MI_TLS_INITIAL_EXPANSION_SLOT; + +static DWORD mi_tls_raw_index_default = TLS_OUT_OF_INDEXES; +static DWORD mi_tls_raw_index_cached = TLS_OUT_OF_INDEXES; + +static bool mi_win_tls_slot_alloc(size_t* slot, size_t* extended, DWORD* raw_index) { + const DWORD index = TlsAlloc(); + *raw_index = index; + if (index==TLS_OUT_OF_INDEXES) { + *extended = 0; + *slot = 0; + return false; + } + else if (index= MI_TLS_DIRECT_FIRST && slot < MI_TLS_DIRECT_FIRST + MI_TLS_DIRECT_SLOTS) || slot == MI_TLS_EXPANSION_SLOT); + if (slot < MI_TLS_DIRECT_FIRST + MI_TLS_DIRECT_SLOTS) { + mi_prim_tls_slot_set(slot, value); } - else if (reason == DLL_THREAD_DETACH) { - mi_thread_done(); + else { + mi_assert_internal(extended_slot < MI_TLS_EXPANSION_SLOTS); + TlsSetValue((DWORD)(extended_slot + MI_TLS_DIRECT_SLOTS), value); // use TlsSetValue to initialize the TlsExpansion array if needed } } -__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message); -__declspec(dllimport) void mi_cdecl mi_allocator_done(void); -#ifdef __cplusplus + +#elif MI_TLS_MODEL_DYNAMIC_PTHREADS + +// only for pthreads for now +mi_decl_hidden pthread_key_t _mi_theap_default_key = 0; +mi_decl_hidden pthread_key_t _mi_theap_cached_key = 0; + +// create a non-zero pthread key +static int mi_pthread_key_create( pthread_key_t* pkey ) { + pthread_key_t key; + int err = pthread_key_create(&key, NULL); + if (err!=0) return err; + if (key==0) { + // if we get a zero key, create another one as we use 0 for an invalid key + pthread_key_t key2; + err = pthread_key_create(&key2, NULL); + pthread_key_delete(key); // delete the old key + if (err!=0) return err; + key = key2; + } + mi_assert_internal(key!=0); + *pkey = key; + return 0; } -#endif + +static void mi_tls_slots_init(void) { + mi_atomic_do_once { + int err = mi_pthread_key_create(&_mi_theap_default_key); + if (err==0) { + err = mi_pthread_key_create(&_mi_theap_cached_key); + } + if (err!=0) { + _mi_error_message(EFAULT, "unable to allocate pthread keys (error %d)\n", err); + } + } +} + +static void mi_tls_slots_done(void) { + if (_mi_theap_default_key != 0) { + pthread_key_delete(_mi_theap_default_key); + _mi_theap_default_key = 0; + } + if (_mi_theap_cached_key != 0) { + pthread_key_delete(_mi_theap_cached_key); + _mi_theap_cached_key = 0; + } +} + #else -static bool mi_allocator_init(const char** message) { - if (message != NULL) *message = NULL; - return true; + +static void mi_tls_slots_init(void) { + // nothing } -static void mi_allocator_done(void) { - // nothing to do + +static void mi_tls_slots_done(void) { + // nothing } + #endif -// Called once by the process loader -static void mi_process_load(void) { - mi_heap_main_init(); - #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) - volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true; - if (dummy == NULL) return; // use dummy or otherwise the access may get optimized away (issue #697) +void _mi_theap_cached_set(mi_theap_t* theap) { + mi_theap_t* prev = _mi_theap_cached(); + if (prev==theap) return; + // set + mi_tls_slots_init(); + #if MI_TLS_MODEL_THREAD_LOCAL + __mi_theap_cached = theap; + #elif MI_TLS_MODEL_FIXED_SLOT + mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_SLOT_CACHED, theap); + #elif MI_TLS_MODEL_DYNAMIC_WIN32 + mi_win_tls_slot_set(_mi_theap_cached_slot, _mi_theap_cached_expansion_slot, theap); + #elif MI_TLS_MODEL_DYNAMIC_PTHREADS + if (_mi_theap_cached_key!=0) pthread_setspecific(_mi_theap_cached_key, theap); + #endif + // update refcounts (so cached theap memory keeps available until no longer cached) + _mi_theap_incref(theap); + _mi_theap_decref(prev); +} + +void _mi_theap_default_set(mi_theap_t* theap) { + mi_theap_t* const theap_old = _mi_theap_default(); + mi_assert_internal(theap != NULL); + mi_assert_internal(theap->tld != NULL); + mi_assert_internal(theap->tld->thread_id==0 || theap->tld->thread_id==_mi_thread_id()); + mi_tls_slots_init(); + #if MI_TLS_MODEL_THREAD_LOCAL + __mi_theap_default = theap; + #elif MI_TLS_MODEL_FIXED_SLOT + mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_SLOT_DEFAULT, theap); + #elif MI_TLS_MODEL_DYNAMIC_WIN32 + mi_win_tls_slot_set(_mi_theap_default_slot, _mi_theap_default_expansion_slot, theap); + #elif MI_TLS_MODEL_DYNAMIC_PTHREADS + if (_mi_theap_default_key!=0) pthread_setspecific(_mi_theap_default_key, theap); #endif + + // set theap main if needed + if (mi_theap_is_initialized(theap)) { + // ensure the default theap is passed to `_mi_thread_done` as on some platforms we cannot access TLS at thread termination (as it would allocate again) + _mi_prim_thread_associate_default_theap(theap); + if (_mi_is_heap_main(_mi_theap_heap(theap))) { + __mi_theap_main = theap; + } + } + + // ensure either the default slot contains the main theap, or __mi_theap_main is initialized + if (mi_theap_is_initialized(theap_old) && _mi_is_heap_main(_mi_theap_heap(theap_old))) { + __mi_theap_main = theap_old; + } +} + +void mi_thread_set_in_threadpool(void) mi_attr_noexcept { + mi_theap_t* theap = _mi_theap_default_safe(); + theap->tld->is_in_threadpool = true; +} + +// -------------------------------------------------------- +// Run functions on process init/done, and thread init/done +// -------------------------------------------------------- +static bool os_preloading = true; // true until this module is initialized + +// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false. +bool mi_decl_noinline _mi_preloading(void) { + return os_preloading; +} + +// Returns true if mimalloc was redirected +mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept { + return _mi_is_redirected(); +} + +// Called once by the process loader from `src/prim/prim.c` +void _mi_auto_process_init(void) { + // mi_heap_main_init(); + // #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) + // volatile mi_theap_t* dummy = __mi_theap_default; // access TLS to allocate it before setting tls_initialized to true; + // if (dummy == NULL) return; // use dummy or otherwise the access may get optimized away (issue #697) + // #endif + os_preloading = false; mi_assert_internal(_mi_is_main_thread()); - #if !(defined(_WIN32) && defined(MI_SHARED_LIB)) // use Dll process detach (see below) instead of atexit (issue #521) - atexit(&mi_process_done); - #endif - _mi_options_init(); - mi_process_setup_auto_thread_done(); + mi_process_init(); - if (mi_redirected) _mi_verbose_message("malloc is redirected.\n"); + mi_process_setup_auto_thread_done(); + _mi_thread_locals_init(); + _mi_options_post_init(); // now we can print to stderr + if (_mi_is_redirected()) _mi_verbose_message("malloc is redirected.\n"); // show message from the redirector (if present) const char* msg = NULL; - mi_allocator_init(&msg); + _mi_allocator_init(&msg); if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) { _mi_fputs(NULL,NULL,NULL,msg); } // reseed random - _mi_random_reinit_if_weak(&_mi_heap_main.random); + _mi_random_reinit_if_weak(&theap_main.random); } -#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) -#include -mi_decl_cache_align bool _mi_cpu_has_fsrm = false; +// CPU features +mi_decl_cache_align size_t _mi_cpu_movsb_max = 0; // for size <= max, rep movsb is fast +mi_decl_cache_align size_t _mi_cpu_stosb_max = 0; // for size <= max, rep stosb is fast +mi_decl_cache_align bool _mi_cpu_has_popcnt = false; + +#if (MI_ARCH_X64 || MI_ARCH_X86) +#if defined(__GNUC__) +// #include +static bool mi_cpuid(uint32_t* regs4, uint32_t level, uint32_t sublevel) { + // note: use explicit assembly instead of __get_cpuid as we need the sublevel (in ecx) + // (on Ubuntu 22 with WSL the __get_cpuid does not clear ecx for level 7 which is incorrect). + uint32_t eax, ebx, ecx, edx; + __asm __volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(level), "c"(sublevel) : ); + regs4[0] = eax; + regs4[1] = ebx; + regs4[2] = ecx; + regs4[3] = edx; + return true; +} + +#elif defined(_MSC_VER) +static bool mi_cpuid(uint32_t* regs4, uint32_t level, uint32_t sublevel) { + __cpuidex((int32_t*)regs4, (int32_t)level, (int32_t)sublevel); + return true; +} +#else +static bool mi_cpuid(uint32_t* regs4, uint32_t level, uint32_t sublevel) { + MI_UNUSED(regs4); MI_UNUSED(level); MI_UNUSED(sublevel); + return false; +} +#endif static void mi_detect_cpu_features(void) { - // FSRM for fast rep movsb support (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017)) - int32_t cpu_info[4]; - __cpuid(cpu_info, 7); - _mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see + // FSRM for fast short rep movsb support (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017)) + // EMRS for fast enhanced rep movsb/stosb support (not used at the moment, memcpy always seems faster?) + // FSRS for fast short rep stosb + bool amd = false; + bool fsrm = false; + // bool erms = false; + bool fsrs = false; + uint32_t cpu_info[4]; + if (mi_cpuid(cpu_info, 0, 0)) { + amd = (cpu_info[2]==0x444d4163); // (Auth enti cAMD) + } + if (mi_cpuid(cpu_info, 7, 0)) { + fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see + // erms = ((cpu_info[1] & (1 << 9)) != 0); // bit 9 of EBX : see + } + if (mi_cpuid(cpu_info, 7, 1)) { + fsrs = ((cpu_info[1] & (1 << 11)) != 0); // bit 11 of EBX: see + } + if (mi_cpuid(cpu_info, 1, 0)) { + _mi_cpu_has_popcnt = ((cpu_info[2] & (1 << 23)) != 0); // bit 23 of ECX : see + } + + if (fsrm) { + _mi_cpu_movsb_max = 127; + } + if (fsrs || (amd && fsrm)) { // fsrm on amd implies fsrs, see: https://marc.info/?l=git-commits-head&m=168186277717803 + _mi_cpu_stosb_max = 127; + } } + #else static void mi_detect_cpu_features(void) { - // nothing + #if MI_ARCH_ARM64 + _mi_cpu_has_popcnt = true; + #endif } #endif + // Initialize the process; called by thread_init or the process loader -void mi_process_init(void) mi_attr_noexcept { - // ensure we are called once - static mi_atomic_once_t process_init; - #if _MSC_VER < 1920 - mi_heap_main_init(); // vs2017 can dynamically re-initialize _mi_heap_main - #endif - if (!mi_atomic_once(&process_init)) return; - _mi_process_is_initialized = true; +static void mi_process_init_once(void) mi_attr_noexcept { _mi_verbose_message("process init: 0x%zx\n", _mi_thread_id()); - mi_process_setup_auto_thread_done(); mi_detect_cpu_features(); + _mi_options_init(); + _mi_stats_init(); _mi_os_init(); - mi_heap_main_init(); - #if MI_DEBUG - _mi_verbose_message("debug level : %d\n", MI_DEBUG); - #endif - _mi_verbose_message("secure level: %d\n", MI_SECURE); - _mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL); - #if MI_TSAN - _mi_verbose_message("thread santizer enabled\n"); - #endif + // the following can potentially allocate (on freeBSD for pthread keys) + // todo: do 2-phase so we can use stats at first, then later init the keys? + mi_heap_main_init(); // before page_map_init so stats are working + _mi_page_map_init(); // todo: this could fail.. should we abort in that case? mi_thread_init(); - - #if defined(_WIN32) + _mi_process_is_initialized = true; + + #if defined(_WIN32) && defined(MI_WIN_USE_FLS) // On windows, when building as a static lib the FLS cleanup happens to early for the main thread. // To avoid this, set the FLS value for the main thread to NULL so the fls cleanup // will not call _mi_thread_done on the (still executing) main thread. See issue #508. - _mi_prim_thread_associate_default_heap(NULL); + _mi_prim_thread_associate_default_theap(NULL); #endif - mi_stats_reset(); // only call stat reset *after* thread init (or the heap tld == NULL) + // mi_stats_reset(); // only call stat reset *after* thread init (or the theap tld == NULL) mi_track_init(); - if (mi_option_is_enabled(mi_option_reserve_huge_os_pages)) { size_t pages = mi_option_get_clamp(mi_option_reserve_huge_os_pages, 0, 128*1024); - long reserve_at = mi_option_get(mi_option_reserve_huge_os_pages_at); + int reserve_at = (int)mi_option_get_clamp(mi_option_reserve_huge_os_pages_at, -1, INT_MAX); if (reserve_at != -1) { mi_reserve_huge_os_pages_at(pages, reserve_at, pages*500); } else { @@ -613,13 +1131,24 @@ void mi_process_init(void) mi_attr_noexcept { if (mi_option_is_enabled(mi_option_reserve_os_memory)) { long ksize = mi_option_get(mi_option_reserve_os_memory); if (ksize > 0) { - mi_reserve_os_memory((size_t)ksize*MI_KiB, true /* commit? */, true /* allow large pages? */); + mi_reserve_os_memory((size_t)ksize*MI_KiB, true, true); } } } -// Called when the process is done (through `at_exit`) -static void mi_cdecl mi_process_done(void) { +// Initialize the process; called by thread_init or the process loader +void mi_process_init(void) mi_attr_noexcept { + // #if _MSC_VER < 1920 + // mi_heap_main_init(); // vs2017 can dynamically re-initialize _mi_heap_main + // #endif + mi_atomic_do_once { + mi_process_init_once(); + } +} + + +// Called when the process is done +static void mi_process_done_once(void) { // only shutdown if we were initialized if (!_mi_process_is_initialized) return; // ensure we are called once @@ -627,6 +1156,9 @@ static void mi_cdecl mi_process_done(void) { if (process_done) return; process_done = true; + // free dynamic thread locals (if used at all) + _mi_thread_locals_done(); + // release any thread specific resources and ensure _mi_thread_done is called on all but the main thread _mi_prim_thread_done_auto_done(); @@ -635,80 +1167,43 @@ static void mi_cdecl mi_process_done(void) { // free all memory if possible on process exit. This is not needed for a stand-alone process // but should be done if mimalloc is statically linked into another shared library which // is repeatedly loaded/unloaded, see issue #281. - mi_collect(true /* force */ ); + mi_theap_collect(_mi_theap_default(), true /* force */); #endif #endif + // done with tracking tools + mi_track_done(); + // Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free // since after process_done there might still be other code running that calls `free` (like at_exit routines, // or C-runtime termination code. if (mi_option_is_enabled(mi_option_destroy_on_exit)) { - mi_collect(true /* force */); - _mi_heap_unsafe_destroy_all(); // forcefully release all memory held by all heaps (of this thread only!) - _mi_arena_unsafe_destroy_all(& _mi_heap_main_get()->tld->stats); + mi_subprocs_unsafe_destroy_all(); // destroys all subprocs, arenas, and the page_map! } - + else { + mi_heap_stats_merge_to_subproc(mi_heap_main()); + } + + // careful now to no longer access any allocator functionality if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) { - mi_stats_print(NULL); + mi_subproc_stats_print_out(mi_subproc_main(), NULL, NULL); } - mi_allocator_done(); - _mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id); + mi_lock_done(&subprocs_lock); + mi_tls_slots_done(); + _mi_allocator_done(); + _mi_verbose_message("process done: 0x%zx\n", tld_main.thread_id); os_preloading = true; // don't call the C runtime anymore } - -#if defined(_WIN32) && defined(MI_SHARED_LIB) - // Windows DLL: easy to hook into process_init and thread_done - __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { - MI_UNUSED(reserved); - MI_UNUSED(inst); - if (reason==DLL_PROCESS_ATTACH) { - mi_process_load(); - } - else if (reason==DLL_PROCESS_DETACH) { - mi_process_done(); - } - else if (reason==DLL_THREAD_DETACH) { - if (!mi_is_redirected()) { - mi_thread_done(); - } - } - return TRUE; - } - -#elif defined(_MSC_VER) - // MSVC: use data section magic for static libraries - // See - static int _mi_process_init(void) { - mi_process_load(); - return 0; - } - typedef int(*_mi_crt_callback_t)(void); - #if defined(_M_X64) || defined(_M_ARM64) - __pragma(comment(linker, "/include:" "_mi_msvc_initu")) - #pragma section(".CRT$XIU", long, read) - #else - __pragma(comment(linker, "/include:" "__mi_msvc_initu")) - #endif - #pragma data_seg(".CRT$XIU") - mi_decl_externc _mi_crt_callback_t _mi_msvc_initu[] = { &_mi_process_init }; - #pragma data_seg() - -#elif defined(__cplusplus) - // C++: use static initialization to detect process start - static bool _mi_process_init(void) { - mi_process_load(); - return (_mi_heap_main.thread_id != 0); - } - static bool mi_initialized = _mi_process_init(); - -#elif defined(__GNUC__) || defined(__clang__) - // GCC,Clang: use the constructor attribute - static void __attribute__((constructor)) _mi_process_init(void) { - mi_process_load(); +// Called when the process is done (cdecl as it is used with `at_exit` on some platforms) +void mi_cdecl mi_process_done(void) mi_attr_noexcept { + mi_atomic_do_once { + mi_process_done_once(); } +} -#else -#pragma message("define a way to call mi_process_load on your platform") -#endif +void mi_cdecl _mi_auto_process_done(void) mi_attr_noexcept { + if (_mi_option_get_fast(mi_option_destroy_on_exit)>1) return; + mi_process_done(); +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/libc.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/libc.c index dd6b4007..5a5e586e 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/libc.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/libc.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2024, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,7 +7,7 @@ terms of the MIT license. A copy of the license can be found in the file // -------------------------------------------------------- // This module defines various std libc functions to reduce -// the dependency on libc, and also prevent errors caused +// the dependency on libc, and also prevent errors caused // by some libc implementations when called before `main` // executes (due to malloc redirection) // -------------------------------------------------------- @@ -22,6 +22,7 @@ char _mi_toupper(char c) { } int _mi_strnicmp(const char* s, const char* t, size_t n) { + mi_assert_internal(s!=NULL && t!=NULL); if (n == 0) return 0; for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) { if (_mi_toupper(*s) != _mi_toupper(*t)) break; @@ -29,6 +30,15 @@ int _mi_strnicmp(const char* s, const char* t, size_t n) { return (n == 0 ? 0 : *s - *t); } +bool _mi_streq(const char* s, const char* t) { + if (s==NULL && t==NULL) return true; + if (s==NULL || t==NULL) return false; + for (; *s != 0 && *t != 0; s++, t++) { + if (*s != *t) break; + } + return (*s == *t); +} + void _mi_strlcpy(char* dest, const char* src, size_t dest_size) { if (dest==NULL || src==NULL || dest_size == 0) return; // copy until end of src, or when dest is (almost) full @@ -51,39 +61,90 @@ void _mi_strlcat(char* dest, const char* src, size_t dest_size) { _mi_strlcpy(dest, src, dest_size); } -size_t _mi_strlen(const char* s) { +size_t _mi_strnlen(const char* s, size_t max_len) { if (s==NULL) return 0; size_t len = 0; - while(s[len] != 0) { len++; } + while(len < max_len && s[len] != 0) { len++; } return len; } -size_t _mi_strnlen(const char* s, size_t max_len) { - if (s==NULL) return 0; - size_t len = 0; - while(s[len] != 0 && len < max_len) { len++; } - return len; +size_t _mi_strlen(const char* s) { + return _mi_strnlen(s,PTRDIFF_MAX); +} + +char* _mi_strnstr(char* s, size_t max_len, const char* pat) { + if (s==NULL) return NULL; + if (pat==NULL) return s; + const size_t m = _mi_strnlen(s, max_len); + const size_t n = _mi_strlen(pat); + for (size_t start = 0; start + n <= m; start++) { + size_t i = 0; + while (i 0 ? 0 : (res == 0 ? ENOENT : EAGAIN)); } #endif + +// -------------------------------------------------------- +// Define our own primitives for doing an action once +// -------------------------------------------------------- + +// Returns `true` only on the first invocation, signifying we can execute an action once. +// If it returns `true`, the caller should call `_mi_atomic_once_release` after performing the action. +// Other threads (than the initial thread that entered) will block until `_mi_atomic_once_release` has been called. +bool _mi_atomic_once_enter(mi_atomic_once_t* once) { + const uintptr_t once_tid = mi_atomic_load_acquire(&once->tid); + if mi_likely(once_tid == 1) { + return false; // already executed + } + const mi_threadid_t current_tid = _mi_thread_id(); + if (once_tid == current_tid) { + return false; // recursive invocation; we need this for process_init for example + } + + mi_lock_acquire(&once->lock); + uintptr_t expected = 0; + if (mi_atomic_cas_strong_acq_rel(&once->tid, &expected, current_tid)) { // could use atomic_load/store as well + return true; // should execute and release + } + else { + mi_lock_release(&once->lock); + return false; // already another thread entered and released + } +} + +void _mi_atomic_once_release(mi_atomic_once_t* once) { + if (mi_atomic_load_acquire(&once->tid)>1) { // paranoia + mi_atomic_store_release(&once->tid,1); // done executing + mi_lock_release(&once->lock); + } +} + + // -------------------------------------------------------- // Define our own limited `_mi_vsnprintf` and `_mi_snprintf` // This is mostly to avoid calling these when libc is not yet // initialized (and to reduce dependencies) -// +// // format: d i, p x u, s // prec: z l ll L // width: 10 @@ -130,7 +191,7 @@ static void mi_out_alignright(char fill, char* start, size_t len, size_t extra, } -static void mi_out_num(uintptr_t x, size_t base, char prefix, char** out, char* end) +static void mi_out_num(uintmax_t x, size_t base, char prefix, char** out, char* end) { if (x == 0 || base == 0 || base > 16) { if (prefix != 0) { mi_outc(prefix, out, end); } @@ -144,8 +205,8 @@ static void mi_out_num(uintptr_t x, size_t base, char prefix, char** out, char* mi_outc((digit <= 9 ? '0' + digit : 'A' + digit - 10),out,end); x = x / base; } - if (prefix != 0) { - mi_outc(prefix, out, end); + if (prefix != 0) { + mi_outc(prefix, out, end); } size_t len = *out - start; // and reverse in-place @@ -160,8 +221,8 @@ static void mi_out_num(uintptr_t x, size_t base, char prefix, char** out, char* #define MI_NEXTC() c = *in; if (c==0) break; in++; -void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { - if (buf == NULL || bufsize == 0 || fmt == NULL) return; +int _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { + if (buf == NULL || bufsize == 0 || fmt == NULL) return 0; buf[bufsize - 1] = 0; char* const end = buf + (bufsize - 1); const char* in = fmt; @@ -171,7 +232,18 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { char c; MI_NEXTC(); if (c != '%') { - if ((c >= ' ' && c <= '~') || c=='\n' || c=='\r' || c=='\t') { // output visible ascii or standard control only + if (c == '\\') { + MI_NEXTC(); + switch (c) { + case 'e': mi_outc('\x1B', &out, end); break; + case 't': mi_outc('\t', &out, end); break; + case 'n': mi_outc('\n', &out, end); break; + case 'r': mi_outc('\r', &out, end); break; + case '\\': mi_outc('\\', &out, end); break; + default: /* ignore */ break; + } + } + else if ((c >= ' ' && c <= '~') || c=='\n' || c=='\r' || c=='\t' || c=='\x1b') { // output visible ascii or standard control only mi_outc(c, &out, end); } } @@ -181,7 +253,7 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { size_t width = 0; char numtype = 'd'; char numplus = 0; - bool alignright = true; + bool alignright = true; if (c == '+' || c == ' ') { numplus = c; MI_NEXTC(); } if (c == '-') { alignright = false; MI_NEXTC(); } if (c == '0') { fill = '0'; MI_NEXTC(); } @@ -191,7 +263,7 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { width = (10 * width) + (c - '0'); MI_NEXTC(); } if (c == 0) break; // extra check due to while - } + } if (c == 'z' || c == 't' || c == 'L') { numtype = c; MI_NEXTC(); } else if (c == 'l') { numtype = c; MI_NEXTC(); @@ -199,22 +271,27 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { } char* start = out; - if (c == 's') { + if (c == '%') { + mi_outc('%', &out, end); + } + else if (c == 's') { // string const char* s = va_arg(args, const char*); mi_outs(s, &out, end); } else if (c == 'p' || c == 'x' || c == 'u') { // unsigned - uintptr_t x = 0; + uintmax_t x = 0; if (c == 'x' || c == 'u') { if (numtype == 'z') x = va_arg(args, size_t); else if (numtype == 't') x = va_arg(args, uintptr_t); // unsigned ptrdiff_t - else if (numtype == 'L') x = (uintptr_t)va_arg(args, unsigned long long); - else x = va_arg(args, unsigned long); + else if (numtype == 'L') x = va_arg(args, unsigned long long); + else if (numtype == 'l') x = va_arg(args, unsigned long); + else x = va_arg(args, unsigned int); } else if (c == 'p') { - x = va_arg(args, uintptr_t); + void* const p = va_arg(args, void*); + x = (uintptr_t)p; mi_outs("0x", &out, end); start = out; width = (width >= 2 ? width - 2 : 0); @@ -228,20 +305,21 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { } else if (c == 'i' || c == 'd') { // signed - intptr_t x = 0; + intmax_t x = 0; if (numtype == 'z') x = va_arg(args, intptr_t ); else if (numtype == 't') x = va_arg(args, ptrdiff_t); - else if (numtype == 'L') x = (intptr_t)va_arg(args, long long); - else x = va_arg(args, long); + else if (numtype == 'L') x = va_arg(args, long long); + else if (numtype == 'l') x = va_arg(args, long); + else x = va_arg(args, int); char pre = 0; if (x < 0) { pre = '-'; - if (x > INTPTR_MIN) { x = -x; } + if (x > INTMAX_MIN) { x = -x; } } else if (numplus != 0) { pre = numplus; } - mi_out_num((uintptr_t)x, 10, pre, &out, end); + mi_out_num((uintmax_t)x, 10, pre, &out, end); } else if (c >= ' ' && c <= '~') { // unknown format @@ -263,11 +341,139 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { } mi_assert_internal(out <= end); *out = 0; + return (int)(out - buf); } -void _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...) { +int _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...) { va_list args; va_start(args, fmt); - _mi_vsnprintf(buf, buflen, fmt, args); + const int written = _mi_vsnprintf(buf, buflen, fmt, args); va_end(args); + return written; +} + + + +// -------------------------------------------------------- +// generic trailing and leading zero count, and popcount +// -------------------------------------------------------- + +#if !MI_HAS_FAST_BITSCAN + +static size_t mi_ctz_generic32(uint32_t x) { + // de Bruijn multiplication, see + static const uint8_t debruijn[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + if (x==0) return 32; + return debruijn[(uint32_t)((x & -(int32_t)x) * (uint32_t)(0x077CB531U)) >> 27]; +} + +static size_t mi_clz_generic32(uint32_t x) { + // de Bruijn multiplication, see + static const uint8_t debruijn[32] = { + 31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1, + 23, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0 + }; + if (x==0) return 32; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return debruijn[(uint32_t)(x * (uint32_t)(0x07C4ACDDU)) >> 27]; } + +size_t _mi_ctz_generic(size_t x) { + if (x==0) return MI_SIZE_BITS; + #if (MI_SIZE_BITS <= 32) + return mi_ctz_generic32((uint32_t)x); + #else + const uint32_t lo = (uint32_t)x; + if (lo != 0) { + return mi_ctz_generic32(lo); + } + else { + return (32 + mi_ctz_generic32((uint32_t)(x>>32))); + } + #endif +} + +size_t _mi_clz_generic(size_t x) { + if (x==0) return MI_SIZE_BITS; + #if (MI_SIZE_BITS <= 32) + return mi_clz_generic32((uint32_t)x); + #else + const uint32_t hi = (uint32_t)(x>>32); + if (hi != 0) { + return mi_clz_generic32(hi); + } + else { + return 32 + mi_clz_generic32((uint32_t)x); + } + #endif +} + +#endif // bit scan + + +#if MI_SIZE_SIZE == 4 +#define mi_mask_even_bits32 (0x55555555) +#define mi_mask_even_pairs32 (0x33333333) +#define mi_mask_even_nibbles32 (0x0F0F0F0F) + +// sum of all the bytes in `x` if it is guaranteed that the sum < 256! +static size_t mi_byte_sum32(uint32_t x) { + // perform `x * 0x01010101`: the highest byte contains the sum of all bytes. + x += (x << 8); + x += (x << 16); + return (size_t)(x >> 24); +} + +static size_t mi_popcount_generic32(uint32_t x) { + // first count each 2-bit group `a`, where: a==0b00 -> 00, a==0b01 -> 01, a==0b10 -> 01, a==0b11 -> 10 + // in other words, `a - (a>>1)`; to do this in parallel, we need to mask to prevent spilling a bit pair + // into the lower bit-pair: + x = x - ((x >> 1) & mi_mask_even_bits32); + // add the 2-bit pair results + x = (x & mi_mask_even_pairs32) + ((x >> 2) & mi_mask_even_pairs32); + // add the 4-bit nibble results + x = (x + (x >> 4)) & mi_mask_even_nibbles32; + // each byte now has a count of its bits, we can sum them now: + return mi_byte_sum32(x); +} + +mi_decl_noinline size_t _mi_popcount_generic(size_t x) { + if (x<=1) return x; + if (~x==0) return MI_SIZE_BITS; + return mi_popcount_generic32(x); +} + +#else +#define mi_mask_even_bits64 (0x5555555555555555) +#define mi_mask_even_pairs64 (0x3333333333333333) +#define mi_mask_even_nibbles64 (0x0F0F0F0F0F0F0F0F) + +// sum of all the bytes in `x` if it is guaranteed that the sum < 256! +static size_t mi_byte_sum64(uint64_t x) { + x += (x << 8); + x += (x << 16); + x += (x << 32); + return (size_t)(x >> 56); +} + +static size_t mi_popcount_generic64(uint64_t x) { + x = x - ((x >> 1) & mi_mask_even_bits64); + x = (x & mi_mask_even_pairs64) + ((x >> 2) & mi_mask_even_pairs64); + x = (x + (x >> 4)) & mi_mask_even_nibbles64; + return mi_byte_sum64(x); +} + +mi_decl_noinline size_t _mi_popcount_generic(size_t x) { + if (x<=1) return x; + if (~x==0) return MI_SIZE_BITS; + return mi_popcount_generic64(x); +} +#endif + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/options.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/options.c index a62727dd..2873f3c3 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/options.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/options.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -12,8 +12,6 @@ terms of the MIT license. A copy of the license can be found in the file #include // stdin/stdout #include // abort - - static long mi_max_error_count = 16; // stop outputting errors after this (use < 0 for no limit) static long mi_max_warning_count = 16; // stop outputting warnings after this (use < 0 for no limit) @@ -30,100 +28,252 @@ int mi_version(void) mi_attr_noexcept { // concurrently initialized, but an initializing data race // is ok since they resolve to the same value. // -------------------------------------------------------- -typedef enum mi_init_e { - UNINIT, // not yet initialized - DEFAULTED, // not found in the environment, use default value - INITIALIZED // found in environment or set explicitly -} mi_init_t; - -typedef struct mi_option_desc_s { - long value; // the value - mi_init_t init; // is it initialized yet? (from the environment) - mi_option_t option; // for debugging: the option index should match the option - const char* name; // option name without `mimalloc_` prefix - const char* legacy_name; // potential legacy option name -} mi_option_desc_t; + #define MI_OPTION(opt) mi_option_##opt, #opt, NULL #define MI_OPTION_LEGACY(opt,legacy) mi_option_##opt, #opt, #legacy -static mi_option_desc_t options[_mi_option_last] = +// Some options can be set at build time for statically linked libraries +// (use `-DMI_EXTRA_CPPDEFS="opt1=val1;opt2=val2"`) +// +// This is useful if we cannot pass them as environment variables +// (and setting them programmatically would be too late) + +#ifndef MI_DEFAULT_VERBOSE +#define MI_DEFAULT_VERBOSE 0 +#endif + +#ifndef MI_DEFAULT_ARENA_EAGER_COMMIT +#define MI_DEFAULT_ARENA_EAGER_COMMIT 2 +#endif + +// in KiB +#ifndef MI_DEFAULT_ARENA_RESERVE + #if (MI_INTPTR_SIZE>4) + #define MI_DEFAULT_ARENA_RESERVE 1024L*1024L + #else + #define MI_DEFAULT_ARENA_RESERVE 128L*1024L + #endif +#endif + +#ifndef MI_DEFAULT_ARENA_MAX_OBJECT_SIZE +#define MI_DEFAULT_ARENA_MAX_OBJECT_SIZE ((MI_SIZE_BITS * MI_ARENA_MAX_CHUNK_OBJ_SIZE)/MI_KiB) /* 2 GiB (or 256 MiB on 32-bit), larger than this is alloc'd by the OS */ +#endif + +#ifndef MI_DEFAULT_DISALLOW_ARENA_ALLOC +#define MI_DEFAULT_DISALLOW_ARENA_ALLOC 0 +#endif + +#ifndef MI_DEFAULT_ALLOW_LARGE_OS_PAGES +#define MI_DEFAULT_ALLOW_LARGE_OS_PAGES 0 +#endif + +#ifndef MI_DEFAULT_RESERVE_HUGE_OS_PAGES +#define MI_DEFAULT_RESERVE_HUGE_OS_PAGES 0 +#endif + +#ifndef MI_DEFAULT_RESERVE_OS_MEMORY +#define MI_DEFAULT_RESERVE_OS_MEMORY 0 +#endif + +#ifndef MI_DEFAULT_GUARDED_SAMPLE_RATE +#if MI_GUARDED && !MI_DEBUG +#define MI_DEFAULT_GUARDED_SAMPLE_RATE 4000 +#else +#define MI_DEFAULT_GUARDED_SAMPLE_RATE 0 +#endif +#endif + +#ifndef MI_DEFAULT_PAGEMAP_COMMIT +#if defined(__APPLE__) // when overloading malloc, we still get mixed pointers sometimes on macOS; this avoids a bad access +#define MI_DEFAULT_PAGEMAP_COMMIT 1 +#else +#define MI_DEFAULT_PAGEMAP_COMMIT 0 +#endif +#endif + +#ifndef MI_DEFAULT_PAGE_MAX_RECLAIM +#define MI_DEFAULT_PAGE_MAX_RECLAIM (-1) // unlimited +#endif + +#ifndef MI_DEFAULT_PAGE_CROSS_THREAD_MAX_RECLAIM +#define MI_DEFAULT_PAGE_CROSS_THREAD_MAX_RECLAIM 32 +#endif + +#ifndef MI_DEFAULT_ALLOW_THP +#if defined(__ANDROID__) +#define MI_DEFAULT_ALLOW_THP 0 +#else +#define MI_DEFAULT_ALLOW_THP 1 +#endif +#endif + +// Static options +static mi_option_desc_t mi_options[_mi_option_last] = { // stable options - #if MI_DEBUG || defined(MI_SHOW_ERRORS) - { 1, UNINIT, MI_OPTION(show_errors) }, - #else - { 0, UNINIT, MI_OPTION(show_errors) }, - #endif - { 0, UNINIT, MI_OPTION(show_stats) }, - { 0, UNINIT, MI_OPTION(verbose) }, - - // the following options are experimental and not all combinations make sense. - { 1, UNINIT, MI_OPTION(eager_commit) }, // commit per segment directly (4MiB) (but see also `eager_commit_delay`) - { 2, UNINIT, MI_OPTION_LEGACY(arena_eager_commit,eager_region_commit) }, // eager commit arena's? 2 is used to enable this only on an OS that has overcommit (i.e. linux) - { 1, UNINIT, MI_OPTION_LEGACY(purge_decommits,reset_decommits) }, // purge decommits memory (instead of reset) (note: on linux this uses MADV_DONTNEED for decommit) - { 0, UNINIT, MI_OPTION_LEGACY(allow_large_os_pages,large_os_pages) }, // use large OS pages, use only with eager commit to prevent fragmentation of VMA's - { 0, UNINIT, MI_OPTION(reserve_huge_os_pages) }, // per 1GiB huge pages - {-1, UNINIT, MI_OPTION(reserve_huge_os_pages_at) }, // reserve huge pages at node N - { 0, UNINIT, MI_OPTION(reserve_os_memory) }, // reserve N KiB OS memory in advance (use `option_get_size`) - { 0, UNINIT, MI_OPTION(deprecated_segment_cache) }, // cache N segments per thread - { 0, UNINIT, MI_OPTION(deprecated_page_reset) }, // reset page memory on free - { 0, UNINIT, MI_OPTION_LEGACY(abandoned_page_purge,abandoned_page_reset) }, // reset free page memory when a thread terminates - { 0, UNINIT, MI_OPTION(deprecated_segment_reset) }, // reset segment memory on free (needs eager commit) -#if defined(__NetBSD__) - { 0, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed +#if MI_DEBUG || defined(MI_SHOW_ERRORS) + { 1, MI_OPTION_UNINIT, MI_OPTION(show_errors) }, #else - { 1, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed (but per page in the segment on demand) + { 0, MI_OPTION_UNINIT, MI_OPTION(show_errors) }, #endif - { 10, UNINIT, MI_OPTION_LEGACY(purge_delay,reset_delay) }, // purge delay in milli-seconds - { 0, UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes. - { 0, UNINIT, MI_OPTION_LEGACY(disallow_os_alloc,limit_os_alloc) }, // 1 = do not use OS memory for allocation (but only reserved arenas) - { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose - { 32, UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output - { 32, UNINIT, MI_OPTION(max_warnings) }, // maximum warnings that are output - { 10, UNINIT, MI_OPTION(max_segment_reclaim)}, // max. percentage of the abandoned segments to be reclaimed per try. - { 0, UNINIT, MI_OPTION(destroy_on_exit)}, // release all OS memory on process exit; careful with dangling pointer or after-exit frees! - #if (MI_INTPTR_SIZE>4) - { 1024L*1024L, UNINIT, MI_OPTION(arena_reserve) }, // reserve memory N KiB at a time (=1GiB) (use `option_get_size`) - #else - { 128L*1024L, UNINIT, MI_OPTION(arena_reserve) }, // =128MiB on 32-bit - #endif - { 10, UNINIT, MI_OPTION(arena_purge_mult) }, // purge delay multiplier for arena's - { 1, UNINIT, MI_OPTION_LEGACY(purge_extend_delay, decommit_extend_delay) }, - { 1, UNINIT, MI_OPTION(abandoned_reclaim_on_free) },// reclaim an abandoned segment on a free - { 0, UNINIT, MI_OPTION(disallow_arena_alloc) }, // 1 = do not use arena's for allocation (except if using specific arena id's) - { 400, UNINIT, MI_OPTION(retry_on_oom) }, // windows only: retry on out-of-memory for N milli seconds (=400), set to 0 to disable retries. + { 0, MI_OPTION_UNINIT, MI_OPTION(show_stats) }, + { MI_DEFAULT_VERBOSE, MI_OPTION_UNINIT, MI_OPTION(verbose) }, + + // some of the following options are experimental and not all combinations are allowed. + { 1, MI_OPTION_UNINIT, MI_OPTION(deprecated_eager_commit) }, + { MI_DEFAULT_ARENA_EAGER_COMMIT, + MI_OPTION_UNINIT, MI_OPTION_LEGACY(arena_eager_commit,eager_region_commit) }, // eager commit arena's? 2 is used to enable this only on an OS that has overcommit (i.e. linux) + { 1, MI_OPTION_UNINIT, MI_OPTION_LEGACY(purge_decommits,reset_decommits) }, // purge decommits memory (instead of reset) (note: on linux this uses MADV_DONTNEED for decommit) + { MI_DEFAULT_ALLOW_LARGE_OS_PAGES, + MI_OPTION_UNINIT, MI_OPTION_LEGACY(allow_large_os_pages,large_os_pages) }, // use large OS pages, use only with eager commit to prevent fragmentation of VMA's + { MI_DEFAULT_RESERVE_HUGE_OS_PAGES, + MI_OPTION_UNINIT, MI_OPTION(reserve_huge_os_pages) }, // per 1GiB huge pages + {-1, MI_OPTION_UNINIT, MI_OPTION(reserve_huge_os_pages_at) }, // reserve huge pages at node N + { MI_DEFAULT_RESERVE_OS_MEMORY, + MI_OPTION_UNINIT, MI_OPTION(reserve_os_memory) }, // reserve N KiB OS memory in advance (use `option_get_size`) + { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_segment_cache) }, // cache N segments per thread + { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_page_reset) }, // reset page memory on free + { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_abandoned_page_purge) }, + { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_segment_reset) }, // reset segment memory on free (needs eager commit) + { 1, MI_OPTION_UNINIT, MI_OPTION(deprecated_eager_commit_delay) }, + { 1000,MI_OPTION_UNINIT, MI_OPTION_LEGACY(purge_delay,reset_delay) }, // purge delay in milli-seconds + { 0, MI_OPTION_UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes. + { 0, MI_OPTION_UNINIT, MI_OPTION_LEGACY(disallow_os_alloc,limit_os_alloc) }, // 1 = do not use OS memory for allocation (but only reserved arenas) + { 100, MI_OPTION_UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose + { 32, MI_OPTION_UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output + { 32, MI_OPTION_UNINIT, MI_OPTION(max_warnings) }, // maximum warnings that are output + { 10, MI_OPTION_UNINIT, MI_OPTION(deprecated_max_segment_reclaim)}, // max. percentage of the abandoned segments to be reclaimed per try. + { 0, MI_OPTION_UNINIT, MI_OPTION(destroy_on_exit)}, // release all OS memory on process exit; careful with dangling pointer or after-exit frees! + { MI_DEFAULT_ARENA_RESERVE, MI_OPTION_UNINIT, MI_OPTION(arena_reserve) }, // reserve memory N KiB at a time (=1GiB) (use `option_get_size`) + { 1, MI_OPTION_UNINIT, MI_OPTION(arena_purge_mult) }, // purge delay multiplier for arena's + { 1, MI_OPTION_UNINIT, MI_OPTION_LEGACY(deprecated_purge_extend_delay, decommit_extend_delay) }, + { MI_DEFAULT_DISALLOW_ARENA_ALLOC, MI_OPTION_UNINIT, MI_OPTION(disallow_arena_alloc) }, // 1 = do not use arena's for allocation (except if using specific arena id's) + { 400, MI_OPTION_UNINIT, MI_OPTION(retry_on_oom) }, // windows only: retry on out-of-memory for N milli seconds (=400), set to 0 to disable retries. +#if defined(MI_VISIT_ABANDONED) + { 1, MI_OPTION_INITIALIZED, MI_OPTION(visit_abandoned) }, // allow visiting theap blocks in abandoned segments; requires taking locks during reclaim. +#else + { 0, MI_OPTION_UNINIT, MI_OPTION(visit_abandoned) }, +#endif + { 0, MI_OPTION_UNINIT, MI_OPTION(guarded_min) }, // only used when building with MI_GUARDED: minimal rounded object size for guarded objects + { MI_GiB, MI_OPTION_UNINIT, MI_OPTION(guarded_max) }, // only used when building with MI_GUARDED: maximal rounded object size for guarded objects + { 0, MI_OPTION_UNINIT, MI_OPTION(guarded_precise) }, // disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0) + { MI_DEFAULT_GUARDED_SAMPLE_RATE, + MI_OPTION_UNINIT, MI_OPTION(guarded_sample_rate)}, // 1 out of N allocations in the min/max range will be guarded (=4000) + { 0, MI_OPTION_UNINIT, MI_OPTION(guarded_sample_seed)}, + { 10000, MI_OPTION_UNINIT, MI_OPTION(generic_collect) }, // collect theaps every N (=10000) generic allocation calls + { 0, MI_OPTION_UNINIT, MI_OPTION_LEGACY(page_reclaim_on_free, abandoned_reclaim_on_free) },// reclaim abandoned (small) pages on a free: -1 = disable completely, 0 = only reclaim into the originating theap, 1 = reclaim on free across theaps + { 2, MI_OPTION_UNINIT, MI_OPTION(page_full_retain) }, // number of (small) pages to retain in the free page queues + { 4, MI_OPTION_UNINIT, MI_OPTION(page_max_candidates) }, // max search to find a best page candidate + { 0, MI_OPTION_UNINIT, MI_OPTION(max_vabits) }, // max virtual address space bits + { MI_DEFAULT_PAGEMAP_COMMIT, + MI_OPTION_UNINIT, MI_OPTION(pagemap_commit) }, // commit the full pagemap upfront? + { 0, MI_OPTION_UNINIT, MI_OPTION(page_commit_on_demand) }, // commit pages on-demand (2 disables this only on overcommit systems (like Linux)) + { MI_DEFAULT_PAGE_MAX_RECLAIM, + MI_OPTION_UNINIT, MI_OPTION(page_max_reclaim) }, // don't reclaim (small) pages of the same originating theap if we already own N pages in that size class + { MI_DEFAULT_PAGE_CROSS_THREAD_MAX_RECLAIM, + MI_OPTION_UNINIT, MI_OPTION(page_cross_thread_max_reclaim) }, // don't reclaim (small) pages across threads if we already own N pages in that size class + { MI_DEFAULT_ALLOW_THP, + MI_OPTION_UNINIT, MI_OPTION(allow_thp) }, // allow transparent huge pages? (=1) (on Android =0 by default). Set to 0 to disable THP for the process. + { 0, MI_OPTION_UNINIT, MI_OPTION(minimal_purge_size) }, // set minimal purge size (in KiB) (=0). Using 0 resolves to either 64 (or 2048 if `mi_option_allow_thp==2`). + { MI_DEFAULT_ARENA_MAX_OBJECT_SIZE, + MI_OPTION_UNINIT, MI_OPTION(arena_max_object_size) }, // set maximal object size that can be allocated in an arena (in KiB) (=2GiB on 64-bit). + { 0, MI_OPTION_UNINIT, MI_OPTION(arena_is_numa_local) }, // associate local numa node with an initial arena allocation }; static void mi_option_init(mi_option_desc_t* desc); static bool mi_option_has_size_in_kib(mi_option_t option) { - return (option == mi_option_reserve_os_memory || option == mi_option_arena_reserve); + return (option == mi_option_reserve_os_memory || option == mi_option_arena_reserve || + option == mi_option_minimal_purge_size || option == mi_option_arena_max_object_size); } void _mi_options_init(void) { - // called on process load; should not be called before the CRT is initialized! - // (e.g. do not call this from process_init as that may run before CRT initialization) - mi_add_stderr_output(); // now it safe to use stderr for output + // called on process load for(int i = 0; i < _mi_option_last; i++ ) { mi_option_t option = (mi_option_t)i; long l = mi_option_get(option); MI_UNUSED(l); // initialize - // if (option != mi_option_verbose) - { - mi_option_desc_t* desc = &options[option]; - _mi_verbose_message("option '%s': %ld %s\n", desc->name, desc->value, (mi_option_has_size_in_kib(option) ? "KiB" : "")); - } } mi_max_error_count = mi_option_get(mi_option_max_errors); mi_max_warning_count = mi_option_get(mi_option_max_warnings); + #if MI_GUARDED + if (mi_option_get(mi_option_guarded_sample_rate) > 0) { + if (mi_option_is_enabled(mi_option_allow_large_os_pages)) { + mi_option_disable(mi_option_allow_large_os_pages); + _mi_warning_message("option 'allow_large_os_pages' is disabled to allow for guarded objects\n"); + } + } + #endif } +// called at actual process load, it should be safe to print now +void _mi_options_post_init(void) { + mi_add_stderr_output(); // now it safe to use stderr for output + if (mi_option_is_enabled(mi_option_verbose)) { mi_options_print(); } +} + +#define mi_stringifyx(str) #str // and stringify +#define mi_stringify(str) mi_stringifyx(str) // expand + +mi_decl_export void mi_options_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept +{ + // show version + const int vermajor = MI_MALLOC_VERSION/10000; + const int verminor = (MI_MALLOC_VERSION%10000)/100; + const int verpatch = (MI_MALLOC_VERSION%100); + _mi_fprintf(out, arg, "v%i.%i.%i%s%s (built on %s, %s)\n", vermajor, verminor, verpatch, + #if defined(MI_CMAKE_BUILD_TYPE) + ", " mi_stringify(MI_CMAKE_BUILD_TYPE) + #else + "" + #endif + , + #if defined(MI_GIT_DESCRIBE) + ", git " mi_stringify(MI_GIT_DESCRIBE) + #else + "" + #endif + , __DATE__, __TIME__); + + // show options + for (int i = 0; i < _mi_option_last; i++) { + mi_option_t option = (mi_option_t)i; + long l = mi_option_get(option); MI_UNUSED(l); // possibly initialize + mi_option_desc_t* desc = &mi_options[option]; + _mi_fprintf(out, arg, "option '%s': %ld %s\n", desc->name, desc->value, (mi_option_has_size_in_kib(option) ? "KiB" : "")); + } + + // show build configuration + _mi_fprintf(out, arg, "debug level : %d\n", MI_DEBUG ); + _mi_fprintf(out, arg, "secure level: %d\n", MI_SECURE ); + _mi_fprintf(out, arg, "mem tracking: %s\n", MI_TRACK_TOOL); + #if MI_GUARDED + _mi_fprintf(out, arg, "guarded build: %s\n", mi_option_get(mi_option_guarded_sample_rate) != 0 ? "enabled" : "disabled"); + #endif + #if MI_TSAN + _mi_fprintf(out, arg, "thread santizer enabled\n"); + #endif +} + +mi_decl_export void mi_options_print(void) mi_attr_noexcept { + mi_options_print_out(NULL, NULL); +} + +long _mi_option_get_fast(mi_option_t option) { + mi_assert(option >= 0 && option < _mi_option_last); + mi_option_desc_t* desc = &mi_options[option]; + mi_assert(desc->option == option); // index should match the option + //mi_assert(desc->init != MI_OPTION_UNINIT); + return desc->value; +} + + mi_decl_nodiscard long mi_option_get(mi_option_t option) { mi_assert(option >= 0 && option < _mi_option_last); if (option < 0 || option >= _mi_option_last) return 0; - mi_option_desc_t* desc = &options[option]; + mi_option_desc_t* desc = &mi_options[option]; mi_assert(desc->option == option); // index should match the option - if mi_unlikely(desc->init == UNINIT) { + if mi_unlikely(desc->init == MI_OPTION_UNINIT) { mi_option_init(desc); } return desc->value; @@ -135,11 +285,12 @@ mi_decl_nodiscard long mi_option_get_clamp(mi_option_t option, long min, long ma } mi_decl_nodiscard size_t mi_option_get_size(mi_option_t option) { - mi_assert_internal(mi_option_has_size_in_kib(option)); const long x = mi_option_get(option); size_t size = (x < 0 ? 0 : (size_t)x); if (mi_option_has_size_in_kib(option)) { - size *= MI_KiB; + if (mi_mul_overflow(size, MI_KiB, &size)) { + size = MI_MAX_ALLOC_SIZE; + } } return size; } @@ -147,17 +298,24 @@ mi_decl_nodiscard size_t mi_option_get_size(mi_option_t option) { void mi_option_set(mi_option_t option, long value) { mi_assert(option >= 0 && option < _mi_option_last); if (option < 0 || option >= _mi_option_last) return; - mi_option_desc_t* desc = &options[option]; + mi_option_desc_t* desc = &mi_options[option]; mi_assert(desc->option == option); // index should match the option desc->value = value; - desc->init = INITIALIZED; + desc->init = MI_OPTION_INITIALIZED; + // ensure min/max range; be careful to not recurse. + if (desc->option == mi_option_guarded_min && _mi_option_get_fast(mi_option_guarded_max) < value) { + mi_option_set(mi_option_guarded_max, value); + } + else if (desc->option == mi_option_guarded_max && _mi_option_get_fast(mi_option_guarded_min) > value) { + mi_option_set(mi_option_guarded_min, value); + } } void mi_option_set_default(mi_option_t option, long value) { mi_assert(option >= 0 && option < _mi_option_last); if (option < 0 || option >= _mi_option_last) return; - mi_option_desc_t* desc = &options[option]; - if (desc->init != INITIALIZED) { + mi_option_desc_t* desc = &mi_options[option]; + if (desc->init != MI_OPTION_INITIALIZED) { desc->value = value; } } @@ -194,37 +352,43 @@ static void mi_cdecl mi_out_stderr(const char* msg, void* arg) { // an output function is registered it is called immediately with // the output up to that point. #ifndef MI_MAX_DELAY_OUTPUT -#define MI_MAX_DELAY_OUTPUT ((size_t)(32*1024)) +#define MI_MAX_DELAY_OUTPUT ((size_t)(16*1024)) #endif static char out_buf[MI_MAX_DELAY_OUTPUT+1]; static _Atomic(size_t) out_len; +static mi_lock_t out_buf_lock = MI_LOCK_INITIALIZER; static void mi_cdecl mi_out_buf(const char* msg, void* arg) { MI_UNUSED(arg); if (msg==NULL) return; - if (mi_atomic_load_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return; + if (mi_atomic_load_acquire(&out_len)>=MI_MAX_DELAY_OUTPUT) return; size_t n = _mi_strlen(msg); - if (n==0) return; - // claim space - size_t start = mi_atomic_add_acq_rel(&out_len, n); - if (start >= MI_MAX_DELAY_OUTPUT) return; - // check bound - if (start+n >= MI_MAX_DELAY_OUTPUT) { - n = MI_MAX_DELAY_OUTPUT-start-1; + if (n==0 || n >= MI_MAX_DELAY_OUTPUT) return; + // copy msg into the buffer + mi_lock(&out_buf_lock) { + const size_t start = mi_atomic_add_acq_rel(&out_len, n); + if (start < MI_MAX_DELAY_OUTPUT) { + // check bound + if (start+n >= MI_MAX_DELAY_OUTPUT) { + n = MI_MAX_DELAY_OUTPUT-start-1; + } + _mi_memcpy(&out_buf[start], msg, n); + } } - _mi_memcpy(&out_buf[start], msg, n); } static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) { if (out==NULL) return; // claim (if `no_more_buf == true`, no more output will be added after this point) - size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1)); - // and output the current contents - if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT; - out_buf[count] = 0; - out(out_buf,arg); - if (!no_more_buf) { - out_buf[count] = '\n'; // if continue with the buffer, insert a newline + mi_lock(&out_buf_lock) { + size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1)); + // and output the current contents + if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT; + out_buf[count] = 0; + out(out_buf,arg); + if (!no_more_buf) { + out_buf[count] = '\n'; // if continue with the buffer, insert a newline + } } } @@ -242,28 +406,29 @@ static void mi_cdecl mi_out_buf_stderr(const char* msg, void* arg) { // Default output handler // -------------------------------------------------------- -// Should be atomic but gives errors on many platforms as generally we cannot cast a function pointer to a uintptr_t. -// For now, don't register output from multiple threads. -static mi_output_fun* volatile mi_out_default; // = NULL +// The program should only install a single output handler from a single thread +// since otherwise the argument and output function may not match. +static _Atomic(void*) mi_out_default; // = // is `mi_output_fun*` (but some platforms don't support atomic function pointers) static _Atomic(void*) mi_out_arg; // = NULL static mi_output_fun* mi_out_get_default(void** parg) { + mi_output_fun* const out = (mi_output_fun*)mi_atomic_load_ptr_acquire(void,&mi_out_default); if (parg != NULL) { *parg = mi_atomic_load_ptr_acquire(void,&mi_out_arg); } - mi_output_fun* out = mi_out_default; return (out == NULL ? &mi_out_buf : out); } void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept { - mi_out_default = (out == NULL ? &mi_out_stderr : out); // stop using the delayed output buffer + mi_atomic_store_ptr_release(void,&mi_out_default, (void*)(out == NULL ? &mi_out_stderr : out)); // stop using the delayed output buffer mi_atomic_store_ptr_release(void,&mi_out_arg, arg); - if (out!=NULL) mi_out_buf_flush(out,true,arg); // output all the delayed output now + if (out!=NULL) { mi_out_buf_flush(out,true,arg); } // output all the delayed output now } // add stderr to the delayed output after the module is loaded static void mi_add_stderr_output(void) { mi_assert_internal(mi_out_default == NULL); mi_out_buf_flush(&mi_out_stderr, false, NULL); // flush current contents to stderr - mi_out_default = &mi_out_buf_stderr; // and add stderr to the delayed output + mi_atomic_store_ptr_release(void,&mi_out_default,(void*)&mi_out_buf_stderr); // and add stderr to the delayed output + mi_atomic_store_ptr_release(void,&mi_out_arg,NULL); } // -------------------------------------------------------- @@ -280,7 +445,7 @@ static _Atomic(size_t) warning_count; // = 0; // when >= max_warning_count stop // (recursively) invoke malloc again to allocate space for the thread local // variables on demand. This is why we use a _mi_preloading test on such // platforms. However, C code generator may move the initial thread local address -// load before the `if` and we therefore split it out in a separate funcion. +// load before the `if` and we therefore split it out in a separate function. static mi_decl_thread bool recurse = false; static mi_decl_noinline bool mi_recurse_enter_prim(void) { @@ -294,14 +459,14 @@ static mi_decl_noinline void mi_recurse_exit_prim(void) { } static bool mi_recurse_enter(void) { - #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) + #if defined(__APPLE__) || defined(__ANDROID__) || defined(MI_TLS_RECURSE_GUARD) if (_mi_preloading()) return false; #endif return mi_recurse_enter_prim(); } static void mi_recurse_exit(void) { - #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) + #if defined(__APPLE__) || defined(__ANDROID__) || defined(MI_TLS_RECURSE_GUARD) if (_mi_preloading()) return; #endif mi_recurse_exit_prim(); @@ -324,7 +489,7 @@ void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* me // Define our own limited `fprintf` that avoids memory allocation. // We do this using `_mi_vsnprintf` with a limited buffer. static void mi_vfprintf( mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args ) { - char buf[512]; + char buf[992]; if (fmt==NULL) return; if (!mi_recurse_enter()) return; _mi_vsnprintf(buf, sizeof(buf)-1, fmt, args); @@ -350,6 +515,20 @@ static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix } } +void _mi_raw_message(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + mi_vfprintf(NULL, NULL, NULL, fmt, args); + va_end(args); +} + +void _mi_message(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args); + va_end(args); +} + void _mi_trace_message(const char* fmt, ...) { if (mi_option_get(mi_option_verbose) <= 1) return; // only with verbose level 2 or higher va_list args; @@ -387,7 +566,7 @@ void _mi_warning_message(const char* fmt, ...) { #if MI_DEBUG -void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) { +mi_decl_noreturn mi_decl_cold void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) mi_attr_noexcept { _mi_fprintf(NULL, NULL, "mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion); abort(); } @@ -457,35 +636,35 @@ static void mi_option_init(mi_option_desc_t* desc) { char buf[64+1]; _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); _mi_strlcat(buf, desc->name, sizeof(buf)); - bool found = _mi_getenv(buf, s, sizeof(s)); - if (!found && desc->legacy_name != NULL) { + int err = _mi_getenv(buf, s, sizeof(s)); + if (err==ENOENT && desc->legacy_name != NULL) { _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); _mi_strlcat(buf, desc->legacy_name, sizeof(buf)); - found = _mi_getenv(buf, s, sizeof(s)); - if (found) { + err = _mi_getenv(buf, s, sizeof(s)); + if (err==0) { _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name); } } - if (found) { + if (err==0) { size_t len = _mi_strnlen(s, sizeof(buf) - 1); for (size_t i = 0; i < len; i++) { buf[i] = _mi_toupper(s[i]); } buf[len] = 0; - if (buf[0] == 0 || strstr("1;TRUE;YES;ON", buf) != NULL) { + if (buf[0] == 0 || _mi_streq(buf,"1") || _mi_streq(buf,"TRUE") || _mi_streq(buf,"YES") || _mi_streq(buf,"ON")) { desc->value = 1; - desc->init = INITIALIZED; + desc->init = MI_OPTION_INITIALIZED; } - else if (strstr("0;FALSE;NO;OFF", buf) != NULL) { + else if (_mi_streq(buf,"0") || _mi_streq(buf,"FALSE") || _mi_streq(buf,"NO") || _mi_streq(buf,"OFF")) { desc->value = 0; - desc->init = INITIALIZED; + desc->init = MI_OPTION_INITIALIZED; } else { char* end = buf; long value = strtol(buf, &end, 10); if (mi_option_has_size_in_kib(desc->option)) { - // this option is interpreted in KiB to prevent overflow of `long` for large allocations + // this option is interpreted in KiB to prevent overflow of `long` for large allocations // (long is 32-bit on 64-bit windows, which allows for 4TiB max.) size_t size = (value < 0 ? 0 : (size_t)value); bool overflow = false; @@ -496,16 +675,15 @@ static void mi_option_init(mi_option_desc_t* desc) { else { size = (size + MI_KiB - 1) / MI_KiB; } if (end[0] == 'I' && end[1] == 'B') { end += 2; } // KiB, MiB, GiB, TiB else if (*end == 'B') { end++; } // Kb, Mb, Gb, Tb - if (overflow || size > MI_MAX_ALLOC_SIZE) { size = (MI_MAX_ALLOC_SIZE / MI_KiB); } + if (overflow || size > (MI_MAX_ALLOC_SIZE / MI_KiB)) { size = (MI_MAX_ALLOC_SIZE / MI_KiB); } value = (size > LONG_MAX ? LONG_MAX : (long)size); } if (*end == 0) { - desc->value = value; - desc->init = INITIALIZED; + mi_option_set(desc->option, value); } else { // set `init` first to avoid recursion through _mi_warning_message on mimalloc_verbose. - desc->init = DEFAULTED; + desc->init = MI_OPTION_DEFAULTED; if (desc->option == mi_option_verbose && desc->value == 0) { // if the 'mimalloc_verbose' env var has a bogus value we'd never know // (since the value defaults to 'off') so in that case briefly enable verbose @@ -518,9 +696,10 @@ static void mi_option_init(mi_option_desc_t* desc) { } } } - mi_assert_internal(desc->init != UNINIT); + mi_assert_internal(desc->init != MI_OPTION_UNINIT); } - else if (!_mi_preloading()) { - desc->init = DEFAULTED; + else if (err==ENOENT) { + desc->init = MI_OPTION_DEFAULTED; } + // and on another error, keep unitialized to try again (can happen during preloading if getenv is not available) } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/os.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/os.c index ce104273..5edcca1b 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/os.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/os.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -9,18 +9,27 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc/atomic.h" #include "mimalloc/prim.h" - /* ----------------------------------------------------------- - Initialization. + Initialization. ----------------------------------------------------------- */ +#ifndef MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB +#if MI_INTPTR_SIZE < 8 +#define MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB 4*MI_MiB // 4 GiB +#else +#define MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB 32*MI_MiB // 32 GiB +#endif +#endif static mi_os_mem_config_t mi_os_mem_config = { - 4096, // page size - 0, // large page size (usually 2MiB) - 4096, // allocation granularity - true, // has overcommit? (if true we use MAP_NORESERVE on mmap systems) - false, // can we partially free allocated blocks? (on mmap systems we can free anywhere in a mapped range, but on Windows we must free the entire span) - true // has virtual reserve? (if true we can reserve virtual address space without using commit or physical memory) + 4096, // page size + 0, // large page size (usually 2MiB) + 4096, // allocation granularity + MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB, + MI_MAX_VABITS, // in `bits.h` + true, // has overcommit? (if true we use MAP_NORESERVE on mmap systems) + false, // can we partially free allocated blocks? (on mmap systems we can free anywhere in a mapped range, but on Windows we must free the entire span) + true, // has virtual reserve? (if true we can reserve virtual address space without using commit or physical memory) + false // has transparent huge pages? (if true we purge in (aligned) large page size chunks only to not fragment such pages) }; bool _mi_os_has_overcommit(void) { @@ -42,9 +51,35 @@ size_t _mi_os_large_page_size(void) { return (mi_os_mem_config.large_page_size != 0 ? mi_os_mem_config.large_page_size : _mi_os_page_size()); } -bool _mi_os_use_large_page(size_t size, size_t alignment) { +// minimal purge size. Can be larger than the page size if transparent huge pages are enabled. +size_t _mi_os_minimal_purge_size(void) { + size_t minsize = mi_option_get_size(mi_option_minimal_purge_size); + if (minsize != 0) { + return _mi_align_up(minsize, _mi_os_page_size()); + } + else if (mi_os_mem_config.has_transparent_huge_pages && mi_option_get(mi_option_allow_thp) == 2) { + return _mi_os_large_page_size(); + } + else { + return _mi_os_page_size(); + } +} + +size_t _mi_os_guard_page_size(void) { + const size_t gsize = _mi_os_page_size(); + mi_assert(gsize <= (MI_ARENA_SLICE_SIZE/4)); // issue #1166 + return gsize; +} + +size_t _mi_os_virtual_address_bits(void) { + const size_t vbits = mi_os_mem_config.virtual_address_bits; + mi_assert(vbits <= MI_MAX_VABITS); + return vbits; +} + +bool _mi_os_canuse_large_page(size_t size, size_t alignment) { // if we have access, check the size and alignment requirements - if (mi_os_mem_config.large_page_size == 0 || !mi_option_is_enabled(mi_option_allow_large_os_pages)) return false; + if (mi_os_mem_config.large_page_size == 0) return false; return ((size % mi_os_mem_config.large_page_size) == 0 && (alignment % mi_os_mem_config.large_page_size) == 0); } @@ -68,98 +103,177 @@ void _mi_os_init(void) { /* ----------------------------------------------------------- Util -------------------------------------------------------------- */ -bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); -bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats); - - -/* ----------------------------------------------------------- - aligned hinting --------------------------------------------------------------- */ +bool _mi_os_decommit(void* addr, size_t size); +bool _mi_os_commit(void* addr, size_t size, bool* is_zero); -// On 64-bit systems, we can do efficient aligned allocation by using -// the 2TiB to 30TiB area to allocate those. -#if (MI_INTPTR_SIZE >= 8) -static mi_decl_cache_align _Atomic(uintptr_t)aligned_base; +// On systems with enough virtual address bits, we can do efficient aligned allocation by using +// the 2TiB to 30TiB area to allocate those. If we have at least 46 bits of virtual address +// space (64TiB) we use this technique. (but see issue #939) +#if (MI_INTPTR_SIZE >= 8) && !defined(MI_NO_ALIGNED_HINT) // && !defined(WIN32) && !defined(ANDROID) -// Return a MI_SEGMENT_SIZE aligned address that is probably available. +// Return a MI_HINT_ALIGN (4MiB) aligned address that is probably available. // If this returns NULL, the OS will determine the address but on some OS's that may not be // properly aligned which can be more costly as it needs to be adjusted afterwards. -// For a size > 1GiB this always returns NULL in order to guarantee good ASLR randomization; +// In secure mode, for a size > 16GiB this always returns NULL in order to guarantee good ASLR randomization; // (otherwise an initial large allocation of say 2TiB has a 50% chance to include (known) addresses // in the middle of the 2TiB - 6TiB address range (see issue #372)) -#define MI_HINT_BASE ((uintptr_t)2 << 40) // 2TiB start -#define MI_HINT_AREA ((uintptr_t)4 << 40) // upto 6TiB (since before win8 there is "only" 8TiB available to processes) -#define MI_HINT_MAX ((uintptr_t)30 << 40) // wrap after 30TiB (area after 32TiB is used for huge OS pages) +#define MI_HINT_ALIGN ((uintptr_t)4 << 20) // 4MiB alignment +#define MI_HINT_BASE ((uintptr_t)2 << 40) // 2TiB start +#define MI_HINT_AREA ((uintptr_t)4 << 40) // upto (2+4) 6TiB (since before win8 there is "only" 8TiB available to processes) +#define MI_HINT_MAX ((uintptr_t)30 << 40) // wrap after 30TiB (area after 32TiB is used for huge OS pages) void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size) { - if (try_alignment <= 1 || try_alignment > MI_SEGMENT_SIZE) return NULL; - size = _mi_align_up(size, MI_SEGMENT_SIZE); - if (size > 1*MI_GiB) return NULL; // guarantee the chance of fixed valid address is at most 1/(MI_HINT_AREA / 1<<30) = 1/4096. - #if (MI_SECURE>0) - size += MI_SEGMENT_SIZE; // put in `MI_SEGMENT_SIZE` virtual gaps between hinted blocks; this splits VLA's but increases guarded areas. + static mi_decl_cache_align _Atomic(uintptr_t) aligned_base; // = 0 + + // todo: perhaps only do alignment hints if THP is enabled? + if (try_alignment <= mi_os_mem_config.alloc_granularity || try_alignment > MI_HINT_ALIGN) return NULL; + if (mi_os_mem_config.virtual_address_bits < 46) return NULL; // < 64TiB virtual address space + size = _mi_align_up(size, MI_HINT_ALIGN); + #if (MI_SECURE>=1) + if (size > 16*MI_GiB) return NULL; // guarantee the chance of fixed valid address is at most 1/(MI_HINT_AREA / 1<<34) = 1/256 #endif + size += MI_HINT_ALIGN; // put in virtual gaps between hinted blocks; this splits VLA's but increases guarded areas. uintptr_t hint = mi_atomic_add_acq_rel(&aligned_base, size); if (hint == 0 || hint > MI_HINT_MAX) { // wrap or initialize uintptr_t init = MI_HINT_BASE; - #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of aligned allocations unless in debug mode - uintptr_t r = _mi_heap_random_next(mi_prim_get_default_heap()); - init = init + ((MI_SEGMENT_SIZE * ((r>>17) & 0xFFFFF)) % MI_HINT_AREA); // (randomly 20 bits)*4MiB == 0 to 4TiB + #if (MI_SECURE>=1 || defined(NDEBUG)) // security: randomize start of aligned allocations unless in debug mode + mi_theap_t* const theap = _mi_theap_default(); // don't use `mi_theap_get_default()` as that can cause allocation recursively (issue #1267) + if (!mi_theap_is_initialized(theap)) return NULL; // no hint as we lack randomness at this point + const uintptr_t r = _mi_theap_random_next(theap); + init = init + ((MI_HINT_ALIGN * ((r>>17) & 0xFFFFF)) % MI_HINT_AREA); // (randomly 20 bits)*4MiB == 0 to 4TiB #endif uintptr_t expected = hint + size; mi_atomic_cas_strong_acq_rel(&aligned_base, &expected, init); hint = mi_atomic_add_acq_rel(&aligned_base, size); // this may still give 0 or > MI_HINT_MAX but that is ok, it is a hint after all } + mi_assert_internal(hint%MI_HINT_ALIGN == 0); if (hint%try_alignment != 0) return NULL; return (void*)hint; } #else void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size) { - MI_UNUSED(try_alignment); MI_UNUSED(size); - return NULL; + MI_UNUSED(try_alignment); MI_UNUSED(size); + return NULL; } #endif +/* ----------------------------------------------------------- + Guard page allocation +----------------------------------------------------------- */ + +// In secure mode, return the size of a guard page, otherwise 0 +size_t _mi_os_secure_guard_page_size(void) { + #if MI_SECURE > 0 + return _mi_os_guard_page_size(); + #else + return 0; + #endif +} + +// In secure mode, try to decommit an area and output a warning if this fails. +bool _mi_os_secure_guard_page_set_at(void* addr, mi_memid_t memid) { + if (addr == NULL) return true; + #if MI_SECURE > 0 + bool ok = false; + if (!memid.is_pinned) { + mi_arena_t* const arena = mi_memid_arena(memid); + if (arena != NULL && arena->commit_fun != NULL) { + ok = (*(arena->commit_fun))(false /* decommit */, addr, _mi_os_secure_guard_page_size(), NULL, arena->commit_fun_arg); + } + else { + ok = _mi_os_decommit(addr, _mi_os_secure_guard_page_size()); + } + } + if (!ok) { + _mi_error_message(EINVAL, "secure level %d, but failed to commit guard page (at %p of size %zu)\n", MI_SECURE, addr, _mi_os_secure_guard_page_size()); + } + return ok; + #else + MI_UNUSED(memid); + return true; + #endif +} + +// In secure mode, try to decommit an area and output a warning if this fails. +bool _mi_os_secure_guard_page_set_before(void* addr, mi_memid_t memid) { + return _mi_os_secure_guard_page_set_at((uint8_t*)addr - _mi_os_secure_guard_page_size(), memid); +} + +// In secure mode, try to recommit an area +bool _mi_os_secure_guard_page_reset_at(void* addr, mi_memid_t memid) { + if (addr == NULL) return true; + #if MI_SECURE > 0 + if (!memid.is_pinned) { + mi_arena_t* const arena = mi_memid_arena(memid); + if (arena != NULL && arena->commit_fun != NULL) { + return (*(arena->commit_fun))(true, addr, _mi_os_secure_guard_page_size(), NULL, arena->commit_fun_arg); + } + else { + return _mi_os_commit(addr, _mi_os_secure_guard_page_size(), NULL); + } + } + #else + MI_UNUSED(memid); + #endif + return true; +} + +// In secure mode, try to recommit an area +bool _mi_os_secure_guard_page_reset_before(void* addr, mi_memid_t memid) { + return _mi_os_secure_guard_page_reset_at((uint8_t*)addr - _mi_os_secure_guard_page_size(), memid); +} + + /* ----------------------------------------------------------- Free memory -------------------------------------------------------------- */ -static void mi_os_free_huge_os_pages(void* p, size_t size, mi_stats_t* stats); +static void mi_os_free_huge_os_pages(void* p, size_t size, mi_subproc_t* subproc); -static void mi_os_prim_free(void* addr, size_t size, bool still_committed, mi_stats_t* tld_stats) { - MI_UNUSED(tld_stats); - mi_stats_t* stats = &_mi_stats_main; +static void mi_os_prim_free(void* addr, size_t size, size_t commit_size, mi_subproc_t* subproc) { mi_assert_internal((size % _mi_os_page_size()) == 0); - if (addr == NULL || size == 0) return; // || _mi_os_is_huge_reserved(addr) - int err = _mi_prim_free(addr, size); + if (addr == NULL) return; // || _mi_os_is_huge_reserved(addr) + int err = _mi_prim_free(addr, size); // allow size==0 (issue #1041) if (err != 0) { _mi_warning_message("unable to free OS memory (error: %d (0x%x), size: 0x%zx bytes, address: %p)\n", err, err, size, addr); } - if (still_committed) { _mi_stat_decrease(&stats->committed, size); } - _mi_stat_decrease(&stats->reserved, size); + if (subproc == NULL) { subproc = _mi_subproc(); } // from `mi_arenas_unsafe_destroy` we pass subproc_main explicitly as we can no longer use the theap pointer + if (commit_size > 0) { + mi_subproc_stat_decrease(subproc, committed, commit_size); + } + mi_subproc_stat_decrease(subproc, reserved, size); } -void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t memid, mi_stats_t* tld_stats) { +void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t memid, mi_subproc_t* subproc /* can be NULL */) { if (mi_memkind_is_os(memid.memkind)) { - size_t csize = _mi_os_good_alloc_size(size); + size_t csize = memid.mem.os.size; + if (csize==0) { csize = _mi_os_good_alloc_size(size); } + mi_assert_internal(csize >= size); + size_t commit_size = (still_committed ? csize : 0); void* base = addr; // different base? (due to alignment) - if (memid.mem.os.base != NULL) { + if (memid.mem.os.base != base) { mi_assert(memid.mem.os.base <= addr); - mi_assert((uint8_t*)memid.mem.os.base + memid.mem.os.alignment >= (uint8_t*)addr); base = memid.mem.os.base; - csize += ((uint8_t*)addr - (uint8_t*)memid.mem.os.base); + const size_t diff = (uint8_t*)addr - (uint8_t*)memid.mem.os.base; + if (memid.mem.os.size==0) { + csize += diff; + } + if (still_committed) { + commit_size -= diff; // the (addr-base) part was already un-committed + } } // free it if (memid.memkind == MI_MEM_OS_HUGE) { mi_assert(memid.is_pinned); - mi_os_free_huge_os_pages(base, csize, tld_stats); + mi_os_free_huge_os_pages(base, csize, subproc); } else { - mi_os_prim_free(base, csize, still_committed, tld_stats); + mi_os_prim_free(base, csize, (still_committed ? commit_size : 0), subproc); } } else { @@ -168,8 +282,8 @@ void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t me } } -void _mi_os_free(void* p, size_t size, mi_memid_t memid, mi_stats_t* tld_stats) { - _mi_os_free_ex(p, size, true, memid, tld_stats); +void _mi_os_free(void* p, size_t size, mi_memid_t memid) { + _mi_os_free_ex(p, size, true, memid, NULL); } @@ -178,27 +292,33 @@ void _mi_os_free(void* p, size_t size, mi_memid_t memid, mi_stats_t* tld_stats) -------------------------------------------------------------- */ // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. -static void* mi_os_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, mi_stats_t* tld_stats) { +// Also `hint_addr` is a hint and may be ignored. +static void* mi_os_prim_alloc_at(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero) { mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); mi_assert_internal(is_zero != NULL); mi_assert_internal(is_large != NULL); if (size == 0) return NULL; if (!commit) { allow_large = false; } if (try_alignment == 0) { try_alignment = 1; } // avoid 0 to ensure there will be no divide by zero when aligning + + // try to align along large OS page size for larger allocations + const size_t large_page_size = mi_os_mem_config.large_page_size; + if (large_page_size > 0 && hint_addr == NULL && size >= 8*large_page_size && _mi_is_power_of_two(try_alignment) && try_alignment < large_page_size) { + try_alignment = large_page_size; + } + *is_zero = false; void* p = NULL; - int err = _mi_prim_alloc(size, try_alignment, commit, allow_large, is_large, is_zero, &p); + int err = _mi_prim_alloc(hint_addr, size, try_alignment, commit, allow_large, is_large, is_zero, &p); if (err != 0) { - _mi_warning_message("unable to allocate OS memory (error: %d (0x%x), size: 0x%zx bytes, align: 0x%zx, commit: %d, allow large: %d)\n", err, err, size, try_alignment, commit, allow_large); + _mi_warning_message("unable to allocate OS memory (error: %d (0x%x), addr: %p, size: 0x%zx bytes, align: 0x%zx, commit: %d, allow large: %d)\n", err, err, hint_addr, size, try_alignment, commit, allow_large); } - MI_UNUSED(tld_stats); - mi_stats_t* stats = &_mi_stats_main; - mi_stat_counter_increase(stats->mmap_calls, 1); + mi_os_stat_counter_increase(mmap_calls, 1); if (p != NULL) { - _mi_stat_increase(&stats->reserved, size); + mi_os_stat_increase(reserved, size); if (commit) { - _mi_stat_increase(&stats->committed, size); + mi_os_stat_increase(committed, size); // seems needed for asan (or `mimalloc-test-api` fails) #ifdef MI_TRACK_ASAN if (*is_zero) { mi_track_mem_defined(p,size); } @@ -209,70 +329,92 @@ static void* mi_os_prim_alloc(size_t size, size_t try_alignment, bool commit, bo return p; } +static void* mi_os_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero) { + return mi_os_prim_alloc_at(NULL, size, try_alignment, commit, allow_large, is_large, is_zero); +} + // Primitive aligned allocation from the OS. // This function guarantees the allocated memory is aligned. -static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** base, mi_stats_t* stats) { +static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, mi_memid_t* memid) { + mi_assert_internal(memid!=NULL); mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0)); mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); - mi_assert_internal(is_large != NULL); - mi_assert_internal(is_zero != NULL); - mi_assert_internal(base != NULL); + *memid = _mi_memid_none(); if (!commit) allow_large = false; if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL; size = _mi_align_up(size, _mi_os_page_size()); - // try first with a hint (this will be aligned directly on Win 10+ or BSD) - void* p = mi_os_prim_alloc(size, alignment, commit, allow_large, is_large, is_zero, stats); - if (p == NULL) return NULL; + // try a direct allocation if the alignment is below the default, or less than or equal to 1/4 fraction of the size. + const bool try_direct_alloc = (alignment <= mi_os_mem_config.alloc_granularity || alignment <= size/4); + + bool os_is_large = false; + bool os_is_zero = false; + void* os_base = NULL; + size_t os_size = size; + void* p = NULL; + if (try_direct_alloc) { + p = mi_os_prim_alloc(size, alignment, commit, allow_large, &os_is_large, &os_is_zero); + } // aligned already? - if (((uintptr_t)p % alignment) == 0) { - *base = p; + if (p != NULL && _mi_is_aligned(p,alignment)) { + os_base = p; } else { // if not aligned, free it, overallocate, and unmap around it - _mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (size: 0x%zx bytes, address: %p, alignment: 0x%zx, commit: %d)\n", size, p, alignment, commit); - mi_os_prim_free(p, size, commit, stats); + #if !MI_TRACK_ASAN + if (try_direct_alloc) { + _mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (size: 0x%zx bytes, address: %p, alignment: 0x%zx, commit: %d)\n", size, p, alignment, commit); + } + #endif + if (p != NULL) { mi_os_prim_free(p, size, (commit ? size : 0), NULL); } if (size >= (SIZE_MAX - alignment)) return NULL; // overflow const size_t over_size = size + alignment; if (!mi_os_mem_config.has_partial_free) { // win32 virtualAlloc cannot free parts of an allocated block // over-allocate uncommitted (virtual) memory - p = mi_os_prim_alloc(over_size, 1 /*alignment*/, false /* commit? */, false /* allow_large */, is_large, is_zero, stats); + p = mi_os_prim_alloc(over_size, 1 /*alignment*/, false /* commit? */, false /* allow_large */, &os_is_large, &os_is_zero); if (p == NULL) return NULL; // set p to the aligned part in the full region - // note: this is dangerous on Windows as VirtualFree needs the actual base pointer - // this is handled though by having the `base` field in the memid's - *base = p; // remember the base - p = mi_align_up_ptr(p, alignment); + // note: Windows VirtualFree needs the actual base pointer + // this is handled though by having the `base` field in the memid + os_base = p; // remember the base + os_size = over_size; + p = _mi_align_up_ptr(p, alignment); // explicitly commit only the aligned part if (commit) { - _mi_os_commit(p, size, NULL, stats); + if (!_mi_os_commit(p, size, NULL)) { + mi_os_prim_free(os_base, over_size, 0, NULL); + return NULL; + } } } else { // mmap can free inside an allocation // overallocate... - p = mi_os_prim_alloc(over_size, 1, commit, false, is_large, is_zero, stats); + p = mi_os_prim_alloc(over_size, 1, commit, false, &os_is_large, &os_is_zero); if (p == NULL) return NULL; - // and selectively unmap parts around the over-allocated area. - void* aligned_p = mi_align_up_ptr(p, alignment); - size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p; - size_t mid_size = _mi_align_up(size, _mi_os_page_size()); - size_t post_size = over_size - pre_size - mid_size; + // and selectively unmap parts around the over-allocated area. + void* const aligned_p = _mi_align_up_ptr(p, alignment); + const size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p; + const size_t mid_size = _mi_align_up(size, _mi_os_page_size()); + const size_t post_size = over_size - pre_size - mid_size; mi_assert_internal(pre_size < over_size&& post_size < over_size&& mid_size >= size); - if (pre_size > 0) { mi_os_prim_free(p, pre_size, commit, stats); } - if (post_size > 0) { mi_os_prim_free((uint8_t*)aligned_p + mid_size, post_size, commit, stats); } + if (pre_size > 0) { mi_os_prim_free(p, pre_size, (commit ? pre_size : 0), NULL); } + if (post_size > 0) { mi_os_prim_free((uint8_t*)aligned_p + mid_size, post_size, (commit ? post_size : 0), NULL); } // we can return the aligned pointer on `mmap` systems p = aligned_p; - *base = aligned_p; // since we freed the pre part, `*base == p`. + os_base = aligned_p; // since we freed the pre part, `*base == p`. + os_size = mid_size; } } - mi_assert_internal(p == NULL || (p != NULL && *base != NULL && ((uintptr_t)p % alignment) == 0)); + mi_assert_internal(p != NULL && os_base != NULL && _mi_is_aligned(p,alignment)); + mi_assert_internal(os_base <= p && size <= os_size); + *memid = _mi_memid_create_os(os_base,os_size,commit,os_is_zero,os_is_large); return p; } @@ -281,20 +423,22 @@ static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit OS API: alloc and alloc_aligned ----------------------------------------------------------- */ -void* _mi_os_alloc(size_t size, mi_memid_t* memid, mi_stats_t* stats) { +void* _mi_os_alloc(size_t size, mi_memid_t* memid) { *memid = _mi_memid_none(); if (size == 0) return NULL; size = _mi_os_good_alloc_size(size); bool os_is_large = false; bool os_is_zero = false; - void* p = mi_os_prim_alloc(size, 0, true, false, &os_is_large, &os_is_zero, stats); - if (p != NULL) { - *memid = _mi_memid_create_os(true, os_is_zero, os_is_large); - } + void* p = mi_os_prim_alloc(size, 0, true, false, &os_is_large, &os_is_zero); + if (p == NULL) return NULL; + + *memid = _mi_memid_create_os(p, size, true, os_is_zero, os_is_large); + mi_assert_internal(memid->mem.os.size >= size); + mi_assert_internal(memid->initially_committed); return p; } -void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, mi_memid_t* memid, mi_stats_t* stats) +void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, mi_memid_t* memid) { MI_UNUSED(&_mi_os_get_aligned_hint); // suppress unused warnings *memid = _mi_memid_none(); @@ -302,18 +446,39 @@ void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allo size = _mi_os_good_alloc_size(size); alignment = _mi_align_up(alignment, _mi_os_page_size()); - bool os_is_large = false; - bool os_is_zero = false; - void* os_base = NULL; - void* p = mi_os_prim_alloc_aligned(size, alignment, commit, allow_large, &os_is_large, &os_is_zero, &os_base, stats ); - if (p != NULL) { - *memid = _mi_memid_create_os(commit, os_is_zero, os_is_large); - memid->mem.os.base = os_base; - memid->mem.os.alignment = alignment; + void* p = mi_os_prim_alloc_aligned(size, alignment, commit, allow_large, memid ); + if (p == NULL) return NULL; + + mi_assert_internal(memid->mem.os.size >= size); + mi_assert_internal(_mi_is_aligned(p,alignment)); + if (commit) { mi_assert_internal(memid->initially_committed); } + return p; +} + + +mi_decl_nodiscard static void* mi_os_ensure_zero(void* p, size_t size, mi_memid_t* memid) { + if (p==NULL || size==0) return p; + // ensure committed + if (!memid->initially_committed) { + bool is_zero = false; + if (!_mi_os_commit(p, size, &is_zero)) { + _mi_os_free(p, size, *memid); + return NULL; + } + memid->initially_committed = true; } + // ensure zero'd + if (memid->initially_zero) return p; + _mi_memzero_aligned(p,size); + memid->initially_zero = true; return p; } +void* _mi_os_zalloc(size_t size, mi_memid_t* memid) { + void* p = _mi_os_alloc(size,memid); + return mi_os_ensure_zero(p, size, memid); +} + /* ----------------------------------------------------------- OS aligned allocation with an offset. This is used for large alignments > MI_BLOCK_ALIGNMENT_MAX. We use a large mimalloc @@ -322,28 +487,28 @@ void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allo to use the actual start of the memory region. ----------------------------------------------------------- */ -void* _mi_os_alloc_aligned_at_offset(size_t size, size_t alignment, size_t offset, bool commit, bool allow_large, mi_memid_t* memid, mi_stats_t* stats) { - mi_assert(offset <= MI_SEGMENT_SIZE); +void* _mi_os_alloc_aligned_at_offset(size_t size, size_t alignment, size_t offset, bool commit, bool allow_large, mi_memid_t* memid) { mi_assert(offset <= size); mi_assert((alignment % _mi_os_page_size()) == 0); *memid = _mi_memid_none(); - if (offset > MI_SEGMENT_SIZE) return NULL; + if (offset > size) return NULL; if (offset == 0) { // regular aligned allocation - return _mi_os_alloc_aligned(size, alignment, commit, allow_large, memid, stats); + return _mi_os_alloc_aligned(size, alignment, commit, allow_large, memid); } else { // overallocate to align at an offset const size_t extra = _mi_align_up(offset, alignment) - offset; + if (size >= SIZE_MAX - extra) return NULL; // too large const size_t oversize = size + extra; - void* const start = _mi_os_alloc_aligned(oversize, alignment, commit, allow_large, memid, stats); + void* const start = _mi_os_alloc_aligned(oversize, alignment, commit, allow_large, memid); if (start == NULL) return NULL; void* const p = (uint8_t*)start + extra; mi_assert(_mi_is_aligned((uint8_t*)p + offset, alignment)); // decommit the overallocation at the start - if (commit && extra > _mi_os_page_size()) { - _mi_os_decommit(start, extra, stats); + if (commit && extra >= _mi_os_page_size()) { + _mi_os_decommit(start, extra); } return p; } @@ -360,11 +525,11 @@ static void* mi_os_page_align_areax(bool conservative, void* addr, size_t size, if (newsize != NULL) *newsize = 0; if (size == 0 || addr == NULL) return NULL; - // page align conservatively within the range - void* start = (conservative ? mi_align_up_ptr(addr, _mi_os_page_size()) - : mi_align_down_ptr(addr, _mi_os_page_size())); - void* end = (conservative ? mi_align_down_ptr((uint8_t*)addr + size, _mi_os_page_size()) - : mi_align_up_ptr((uint8_t*)addr + size, _mi_os_page_size())); + // page align conservatively within the range, or liberally straddling pages outside the range + void* start = (conservative ? _mi_align_up_ptr(addr, _mi_os_page_size()) + : _mi_align_down_ptr(addr, _mi_os_page_size())); + void* end = (conservative ? _mi_align_down_ptr((uint8_t*)addr + size, _mi_os_page_size()) + : _mi_align_up_ptr((uint8_t*)addr + size, _mi_os_page_size())); ptrdiff_t diff = (uint8_t*)end - (uint8_t*)start; if (diff <= 0) return NULL; @@ -377,12 +542,9 @@ static void* mi_os_page_align_area_conservative(void* addr, size_t size, size_t* return mi_os_page_align_areax(true, addr, size, newsize); } -bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats) { - MI_UNUSED(tld_stats); - mi_stats_t* stats = &_mi_stats_main; +bool _mi_os_commit_ex(void* addr, size_t size, bool* is_zero, size_t stat_size) { if (is_zero != NULL) { *is_zero = false; } - _mi_stat_increase(&stats->committed, size); // use size for precise commit vs. decommit - _mi_stat_counter_increase(&stats->commit_calls, 1); + mi_os_stat_counter_increase(commit_calls, 1); // page align range size_t csize; @@ -405,14 +567,16 @@ bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats if (os_is_zero) { mi_track_mem_defined(start,csize); } else { mi_track_mem_undefined(start,csize); } #endif + mi_os_stat_increase(committed, stat_size); // use size for precise commit vs. decommit return true; } -static bool mi_os_decommit_ex(void* addr, size_t size, bool* needs_recommit, mi_stats_t* tld_stats) { - MI_UNUSED(tld_stats); - mi_stats_t* stats = &_mi_stats_main; +bool _mi_os_commit(void* addr, size_t size, bool* is_zero) { + return _mi_os_commit_ex(addr, size, is_zero, size); +} + +static bool mi_os_decommit_ex(void* addr, size_t size, bool* needs_recommit, size_t stat_size) { mi_assert_internal(needs_recommit!=NULL); - _mi_stat_decrease(&stats->committed, size); // page align size_t csize; @@ -425,13 +589,16 @@ static bool mi_os_decommit_ex(void* addr, size_t size, bool* needs_recommit, mi_ if (err != 0) { _mi_warning_message("cannot decommit OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize); } + else if (*needs_recommit) { + mi_os_stat_decrease(committed, stat_size); + } mi_assert_internal(err == 0); return (err == 0); } -bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* tld_stats) { +bool _mi_os_decommit(void* addr, size_t size) { bool needs_recommit; - return mi_os_decommit_ex(addr, size, &needs_recommit, tld_stats); + return mi_os_decommit_ex(addr, size, &needs_recommit, size); } @@ -439,13 +606,13 @@ bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* tld_stats) { // but may be used later again. This will release physical memory // pages and reduce swapping while keeping the memory committed. // We page align to a conservative area inside the range to reset. -bool _mi_os_reset(void* addr, size_t size, mi_stats_t* stats) { +bool _mi_os_reset(void* addr, size_t size) { // page align conservatively within the range size_t csize; void* start = mi_os_page_align_area_conservative(addr, size, &csize); if (csize == 0) return true; // || _mi_os_is_huge_reserved(addr) - _mi_stat_increase(&stats->reset, csize); - _mi_stat_counter_increase(&stats->reset_calls, 1); + mi_os_stat_counter_increase(reset, csize); + mi_os_stat_counter_increase(reset_calls, 1); #if (MI_DEBUG>1) && !MI_SECURE && !MI_TRACK_ENABLED // && !MI_TSAN memset(start, 0, csize); // pretend it is eagerly reset @@ -459,24 +626,39 @@ bool _mi_os_reset(void* addr, size_t size, mi_stats_t* stats) { } +void _mi_os_reuse( void* addr, size_t size ) { + // page align conservatively within the range + size_t csize = 0; + void* const start = mi_os_page_align_area_conservative(addr, size, &csize); + if (csize == 0) return; + const int err = _mi_prim_reuse(start, csize); + if (err != 0) { + _mi_warning_message("cannot reuse OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize); + } +} + // either resets or decommits memory, returns true if the memory needs // to be recommitted if it is to be re-used later on. -bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, mi_stats_t* stats) +bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, size_t stat_size, mi_commit_fun_t* commit_fun, void* commit_fun_arg) { if (mi_option_get(mi_option_purge_delay) < 0) return false; // is purging allowed? - _mi_stat_counter_increase(&stats->purge_calls, 1); - _mi_stat_increase(&stats->purged, size); + mi_os_stat_counter_increase(purge_calls, 1); + mi_os_stat_counter_increase(purged, size); - if (mi_option_is_enabled(mi_option_purge_decommits) && // should decommit? - !_mi_preloading()) // don't decommit during preloading (unsafe) + if (commit_fun != NULL) { + bool decommitted = (*commit_fun)(false, p, size, NULL, commit_fun_arg); + return decommitted; // needs_recommit? + } + else if (mi_option_is_enabled(mi_option_purge_decommits) && // should decommit? + !_mi_preloading()) // don't decommit during preloading (unsafe) { bool needs_recommit = true; - mi_os_decommit_ex(p, size, &needs_recommit, stats); + mi_os_decommit_ex(p, size, &needs_recommit, stat_size); return needs_recommit; } else { - if (allow_reset) { // this can sometimes be not allowed if the range is not fully committed - _mi_os_reset(p, size, stats); + if (allow_reset) { // this can sometimes be not allowed if the range is not fully committed (on Windows, we cannot reset uncommitted memory) + _mi_os_reset(p, size); } return false; // needs no recommit } @@ -484,10 +666,11 @@ bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, mi_stats_t* stats) // either resets or decommits memory, returns true if the memory needs // to be recommitted if it is to be re-used later on. -bool _mi_os_purge(void* p, size_t size, mi_stats_t * stats) { - return _mi_os_purge_ex(p, size, true, stats); +bool _mi_os_purge(void* p, size_t size) { + return _mi_os_purge_ex(p, size, true, size, NULL, NULL); } + // Protect a region in memory to be not accessible. static bool mi_os_protectx(void* addr, size_t size, bool protect) { // page align conservatively within the range @@ -539,15 +722,20 @@ static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) { start = huge_start; if (start == 0) { // Initialize the start address after the 32TiB area - start = ((uintptr_t)32 << 40); // 32TiB virtual start address - #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of huge pages unless in debug mode - uintptr_t r = _mi_heap_random_next(mi_prim_get_default_heap()); - start = start + ((uintptr_t)MI_HUGE_OS_PAGE_SIZE * ((r>>17) & 0x0FFF)); // (randomly 12bits)*1GiB == between 0 to 4TiB - #endif + start = ((uintptr_t)8 << 40); // 8TiB virtual start address + #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of huge pages unless in debug mode + mi_theap_t* const theap = _mi_theap_default(); // don't use `mi_theap_get_default()` as that can cause allocation recursively (issue #1267) + if (mi_theap_is_initialized(theap)) { // todo: or no hint at all if we lack randomness? + const uintptr_t r = _mi_theap_random_next(theap); + start = start + ((uintptr_t)MI_HUGE_OS_PAGE_SIZE * ((r>>17) & 0x0FFF)); // (randomly 12bits)*1GiB == between 0 to 4TiB + } + else { + _mi_warning_message("failed to randomize the start address of huge pages allocation (%zu bytes at %p)", size, (void*)start); + } + #endif } end = start + size; - mi_assert_internal(end % MI_SEGMENT_SIZE == 0); - } while (!mi_atomic_cas_strong_acq_rel(&mi_huge_start, &huge_start, end)); + } while (!mi_atomic_cas_weak_acq_rel(&mi_huge_start, &huge_start, end)); if (total_size != NULL) *total_size = size; return (uint8_t*)start; @@ -560,13 +748,13 @@ static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) { } #endif -// Allocate MI_SEGMENT_SIZE aligned huge pages +// Allocate MI_ARENA_SLICE_ALIGN aligned huge pages void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_msecs, size_t* pages_reserved, size_t* psize, mi_memid_t* memid) { *memid = _mi_memid_none(); if (psize != NULL) *psize = 0; if (pages_reserved != NULL) *pages_reserved = 0; size_t size = 0; - uint8_t* start = mi_os_claim_huge_pages(pages, &size); + uint8_t* const start = mi_os_claim_huge_pages(pages, &size); if (start == NULL) return NULL; // or 32-bit systems // Allocate one page at the time but try to place them contiguously @@ -592,15 +780,15 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse // no success, issue a warning and break if (p != NULL) { _mi_warning_message("could not allocate contiguous huge OS page %zu at %p\n", page, addr); - mi_os_prim_free(p, MI_HUGE_OS_PAGE_SIZE, true, &_mi_stats_main); + mi_os_prim_free(p, MI_HUGE_OS_PAGE_SIZE, MI_HUGE_OS_PAGE_SIZE, NULL); } break; } // success, record it page++; // increase before timeout check (see issue #711) - _mi_stat_increase(&_mi_stats_main.committed, MI_HUGE_OS_PAGE_SIZE); - _mi_stat_increase(&_mi_stats_main.reserved, MI_HUGE_OS_PAGE_SIZE); + mi_os_stat_increase(committed, MI_HUGE_OS_PAGE_SIZE); + mi_os_stat_increase(reserved, MI_HUGE_OS_PAGE_SIZE); // check for timeout if (max_msecs > 0) { @@ -617,16 +805,17 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse } } } - mi_assert_internal(page*MI_HUGE_OS_PAGE_SIZE <= size); + const size_t allocated = page * MI_HUGE_OS_PAGE_SIZE; + mi_assert_internal(allocated <= size); if (pages_reserved != NULL) { *pages_reserved = page; } - if (psize != NULL) { *psize = page * MI_HUGE_OS_PAGE_SIZE; } + if (psize != NULL) { *psize = allocated; } if (page != 0) { mi_assert(start != NULL); - *memid = _mi_memid_create_os(true /* is committed */, all_zero, true /* is_large */); + *memid = _mi_memid_create_os(start, allocated, true /* is committed */, all_zero, true /* is_large */); memid->memkind = MI_MEM_OS_HUGE; mi_assert(memid->is_pinned); #ifdef MI_TRACK_ASAN - if (all_zero) { mi_track_mem_defined(start,size); } + if (all_zero) { mi_track_mem_defined(start,allocated); } #endif } return (page == 0 ? NULL : start); @@ -634,45 +823,103 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse // free every huge page in a range individually (as we allocated per page) // note: needed with VirtualAlloc but could potentially be done in one go on mmap'd systems. -static void mi_os_free_huge_os_pages(void* p, size_t size, mi_stats_t* stats) { +static void mi_os_free_huge_os_pages(void* p, size_t size, mi_subproc_t* subproc) { if (p==NULL || size==0) return; uint8_t* base = (uint8_t*)p; while (size >= MI_HUGE_OS_PAGE_SIZE) { - mi_os_prim_free(base, MI_HUGE_OS_PAGE_SIZE, true, stats); + mi_os_prim_free(base, MI_HUGE_OS_PAGE_SIZE, MI_HUGE_OS_PAGE_SIZE, subproc); size -= MI_HUGE_OS_PAGE_SIZE; base += MI_HUGE_OS_PAGE_SIZE; } } + /* ---------------------------------------------------------------------------- Support NUMA aware allocation -----------------------------------------------------------------------------*/ -_Atomic(size_t) _mi_numa_node_count; // = 0 // cache the node count +static _Atomic(size_t) mi_numa_node_count; // = 0 // cache the node count -size_t _mi_os_numa_node_count_get(void) { - size_t count = mi_atomic_load_acquire(&_mi_numa_node_count); - if (count <= 0) { +int _mi_os_numa_node_count(void) { + size_t count = mi_atomic_load_acquire(&mi_numa_node_count); + if mi_unlikely(count == 0) { long ncount = mi_option_get(mi_option_use_numa_nodes); // given explicitly? - if (ncount > 0) { + if (ncount > 0 && ncount < INT_MAX) { count = (size_t)ncount; } else { - count = _mi_prim_numa_node_count(); // or detect dynamically - if (count == 0) count = 1; + const size_t n = _mi_prim_numa_node_count(); // or detect dynamically + if (n == 0 || n > INT_MAX) { count = 1; } + else { count = n; } } - mi_atomic_store_release(&_mi_numa_node_count, count); // save it - _mi_verbose_message("using %zd numa regions\n", count); + mi_atomic_store_release(&mi_numa_node_count, count); // save it + if (count>1) { _mi_verbose_message("using %zd numa regions\n", count); } } - return count; + mi_assert_internal(count > 0 && count <= INT_MAX); + return (int)count; } -int _mi_os_numa_node_get(mi_os_tld_t* tld) { - MI_UNUSED(tld); - size_t numa_count = _mi_os_numa_node_count(); +static int mi_os_numa_node_get(void) { + int numa_count = _mi_os_numa_node_count(); if (numa_count<=1) return 0; // optimize on single numa node systems: always node 0 // never more than the node count and >= 0 - size_t numa_node = _mi_prim_numa_node(); + const size_t n = _mi_prim_numa_node(); + int numa_node = (n < INT_MAX ? (int)n : 0); if (numa_node >= numa_count) { numa_node = numa_node % numa_count; } - return (int)numa_node; + return numa_node; } + +int _mi_os_numa_node(void) { + if mi_likely(mi_atomic_load_relaxed(&mi_numa_node_count) == 1) { + return 0; + } + else { + return mi_os_numa_node_get(); + } +} + + +/* ---------------------------------------------------------------------------- + Public API +-----------------------------------------------------------------------------*/ +#if 0 +mi_decl_export void* mi_os_alloc(size_t size, bool commit, size_t* full_size) { + return mi_os_alloc_aligned(size, mi_os_mem_config.alloc_granularity, commit, NULL, full_size); +} + +static void* mi_os_alloc_aligned_ex(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_committed, bool* is_pinned, void** base, size_t* full_size) { + mi_memid_t memid = _mi_memid_none(); + void* p = _mi_os_alloc_aligned(size, alignment, commit, allow_large, &memid); + if (p == NULL) return p; + if (is_committed != NULL) { *is_committed = memid.initially_committed; } + if (is_pinned != NULL) { *is_pinned = memid.is_pinned; } + if (base != NULL) { *base = memid.mem.os.base; } + if (full_size != NULL) { *full_size = memid.mem.os.size; } + if (!memid.initially_zero && memid.initially_committed) { + _mi_memzero_aligned(memid.mem.os.base, memid.mem.os.size); + } + return p; +} + +mi_decl_export void* mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, void** base, size_t* full_size) { + return mi_os_alloc_aligned_ex(size, alignment, commit, false, NULL, NULL, base, full_size); +} + +mi_decl_export void* mi_os_alloc_aligned_allow_large(size_t size, size_t alignment, bool commit, bool* is_committed, bool* is_pinned, void** base, size_t* full_size) { + return mi_os_alloc_aligned_ex(size, alignment, commit, true, is_committed, is_pinned, base, full_size); +} + +mi_decl_export void mi_os_free(void* p, size_t size) { + if (p==NULL || size == 0) return; + mi_memid_t memid = _mi_memid_create_os(p, size, true, false, false); + _mi_os_free(p, size, memid); +} + +mi_decl_export void mi_os_commit(void* p, size_t size) { + _mi_os_commit(p, size, NULL); +} + +mi_decl_export void mi_os_decommit(void* p, size_t size) { + _mi_os_decommit(p, size); +} +#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-map.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-map.c new file mode 100644 index 00000000..b213eb09 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-map.c @@ -0,0 +1,448 @@ +/*---------------------------------------------------------------------------- +Copyright (c) 2023-2025, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "bitmap.h" + +static void mi_page_map_cannot_commit(void) { + _mi_warning_message("unable to commit the allocation page-map on-demand\n" ); +} + +#if MI_PAGE_MAP_FLAT + +// The page-map contains a byte for each 64kb slice in the address space. +// For an address `a` where `ofs = _mi_page_map[a >> 16]`: +// 0 = unused +// 1 = the slice at `a & ~0xFFFF` is a mimalloc page. +// 1 < ofs <= 127 = the slice is part of a page, starting at `(((a>>16) - ofs - 1) << 16)`. +// +// 1 byte per slice => 1 TiB address space needs a 2^14 * 2^16 = 16 MiB page map. +// A full 256 TiB address space (48 bit) needs a 4 GiB page map. +// A full 4 GiB address space (32 bit) needs only a 64 KiB page map. + +mi_decl_cache_align uint8_t* _mi_page_map = NULL; +static void* mi_page_map_max_address = NULL; +static mi_memid_t mi_page_map_memid; + +#define MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT MI_ARENA_SLICE_SIZE +static mi_bitmap_t* mi_page_map_commit; // one bit per committed 64 KiB entries + +mi_decl_nodiscard static bool mi_page_map_ensure_committed(size_t idx, size_t slice_count); + +bool _mi_page_map_init(void) { + size_t vbits = (size_t)mi_option_get_clamp(mi_option_max_vabits, 0, MI_SIZE_BITS); + if (vbits == 0) { + vbits = _mi_os_virtual_address_bits(); + #if MI_ARCH_X64 // canonical address is limited to the first 128 TiB + if (vbits >= 48) { vbits = 47; } + #endif + } + if (vbits < MI_ARENA_SLICE_SHIFT) { + vbits = MI_ARENA_SLICE_SHIFT; + } + + // Allocate the page map and commit bits + mi_page_map_max_address = (void*)(vbits >= MI_SIZE_BITS ? (SIZE_MAX - MI_ARENA_SLICE_SIZE + 1) : (MI_PU(1) << vbits)); + const size_t page_map_size = (MI_ZU(1) << (vbits - MI_ARENA_SLICE_SHIFT)); + const bool commit = (page_map_size <= 1*MI_MiB || mi_option_is_enabled(mi_option_pagemap_commit)); // _mi_os_has_overcommit(); // commit on-access on Linux systems? + const size_t commit_bits = _mi_divide_up(page_map_size, MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT); + const size_t bitmap_size = (commit ? 0 : mi_bitmap_size(commit_bits, NULL)); + const size_t reserve_size = bitmap_size + page_map_size; + uint8_t* const base = (uint8_t*)_mi_os_alloc_aligned(reserve_size, 1, commit, true /* allow large */, &mi_page_map_memid); + if (base==NULL) { + _mi_error_message(ENOMEM, "unable to reserve virtual memory for the page map (%zu KiB)\n", page_map_size / MI_KiB); + return false; + } + if (mi_page_map_memid.initially_committed && !mi_page_map_memid.initially_zero) { + _mi_warning_message("internal: the page map was committed but not zero initialized!\n"); + _mi_memzero_aligned(base, reserve_size); + } + if (bitmap_size > 0) { + mi_page_map_commit = (mi_bitmap_t*)base; + if (!_mi_os_commit(mi_page_map_commit, bitmap_size, NULL)) { + mi_page_map_cannot_commit(); + return false; + } + mi_bitmap_init(mi_page_map_commit, commit_bits, true); + } + _mi_page_map = base + bitmap_size; + + // commit the first part so NULL pointers get resolved without an access violation + if (!commit) { + if (!mi_page_map_ensure_committed(0, 1)) { + mi_page_map_cannot_commit(); + return false; + } + } + _mi_page_map[0] = 1; // so _mi_ptr_page(NULL) == NULL + mi_assert_internal(_mi_ptr_page(NULL)==NULL); + return true; +} + +void _mi_page_map_unsafe_destroy(mi_subproc_t* subproc) { + mi_assert_internal(subproc != NULL); + mi_assert_internal(_mi_page_map != NULL); + if (_mi_page_map == NULL) return; + _mi_os_free_ex(mi_page_map_memid.mem.os.base, mi_page_map_memid.mem.os.size, true, mi_page_map_memid, subproc); + _mi_page_map = NULL; + mi_page_map_commit = NULL; + mi_page_map_max_address = NULL; + mi_page_map_memid = _mi_memid_none(); +} + + +static bool mi_page_map_ensure_committed(size_t idx, size_t slice_count) { + // is the page map area that contains the page address committed? + // we always set the commit bits so we can track what ranges are in-use. + // we only actually commit if the map wasn't committed fully already. + if (mi_page_map_commit != NULL) { + const size_t commit_idx = idx / MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT; + const size_t commit_idx_hi = (idx + slice_count - 1) / MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT; + for (size_t i = commit_idx; i <= commit_idx_hi; i++) { // per bit to avoid crossing over bitmap chunks + if (mi_bitmap_is_clear(mi_page_map_commit, i)) { + // this may race, in which case we do multiple commits (which is ok) + bool is_zero; + uint8_t* const start = _mi_page_map + (i * MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT); + const size_t size = MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT; + if (!_mi_os_commit(start, size, &is_zero)) { + mi_page_map_cannot_commit(); + return false; + } + if (!is_zero && !mi_page_map_memid.initially_zero) { _mi_memzero(start, size); } + mi_bitmap_set(mi_page_map_commit, i); + } + } + } + #if MI_DEBUG > 0 + _mi_page_map[idx] = 0; + _mi_page_map[idx+slice_count-1] = 0; + #endif + return true; +} + + +static size_t mi_page_map_get_idx(mi_page_t* page, uint8_t** page_start, size_t* slice_count) { + size_t page_size; + *page_start = mi_page_area(page, &page_size); + if (page_size > MI_LARGE_PAGE_SIZE) { page_size = MI_LARGE_PAGE_SIZE - MI_ARENA_SLICE_SIZE; } // furthest interior pointer + *slice_count = mi_slice_count_of_size(page_size) + ((*page_start - mi_page_slice_start(page))/MI_ARENA_SLICE_SIZE); // add for large aligned blocks + return _mi_page_map_index(page); +} + +bool _mi_page_map_register(mi_page_t* page) { + mi_assert_internal(page != NULL); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_page_map != NULL); // should be initialized before multi-thread access! + if mi_unlikely(_mi_page_map == NULL) { + if (!_mi_page_map_init()) return false; + } + mi_assert(_mi_page_map!=NULL); + uint8_t* page_start; + size_t slice_count; + const size_t idx = mi_page_map_get_idx(page, &page_start, &slice_count); + + if (!mi_page_map_ensure_committed(idx, slice_count)) { + return false; + } + + // set the offsets + for (size_t i = 0; i < slice_count; i++) { + mi_assert_internal(i < 128); + _mi_page_map[idx + i] = (uint8_t)(i+1); + } + return true; +} + +void _mi_page_map_unregister(mi_page_t* page) { + mi_assert_internal(_mi_page_map != NULL); + // get index and count + uint8_t* page_start; + size_t slice_count; + const size_t idx = mi_page_map_get_idx(page, &page_start, &slice_count); + // unset the offsets + _mi_memzero(_mi_page_map + idx, slice_count); +} + +void _mi_page_map_unregister_range(void* start, size_t size) { + const size_t slice_count = _mi_divide_up(size, MI_ARENA_SLICE_SIZE); + const uintptr_t index = _mi_page_map_index(start); + // todo: scan the commit bits and clear only those ranges? + if (!mi_page_map_ensure_committed(index, slice_count)) { // we commit the range in total; + return; + } + _mi_memzero(&_mi_page_map[index], slice_count); +} + + +mi_page_t* _mi_safe_ptr_page(const void* p) { + if mi_unlikely(p >= mi_page_map_max_address) return NULL; + const uintptr_t idx = _mi_page_map_index(p); + if mi_unlikely(mi_page_map_commit != NULL && !mi_bitmap_is_set(mi_page_map_commit, idx/MI_PAGE_MAP_ENTRIES_PER_COMMIT_BIT)) return NULL; + const uintptr_t ofs = _mi_page_map[idx]; + if mi_unlikely(ofs == 0) return NULL; + return (mi_page_t*)((((uintptr_t)p >> MI_ARENA_SLICE_SHIFT) - ofs + 1) << MI_ARENA_SLICE_SHIFT); +} + +mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { + return (_mi_safe_ptr_page(p) != NULL); +} + +#else + +// A 2-level page map +#define MI_PAGE_MAP_SUB_SIZE (MI_PAGE_MAP_SUB_COUNT * sizeof(mi_page_t*)) +#define MI_PAGE_MAP_ENTRIES_PER_CBIT (MI_PAGE_MAP_COUNT < MI_BFIELD_BITS ? 1 : (MI_PAGE_MAP_COUNT / MI_BFIELD_BITS)) + +mi_decl_cache_align _Atomic(mi_submap_t)* _mi_page_map; +static size_t mi_page_map_count; +static void* mi_page_map_max_address; +static mi_memid_t mi_page_map_memid; +static mi_lock_t mi_page_map_lock; + +// divide the main map in 64 (`MI_BFIELD_BITS`) parts commit those parts on demand +static _Atomic(mi_bfield_t) mi_page_map_commit; + +mi_decl_nodiscard static inline bool mi_page_map_is_committed(size_t idx, size_t* pbit_idx) { + mi_bfield_t commit = mi_atomic_load_relaxed(&mi_page_map_commit); + const size_t bit_idx = idx/MI_PAGE_MAP_ENTRIES_PER_CBIT; + mi_assert_internal(bit_idx < MI_BFIELD_BITS); + if (pbit_idx != NULL) { *pbit_idx = bit_idx; } + return ((commit & (MI_ZU(1) << bit_idx)) != 0); +} + +mi_decl_nodiscard static bool mi_page_map_ensure_committed(size_t idx, mi_submap_t* submap) { + mi_assert_internal(submap!=NULL && *submap==NULL); + size_t bit_idx; + if mi_unlikely(!mi_page_map_is_committed(idx, &bit_idx)) { + uint8_t* start = (uint8_t*)&_mi_page_map[bit_idx * MI_PAGE_MAP_ENTRIES_PER_CBIT]; + if (!_mi_os_commit(start, MI_PAGE_MAP_ENTRIES_PER_CBIT * sizeof(mi_submap_t), NULL)) { + mi_page_map_cannot_commit(); + return false; + } + mi_atomic_or_acq_rel(&mi_page_map_commit, MI_ZU(1) << bit_idx); + } + *submap = mi_atomic_load_ptr_acquire(mi_page_t*, &_mi_page_map[idx]); // acquire _mi_page_map_at(idx); + return true; +} + +// initialize the page map +bool _mi_page_map_init(void) { + size_t vbits = (size_t)mi_option_get_clamp(mi_option_max_vabits, 0, MI_SIZE_BITS); + if (vbits == 0) { + vbits = _mi_os_virtual_address_bits(); + #if MI_ARCH_X64 // canonical address is limited to the first 128 TiB + if (vbits >= 48) { vbits = 47; } + #endif + } + if (vbits < MI_PAGE_MAP_SUB_SHIFT + MI_ARENA_SLICE_SHIFT) { + vbits = MI_PAGE_MAP_SUB_SHIFT + MI_ARENA_SLICE_SHIFT; + } + + // Allocate the page map and commit bits + mi_assert(MI_MAX_VABITS >= vbits); + mi_page_map_max_address = (void*)(vbits >= MI_SIZE_BITS ? (SIZE_MAX - MI_ARENA_SLICE_SIZE + 1) : (MI_PU(1) << vbits)); + mi_page_map_count = (MI_ZU(1) << (vbits - MI_PAGE_MAP_SUB_SHIFT - MI_ARENA_SLICE_SHIFT)); + mi_assert(mi_page_map_count <= MI_PAGE_MAP_COUNT); + const size_t os_page_size = _mi_os_page_size(); + const size_t page_map_size = _mi_align_up( mi_page_map_count * sizeof(mi_page_t**), os_page_size); + const size_t submap_size = MI_PAGE_MAP_SUB_SIZE; + const size_t reserve_size = page_map_size + submap_size; + #if MI_SECURE + const bool commit = true; // the whole page map is valid and we can reliably check any pointer + #else + const bool commit = page_map_size <= 64*MI_KiB || + mi_option_is_enabled(mi_option_pagemap_commit) || _mi_os_has_overcommit(); + #endif + _mi_page_map = (_Atomic(mi_page_t**)*)_mi_os_alloc_aligned(reserve_size, 1, commit, true /* allow large */, &mi_page_map_memid); + if (_mi_page_map==NULL) { + _mi_error_message(ENOMEM, "unable to reserve virtual memory for the page map (%zu KiB)\n", page_map_size / MI_KiB); + return false; + } + if (mi_page_map_memid.initially_committed && !mi_page_map_memid.initially_zero) { + _mi_warning_message("internal: the page map was committed but not zero initialized!\n"); + _mi_memzero_aligned(_mi_page_map, page_map_size); + } + mi_atomic_store_release(&mi_page_map_commit, (mi_page_map_memid.initially_committed ? ~MI_ZU(0) : MI_ZU(0))); + + // ensure there is a submap for the NULL address + mi_page_t** const sub0 = (mi_page_t**)((uint8_t*)_mi_page_map + page_map_size); // we reserved a submap part at the end already + if (!mi_page_map_memid.initially_committed) { + if (!_mi_os_commit(sub0, submap_size, NULL)) { // commit full submap (issue #1087) + mi_page_map_cannot_commit(); + return false; + } + } + if (!mi_page_map_memid.initially_zero) { // initialize low addresses with NULL + _mi_memzero_aligned(sub0, submap_size); + } + mi_submap_t nullsub = NULL; + if (!mi_page_map_ensure_committed(0,&nullsub)) { + mi_page_map_cannot_commit(); + return false; + } + mi_atomic_store_ptr_release(mi_page_t*, &_mi_page_map[0], sub0); + mi_lock_init(&mi_page_map_lock); // initialize late in case the lock init causes allocation + + mi_assert_internal(_mi_ptr_page(NULL)==NULL); + return true; +} + + +void _mi_page_map_unsafe_destroy(mi_subproc_t* subproc) { + mi_assert_internal(subproc != NULL); + mi_assert_internal(_mi_page_map != NULL); + if (_mi_page_map == NULL) return; + mi_lock_done(&mi_page_map_lock); + for (size_t idx = 1; idx < mi_page_map_count; idx++) { // skip entry 0 (as we allocate that submap at the end of the page_map) + // free all sub-maps + if (mi_page_map_is_committed(idx, NULL)) { + mi_submap_t sub = _mi_page_map_at(idx); + if (sub != NULL) { + mi_memid_t memid = _mi_memid_create_os(sub, MI_PAGE_MAP_SUB_SIZE, true, false, false); + _mi_os_free_ex(memid.mem.os.base, memid.mem.os.size, true, memid, subproc); + mi_atomic_store_ptr_release(mi_page_t*, &_mi_page_map[idx], NULL); + } + } + } + _mi_os_free_ex(_mi_page_map, mi_page_map_memid.mem.os.size, true, mi_page_map_memid, subproc); + _mi_page_map = NULL; + mi_page_map_count = 0; + mi_page_map_memid = _mi_memid_none(); + mi_page_map_max_address = NULL; + mi_atomic_store_release(&mi_page_map_commit, (mi_bfield_t)0); +} + + +mi_decl_nodiscard static bool mi_page_map_ensure_submap_at(size_t idx, mi_submap_t* submap) { + mi_assert_internal(submap!=NULL && *submap==NULL); + mi_submap_t sub = NULL; + if (!mi_page_map_ensure_committed(idx, &sub)) { + return false; + } + if mi_unlikely(sub == NULL) { + // sub map not yet allocated, alloc now + mi_lock(&mi_page_map_lock) + { + sub = mi_atomic_load_ptr_acquire(mi_page_t*, &_mi_page_map[idx]); // reload + if (sub==NULL) // not yet allocated by another thread? + { + mi_memid_t memid; + const size_t submap_size = MI_PAGE_MAP_SUB_SIZE; + sub = (mi_submap_t)_mi_os_zalloc(submap_size, &memid); + if (sub==NULL) { + _mi_warning_message("internal error: unable to extend the page map\n"); + } + else { + mi_submap_t expect = NULL; + if (!mi_atomic_cas_ptr_strong_acq_rel(mi_page_t*, &_mi_page_map[idx], &expect, sub)) { + // another thread already allocated it.. free and continue + _mi_os_free(sub, submap_size, memid); + sub = expect; + } + } + } + } + if (sub==NULL) return false; // unable to allocate the submap.. + } + mi_assert_internal(sub!=NULL); + *submap = sub; + return true; +} + +static bool mi_page_map_set_range_prim(mi_page_t* page, size_t idx, size_t sub_idx, size_t slice_count) { + // is the page map area that contains the page address committed? + while (slice_count > 0) { + mi_submap_t sub = NULL; + if (!mi_page_map_ensure_submap_at(idx, &sub)) { + return false; + }; + mi_assert_internal(sub!=NULL); + // set the offsets for the page + while (slice_count > 0 && sub_idx < MI_PAGE_MAP_SUB_COUNT) { + sub[sub_idx] = page; + slice_count--; + sub_idx++; + } + idx++; // potentially wrap around to the next idx + sub_idx = 0; + } + return true; +} + +static bool mi_page_map_set_range(mi_page_t* page, size_t idx, size_t sub_idx, size_t slice_count) { + if mi_unlikely(!mi_page_map_set_range_prim(page,idx,sub_idx,slice_count)) { + // failed to commit, call again to reset the page pointer if needed + if (page!=NULL) { + mi_page_map_set_range_prim(NULL,idx,sub_idx,slice_count); + } + return false; + } + return true; +} + +static size_t mi_page_map_get_idx(mi_page_t* page, size_t* sub_idx, size_t* slice_count) { + size_t page_size; + uint8_t* page_start = mi_page_area(page, &page_size); + if (page_size > MI_LARGE_PAGE_SIZE) { page_size = MI_LARGE_PAGE_SIZE - MI_ARENA_SLICE_SIZE; } // furthest interior pointer + *slice_count = mi_slice_count_of_size(page_size) + ((page_start - mi_page_slice_start(page))/MI_ARENA_SLICE_SIZE); // add for large aligned blocks + return _mi_page_map_index(page_start, sub_idx); +} + +bool _mi_page_map_register(mi_page_t* page) { + mi_assert_internal(page != NULL); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_page_map != NULL); // should be initialized before multi-thread access! + if mi_unlikely(_mi_page_map == NULL) { + if (!_mi_page_map_init()) return false; + } + mi_assert(_mi_page_map!=NULL); + size_t slice_count; + size_t sub_idx; + const size_t idx = mi_page_map_get_idx(page, &sub_idx, &slice_count); + return mi_page_map_set_range(page, idx, sub_idx, slice_count); +} + +void _mi_page_map_unregister(mi_page_t* page) { + mi_assert_internal(_mi_page_map != NULL); + mi_assert_internal(page != NULL); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + // note: should proceed even if the page was not registered yet (for failure paths in page allocation in `arena.c`) + if mi_unlikely(_mi_page_map == NULL) return; + // get index and count + size_t slice_count; + size_t sub_idx; + const size_t idx = mi_page_map_get_idx(page, &sub_idx, &slice_count); + // unset the offsets + mi_page_map_set_range(NULL, idx, sub_idx, slice_count); +} + +void _mi_page_map_unregister_range(void* start, size_t size) { + if mi_unlikely(_mi_page_map == NULL) return; + const size_t slice_count = _mi_divide_up(size, MI_ARENA_SLICE_SIZE); + size_t sub_idx; + const uintptr_t idx = _mi_page_map_index(start, &sub_idx); + mi_page_map_set_range(NULL, idx, sub_idx, slice_count); // todo: avoid committing if not already committed? +} + +// Return NULL for invalid pointers +mi_page_t* _mi_safe_ptr_page(const void* p) { + if (p==NULL) return NULL; + if mi_unlikely(p >= mi_page_map_max_address) return NULL; + size_t sub_idx; + const size_t idx = _mi_page_map_index(p,&sub_idx); + if mi_unlikely(!mi_page_map_is_committed(idx,NULL)) return NULL; + mi_page_t** const sub = _mi_page_map[idx]; + if mi_unlikely(sub==NULL) return NULL; + return sub[sub_idx]; +} + +mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { + return (_mi_safe_ptr_page(p) != NULL); +} + +#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-queue.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-queue.c index ceea91ee..8467f7ac 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-queue.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page-queue.c @@ -38,15 +38,19 @@ terms of the MIT license. A copy of the license can be found in the file static inline bool mi_page_queue_is_huge(const mi_page_queue_t* pq) { - return (pq->block_size == (MI_MEDIUM_OBJ_SIZE_MAX+sizeof(uintptr_t))); + return (pq->block_size == (MI_LARGE_MAX_OBJ_SIZE+sizeof(uintptr_t))); } static inline bool mi_page_queue_is_full(const mi_page_queue_t* pq) { - return (pq->block_size == (MI_MEDIUM_OBJ_SIZE_MAX+(2*sizeof(uintptr_t)))); + return (pq->block_size == (MI_LARGE_MAX_OBJ_SIZE+(2*sizeof(uintptr_t)))); } static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) { - return (pq->block_size > MI_MEDIUM_OBJ_SIZE_MAX); + return (pq->block_size > MI_LARGE_MAX_OBJ_SIZE); +} + +static inline size_t mi_page_queue_count(const mi_page_queue_t* pq) { + return pq->count; } /* ----------------------------------------------------------- @@ -57,27 +61,23 @@ static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) { // Returns MI_BIN_HUGE if the size is too large. // We use `wsize` for the size in "machine word sizes", // i.e. byte size == `wsize*sizeof(void*)`. -static inline uint8_t mi_bin(size_t size) { +static mi_decl_noinline size_t mi_bin(size_t size) { size_t wsize = _mi_wsize_from_size(size); - uint8_t bin; - if (wsize <= 1) { - bin = 1; +#if defined(MI_ALIGN4W) + if mi_likely(wsize <= 4) { + return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes } - #if defined(MI_ALIGN4W) - else if (wsize <= 4) { - bin = (uint8_t)((wsize+1)&~1); // round to double word sizes +#elif defined(MI_ALIGN2W) + if mi_likely(wsize <= 8) { + return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes } - #elif defined(MI_ALIGN2W) - else if (wsize <= 8) { - bin = (uint8_t)((wsize+1)&~1); // round to double word sizes - } - #else - else if (wsize <= 8) { - bin = (uint8_t)wsize; +#else + if mi_likely(wsize <= 8) { + return (wsize == 0 ? 1 : wsize); } - #endif - else if (wsize > MI_MEDIUM_OBJ_WSIZE_MAX) { - bin = MI_BIN_HUGE; +#endif + else if mi_unlikely(wsize > MI_LARGE_MAX_OBJ_WSIZE) { + return MI_BIN_HUGE; } else { #if defined(MI_ALIGN4W) @@ -85,15 +85,14 @@ static inline uint8_t mi_bin(size_t size) { #endif wsize--; // find the highest bit - uint8_t b = (uint8_t)mi_bsr(wsize); // note: wsize != 0 + const size_t b = (MI_SIZE_BITS - 1 - mi_clz(wsize)); // note: wsize != 0 // and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation). // - adjust with 3 because we use do not round the first 8 sizes // which each get an exact bin - bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3; - mi_assert_internal(bin < MI_BIN_HUGE); + const size_t bin = ((b << 2) + ((wsize >> (b - 2)) & 0x03)) - 3; + mi_assert_internal(bin > 0 && bin < MI_BIN_HUGE); + return bin; } - mi_assert_internal(bin > 0 && bin <= MI_BIN_HUGE); - return bin; } @@ -102,22 +101,26 @@ static inline uint8_t mi_bin(size_t size) { Queue of pages with free blocks ----------------------------------------------------------- */ -uint8_t _mi_bin(size_t size) { +size_t _mi_bin(size_t size) { return mi_bin(size); } -size_t _mi_bin_size(uint8_t bin) { - return _mi_heap_empty.pages[bin].block_size; +size_t _mi_bin_size(size_t bin) { + mi_assert_internal(bin <= MI_BIN_HUGE); + return _mi_theap_empty.pages[bin].block_size; } // Good size for allocation -size_t mi_good_size(size_t size) mi_attr_noexcept { - if (size <= MI_MEDIUM_OBJ_SIZE_MAX) { +mi_decl_nodiscard mi_decl_export size_t mi_good_size(size_t size) mi_attr_noexcept { + if (size <= MI_LARGE_MAX_OBJ_SIZE - MI_PADDING_SIZE) { return _mi_bin_size(mi_bin(size + MI_PADDING_SIZE)); } - else { + else if (size <= MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) { return _mi_align_up(size + MI_PADDING_SIZE,_mi_os_page_size()); } + else { + return size; + } } #if (MI_DEBUG>1) @@ -136,29 +139,64 @@ static bool mi_page_queue_contains(mi_page_queue_t* queue, const mi_page_t* page #endif #if (MI_DEBUG>1) -static bool mi_heap_contains_queue(const mi_heap_t* heap, const mi_page_queue_t* pq) { - return (pq >= &heap->pages[0] && pq <= &heap->pages[MI_BIN_FULL]); +static bool mi_theap_contains_queue(const mi_theap_t* theap, const mi_page_queue_t* pq) { + return (pq >= &theap->pages[0] && pq <= &theap->pages[MI_BIN_FULL]); } #endif -static inline bool mi_page_is_large_or_huge(const mi_page_t* page) { - return (mi_page_block_size(page) > MI_MEDIUM_OBJ_SIZE_MAX || mi_page_is_huge(page)); +bool _mi_page_queue_is_valid(mi_theap_t* theap, const mi_page_queue_t* pq) { + MI_UNUSED_RELEASE(theap); + if (pq==NULL) return false; + size_t count = 0; MI_UNUSED_RELEASE(count); + mi_page_t* prev = NULL; MI_UNUSED_RELEASE(prev); + for (mi_page_t* page = pq->first; page != NULL; page = page->next) { + mi_assert_internal(page->prev == prev); + if (mi_page_is_in_full(page)) { + mi_assert_internal(_mi_wsize_from_size(pq->block_size) == MI_LARGE_MAX_OBJ_WSIZE + 2); + } + else if (mi_page_is_huge(page)) { + mi_assert_internal(_mi_wsize_from_size(pq->block_size) == MI_LARGE_MAX_OBJ_WSIZE + 1); + } + else { + mi_assert_internal(mi_page_block_size(page) == pq->block_size); + } + mi_assert_internal(page->theap == theap); + if (page->next == NULL) { + mi_assert_internal(pq->last == page); + } + count++; + prev = page; + } + mi_assert_internal(pq->count == count); + return true; } -static mi_page_queue_t* mi_heap_page_queue_of(mi_heap_t* heap, const mi_page_t* page) { - mi_assert_internal(heap!=NULL); - uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : (mi_page_is_huge(page) ? MI_BIN_HUGE : mi_bin(mi_page_block_size(page)))); +static size_t mi_page_bin(const mi_page_t* page) { + const size_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : (mi_page_is_huge(page) ? MI_BIN_HUGE : mi_bin(mi_page_block_size(page)))); mi_assert_internal(bin <= MI_BIN_FULL); - mi_page_queue_t* pq = &heap->pages[bin]; + return bin; +} + +// returns the page bin without using MI_BIN_FULL for statistics +size_t _mi_page_stats_bin(const mi_page_t* page) { + const size_t bin = (mi_page_is_huge(page) ? MI_BIN_HUGE : mi_bin(mi_page_block_size(page))); + mi_assert_internal(bin <= MI_BIN_HUGE); + return bin; +} + +static mi_page_queue_t* mi_theap_page_queue_of(mi_theap_t* theap, const mi_page_t* page) { + mi_assert_internal(theap!=NULL); + const size_t bin = mi_page_bin(page); + mi_page_queue_t* pq = &theap->pages[bin]; mi_assert_internal((mi_page_block_size(page) == pq->block_size) || - (mi_page_is_large_or_huge(page) && mi_page_queue_is_huge(pq)) || + (mi_page_is_huge(page) && mi_page_queue_is_huge(pq)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(pq))); return pq; } static mi_page_queue_t* mi_page_queue_of(const mi_page_t* page) { - mi_heap_t* heap = mi_page_heap(page); - mi_page_queue_t* pq = mi_heap_page_queue_of(heap, page); + mi_theap_t* theap = mi_page_theap(page); + mi_page_queue_t* pq = mi_theap_page_queue_of(theap, page); mi_assert_expensive(mi_page_queue_contains(pq, page)); return pq; } @@ -168,30 +206,30 @@ static mi_page_queue_t* mi_page_queue_of(const mi_page_t* page) { // size without having to compute the bin. This means when the // current free page queue is updated for a small bin, we need to update a // range of entries in `_mi_page_small_free`. -static inline void mi_heap_queue_first_update(mi_heap_t* heap, const mi_page_queue_t* pq) { - mi_assert_internal(mi_heap_contains_queue(heap,pq)); - size_t size = pq->block_size; +static inline void mi_theap_queue_first_update(mi_theap_t* theap, const mi_page_queue_t* pq) { + mi_assert_internal(mi_theap_contains_queue(theap,pq)); + const size_t size = pq->block_size; if (size > MI_SMALL_SIZE_MAX) return; mi_page_t* page = pq->first; if (pq->first == NULL) page = (mi_page_t*)&_mi_page_empty; // find index in the right direct page array - size_t start; - size_t idx = _mi_wsize_from_size(size); - mi_page_t** pages_free = heap->pages_free_direct; - + const size_t idx = _mi_wsize_from_size(size); + mi_page_t** const pages_free = theap->pages_free_direct; if (pages_free[idx] == page) return; // already set // find start slot + size_t start; if (idx<=1) { start = 0; } else { // find previous size; due to minimal alignment upto 3 previous bins may need to be skipped - uint8_t bin = mi_bin(size); + mi_assert_internal(pq > &theap->pages[0]); // since idx > 1 + size_t bin = mi_bin(size); const mi_page_queue_t* prev = pq - 1; - while( bin == mi_bin(prev->block_size) && prev > &heap->pages[0]) { + while( bin == mi_bin(prev->block_size) && prev > &theap->pages[0]) { prev--; } start = 1 + _mi_wsize_from_size(prev->block_size); @@ -214,40 +252,40 @@ static bool mi_page_queue_is_empty(mi_page_queue_t* queue) { static void mi_page_queue_remove(mi_page_queue_t* queue, mi_page_t* page) { mi_assert_internal(page != NULL); mi_assert_expensive(mi_page_queue_contains(queue, page)); + mi_assert_internal(queue->count >= 1); mi_assert_internal(mi_page_block_size(page) == queue->block_size || - (mi_page_is_large_or_huge(page) && mi_page_queue_is_huge(queue)) || + (mi_page_is_huge(page) && mi_page_queue_is_huge(queue)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue))); - mi_heap_t* heap = mi_page_heap(page); - + mi_theap_t* theap = mi_page_theap(page); if (page->prev != NULL) page->prev->next = page->next; if (page->next != NULL) page->next->prev = page->prev; if (page == queue->last) queue->last = page->prev; if (page == queue->first) { queue->first = page->next; // update first - mi_assert_internal(mi_heap_contains_queue(heap, queue)); - mi_heap_queue_first_update(heap,queue); + mi_assert_internal(mi_theap_contains_queue(theap, queue)); + mi_theap_queue_first_update(theap,queue); } - heap->page_count--; + theap->page_count--; + queue->count--; page->next = NULL; page->prev = NULL; - // mi_atomic_store_ptr_release(mi_atomic_cast(void*, &page->heap), NULL); mi_page_set_in_full(page,false); } -static void mi_page_queue_push(mi_heap_t* heap, mi_page_queue_t* queue, mi_page_t* page) { - mi_assert_internal(mi_page_heap(page) == heap); +static void mi_page_queue_push(mi_theap_t* theap, mi_page_queue_t* queue, mi_page_t* page) { + mi_assert_internal(mi_page_theap(page) == theap); mi_assert_internal(!mi_page_queue_contains(queue, page)); #if MI_HUGE_PAGE_ABANDON - mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE); + mi_assert_internal(_mi_page_segment(page)->page_kind != MI_PAGE_HUGE); #endif mi_assert_internal(mi_page_block_size(page) == queue->block_size || - (mi_page_is_large_or_huge(page) && mi_page_queue_is_huge(queue)) || + (mi_page_is_huge(page) && mi_page_queue_is_huge(queue)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue))); mi_page_set_in_full(page, mi_page_queue_is_full(queue)); - // mi_atomic_store_ptr_release(mi_atomic_cast(void*, &page->heap), heap); + page->next = queue->first; page->prev = NULL; if (queue->first != NULL) { @@ -258,15 +296,54 @@ static void mi_page_queue_push(mi_heap_t* heap, mi_page_queue_t* queue, mi_page_ else { queue->first = queue->last = page; } + queue->count++; // update direct - mi_heap_queue_first_update(heap, queue); - heap->page_count++; + mi_theap_queue_first_update(theap, queue); + theap->page_count++; } +static void mi_page_queue_push_at_end(mi_theap_t* theap, mi_page_queue_t* queue, mi_page_t* page) { + mi_assert_internal(mi_page_theap(page) == theap); + mi_assert_internal(!mi_page_queue_contains(queue, page)); -static void mi_page_queue_enqueue_from(mi_page_queue_t* to, mi_page_queue_t* from, mi_page_t* page) { + mi_assert_internal(mi_page_block_size(page) == queue->block_size || + (mi_page_is_huge(page) && mi_page_queue_is_huge(queue)) || + (mi_page_is_in_full(page) && mi_page_queue_is_full(queue))); + + mi_page_set_in_full(page, mi_page_queue_is_full(queue)); + + page->prev = queue->last; + page->next = NULL; + if (queue->last != NULL) { + mi_assert_internal(queue->last->next == NULL); + queue->last->next = page; + queue->last = page; + } + else { + queue->first = queue->last = page; + } + queue->count++; + + // update direct + if (queue->first == page) { + mi_theap_queue_first_update(theap, queue); + } + theap->page_count++; +} + +static void mi_page_queue_move_to_front(mi_theap_t* theap, mi_page_queue_t* queue, mi_page_t* page) { + mi_assert_internal(mi_page_theap(page) == theap); + mi_assert_internal(mi_page_queue_contains(queue, page)); + if (queue->first == page) return; + mi_page_queue_remove(queue, page); + mi_page_queue_push(theap, queue, page); + mi_assert_internal(queue->first == page); +} + +static void mi_page_queue_enqueue_from_ex(mi_page_queue_t* to, mi_page_queue_t* from, bool enqueue_at_end, mi_page_t* page) { mi_assert_internal(page != NULL); + mi_assert_internal(from->count >= 1); mi_assert_expensive(mi_page_queue_contains(from, page)); mi_assert_expensive(!mi_page_queue_contains(to, page)); const size_t bsize = mi_page_block_size(page); @@ -274,62 +351,98 @@ static void mi_page_queue_enqueue_from(mi_page_queue_t* to, mi_page_queue_t* fro mi_assert_internal((bsize == to->block_size && bsize == from->block_size) || (bsize == to->block_size && mi_page_queue_is_full(from)) || (bsize == from->block_size && mi_page_queue_is_full(to)) || - (mi_page_is_large_or_huge(page) && mi_page_queue_is_huge(to)) || - (mi_page_is_large_or_huge(page) && mi_page_queue_is_full(to))); + (mi_page_is_huge(page) && mi_page_queue_is_huge(to)) || + (mi_page_is_huge(page) && mi_page_queue_is_full(to))); + + mi_theap_t* theap = mi_page_theap(page); - mi_heap_t* heap = mi_page_heap(page); + // delete from `from` if (page->prev != NULL) page->prev->next = page->next; if (page->next != NULL) page->next->prev = page->prev; if (page == from->last) from->last = page->prev; if (page == from->first) { from->first = page->next; // update first - mi_assert_internal(mi_heap_contains_queue(heap, from)); - mi_heap_queue_first_update(heap, from); + mi_assert_internal(mi_theap_contains_queue(theap, from)); + mi_theap_queue_first_update(theap, from); } - - page->prev = to->last; - page->next = NULL; - if (to->last != NULL) { - mi_assert_internal(heap == mi_page_heap(to->last)); - to->last->next = page; - to->last = page; + from->count--; + + // insert into `to` + to->count++; + if (enqueue_at_end) { + // enqueue at the end + page->prev = to->last; + page->next = NULL; + if (to->last != NULL) { + mi_assert_internal(theap == mi_page_theap(to->last)); + to->last->next = page; + to->last = page; + } + else { + to->first = page; + to->last = page; + mi_theap_queue_first_update(theap, to); + } } else { - to->first = page; - to->last = page; - mi_heap_queue_first_update(heap, to); + if (to->first != NULL) { + // enqueue at 2nd place + mi_assert_internal(theap == mi_page_theap(to->first)); + mi_page_t* next = to->first->next; + page->prev = to->first; + page->next = next; + to->first->next = page; + if (next != NULL) { + next->prev = page; + } + else { + to->last = page; + } + } + else { + // enqueue at the head (singleton list) + page->prev = NULL; + page->next = NULL; + to->first = page; + to->last = page; + mi_theap_queue_first_update(theap, to); + } } mi_page_set_in_full(page, mi_page_queue_is_full(to)); } -// Only called from `mi_heap_absorb`. -size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append) { - mi_assert_internal(mi_heap_contains_queue(heap,pq)); +static void mi_page_queue_enqueue_from(mi_page_queue_t* to, mi_page_queue_t* from, mi_page_t* page) { + mi_page_queue_enqueue_from_ex(to, from, true /* enqueue at the end */, page); +} + +static void mi_page_queue_enqueue_from_full(mi_page_queue_t* to, mi_page_queue_t* from, mi_page_t* page) { + // note: we could insert at the front to increase reuse, but it slows down certain benchmarks (like `alloc-test`) + mi_page_queue_enqueue_from_ex(to, from, true /* enqueue at the end of the `to` queue? */, page); +} + +// Only called from `mi_theap_absorb`. +size_t _mi_page_queue_append(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_queue_t* append) { + mi_assert_internal(mi_theap_contains_queue(theap,pq)); mi_assert_internal(pq->block_size == append->block_size); if (append->first==NULL) return 0; - // set append pages to new heap and count + // set append pages to new theap and count size_t count = 0; for (mi_page_t* page = append->first; page != NULL; page = page->next) { - // inline `mi_page_set_heap` to avoid wrong assertion during absorption; - // in this case it is ok to be delayed freeing since both "to" and "from" heap are still alive. - mi_atomic_store_release(&page->xheap, (uintptr_t)heap); - // set the flag to delayed free (not overriding NEVER_DELAYED_FREE) which has as a - // side effect that it spins until any DELAYED_FREEING is finished. This ensures - // that after appending only the new heap will be used for delayed free operations. - _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false); + mi_page_set_theap(page, theap); count++; } + mi_assert_internal(count == append->count); if (pq->last==NULL) { // take over afresh mi_assert_internal(pq->first==NULL); pq->first = append->first; pq->last = append->last; - mi_heap_queue_first_update(heap, pq); + mi_theap_queue_first_update(theap, pq); } else { // append to end @@ -339,5 +452,7 @@ size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue append->first->prev = pq->last; pq->last = append->last; } + pq->count += append->count; + return count; } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page.c index 871ed215..24dd45df 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/page.c @@ -14,6 +14,7 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" #include "mimalloc/atomic.h" +#include "mimalloc/prim.h" /* ----------------------------------------------------------- Definition of page queues for each block size @@ -36,14 +37,16 @@ static inline mi_block_t* mi_page_block_at(const mi_page_t* page, void* page_sta return (mi_block_t*)((uint8_t*)page_start + (i * block_size)); } -static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t size, mi_tld_t* tld); -static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld); +static bool mi_page_extend_free(mi_theap_t* theap, mi_page_t* page); #if (MI_DEBUG>=3) static size_t mi_page_list_count(mi_page_t* page, mi_block_t* head) { + mi_assert_internal(_mi_ptr_page(mi_page_start(page)) == page); + const uint8_t* slice_start = mi_page_slice_start(page); + mi_assert_internal(_mi_is_aligned(slice_start,MI_PAGE_ALIGN)); size_t count = 0; while (head != NULL) { - mi_assert_internal(page == _mi_ptr_page(head)); + mi_assert_internal((uint8_t*)head - slice_start > (ptrdiff_t)MI_LARGE_PAGE_SIZE || page == _mi_ptr_page(head)); count++; head = mi_block_next(page, head); } @@ -59,7 +62,7 @@ static inline uint8_t* mi_page_area(const mi_page_t* page) { static bool mi_page_list_is_valid(mi_page_t* page, mi_block_t* p) { size_t psize; - uint8_t* page_area = _mi_segment_page_start(_mi_page_segment(page), page, &psize); + uint8_t* page_area = mi_page_area(page, &psize); mi_block_t* start = (mi_block_t*)page_area; mi_block_t* end = (mi_block_t*)(page_area + psize); while(p != NULL) { @@ -82,9 +85,8 @@ static bool mi_page_is_valid_init(mi_page_t* page) { mi_assert_internal(page->used <= page->capacity); mi_assert_internal(page->capacity <= page->reserved); - uint8_t* start = mi_page_start(page); - mi_assert_internal(start == _mi_segment_page_start(_mi_page_segment(page), page, NULL)); - mi_assert_internal(page->is_huge == (_mi_page_segment(page)->kind == MI_SEGMENT_HUGE)); + // const size_t bsize = mi_page_block_size(page); + // uint8_t* start = mi_page_start(page); //mi_assert_internal(start + page->capacity*page->block_size == page->top); mi_assert_internal(mi_page_list_is_valid(page,page->free)); @@ -112,115 +114,100 @@ static bool mi_page_is_valid_init(mi_page_t* page) { return true; } -extern bool _mi_process_is_initialized; // has mi_process_init been called? +extern mi_decl_hidden bool _mi_process_is_initialized; // has mi_process_init been called? bool _mi_page_is_valid(mi_page_t* page) { mi_assert_internal(mi_page_is_valid_init(page)); #if MI_SECURE mi_assert_internal(page->keys[0] != 0); #endif - if (mi_page_heap(page)!=NULL) { - mi_segment_t* segment = _mi_page_segment(page); - - mi_assert_internal(!_mi_process_is_initialized || segment->thread_id==0 || segment->thread_id == mi_page_heap(page)->thread_id); - #if MI_HUGE_PAGE_ABANDON - if (segment->kind != MI_SEGMENT_HUGE) - #endif + if (!mi_page_is_abandoned(page)) { + //mi_assert_internal(!_mi_process_is_initialized); { mi_page_queue_t* pq = mi_page_queue_of(page); mi_assert_internal(mi_page_queue_contains(pq, page)); - mi_assert_internal(pq->block_size==mi_page_block_size(page) || mi_page_block_size(page) > MI_MEDIUM_OBJ_SIZE_MAX || mi_page_is_in_full(page)); - mi_assert_internal(mi_heap_contains_queue(mi_page_heap(page),pq)); + mi_assert_internal(pq->block_size==mi_page_block_size(page) || mi_page_is_huge(page) || mi_page_is_in_full(page)); + // mi_assert_internal(mi_theap_contains_queue(mi_page_theap(page),pq)); } } return true; } #endif -void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { - while (!_mi_page_try_use_delayed_free(page, delay, override_never)) { - mi_atomic_yield(); - } -} - -bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { - mi_thread_free_t tfreex; - mi_delayed_t old_delay; - mi_thread_free_t tfree; - size_t yield_count = 0; - do { - tfree = mi_atomic_load_acquire(&page->xthread_free); // note: must acquire as we can break/repeat this loop and not do a CAS; - tfreex = mi_tf_set_delayed(tfree, delay); - old_delay = mi_tf_delayed(tfree); - if mi_unlikely(old_delay == MI_DELAYED_FREEING) { - if (yield_count >= 4) return false; // give up after 4 tries - yield_count++; - mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done. - // tfree = mi_tf_set_delayed(tfree, MI_NO_DELAYED_FREE); // will cause CAS to busy fail - } - else if (delay == old_delay) { - break; // avoid atomic operation if already equal - } - else if (!override_never && old_delay == MI_NEVER_DELAYED_FREE) { - break; // leave never-delayed flag set - } - } while ((old_delay == MI_DELAYED_FREEING) || - !mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); - - return true; // success -} /* ----------------------------------------------------------- Page collect the `local_free` and `thread_free` lists ----------------------------------------------------------- */ -// Collect the local `thread_free` list using an atomic exchange. -// Note: The exchange must be done atomically as this is used right after -// moving to the full list in `mi_page_collect_ex` and we need to -// ensure that there was no race where the page became unfull just before the move. -static void _mi_page_thread_free_collect(mi_page_t* page) +static void mi_page_thread_collect_to_local(mi_page_t* page, mi_block_t* head) { - mi_block_t* head; - mi_thread_free_t tfreex; - mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free); - do { - head = mi_tf_block(tfree); - tfreex = mi_tf_set_block(tfree,NULL); - } while (!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tfree, tfreex)); - - // return if the list is empty if (head == NULL) return; - // find the tail -- also to get a proper count (without data races) + // find the last block in the list -- also to get a proper use count (without data races) size_t max_count = page->capacity; // cannot collect more than capacity size_t count = 1; - mi_block_t* tail = head; + mi_block_t* last = head; mi_block_t* next; - while ((next = mi_block_next(page,tail)) != NULL && count <= max_count) { + while ((next = mi_block_next(page, last)) != NULL && count <= max_count) { count++; - tail = next; + last = next; } + // if `count > max_count` there was a memory corruption (possibly infinite list due to double multi-threaded free) - if (count > max_count) { + if mi_unlikely(count > max_count) { _mi_error_message(EFAULT, "corrupted thread-free list\n"); return; // the thread-free items cannot be freed } + // if `count > page->used` there was another kind memory corruption (either in the page meta-data or in the linked list) + else if mi_unlikely(count > page->used) { + _mi_error_message(EFAULT, "corrupted meta-data in thread-free list\n"); + return; // the thread-free items cannot be freed + } // and append the current local free list - mi_block_set_next(page,tail, page->local_free); + mi_block_set_next(page, last, page->local_free); page->local_free = head; // update counts now - page->used -= (uint16_t)count; + mi_assert_internal(count <= UINT16_MAX); + mi_assert_internal(page->used >= (uint16_t)count); + page->used = page->used - (uint16_t)count; +} + +// Collect the local `thread_free` list using an atomic exchange. +static void mi_page_thread_free_collect(mi_page_t* page) +{ + // atomically capture the thread free list + mi_block_t* head; + mi_thread_free_t tfreex; + mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free); + do { + head = mi_tf_block(tfree); + if mi_likely(head == NULL) return; // return if the list is empty + tfreex = mi_tf_create(NULL,mi_tf_is_owned(tfree)); // set the thread free list to NULL + } while (!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tfree, tfreex)); // release is enough? + mi_assert_internal(head != NULL); + + // and move it to the local list + mi_page_thread_collect_to_local(page, head); +} + +// returns `true` if after collection `mi_page_immediate_available` is true. +static bool mi_page_free_quick_collect(mi_page_t* page) { + if (page->free != NULL) return true; + if (page->local_free == NULL) return false; + // move local_free to free + page->free = page->local_free; + page->local_free = NULL; + page->free_is_zero = false; + return true; } void _mi_page_free_collect(mi_page_t* page, bool force) { mi_assert_internal(page!=NULL); // collect the thread free list - if (force || mi_page_thread_free(page) != NULL) { // quick test to avoid an atomic operation - _mi_page_thread_free_collect(page); - } + mi_page_thread_free_collect(page); // and the local free list if (page->local_free != NULL) { @@ -247,118 +234,149 @@ void _mi_page_free_collect(mi_page_t* page, bool force) { mi_assert_internal(!force || page->local_free == NULL); } +// Collect elements in the thread-free list starting at `head`. This is an optimized +// version of `_mi_page_free_collect` to be used from `free.c:_mi_free_collect_mt` that avoids atomic access to `xthread_free`. +// +// `head` must be in the `xthread_free` list. It will not collect `head` itself +// so the `used` count is not fully updated in general. However, if the `head` is +// the last remaining element, it will be collected and the used count will become `0` (so `mi_page_all_free` becomes true). +void _mi_page_free_collect_partly(mi_page_t* page, mi_block_t* head) { + if (head == NULL) return; + mi_block_t* next = mi_block_next(page,head); // we cannot collect the head element itself as `page->thread_free` may point to it (and we want to avoid atomic ops) + if (next != NULL) { + mi_block_set_next(page, head, NULL); + mi_page_thread_collect_to_local(page, next); + if (page->local_free != NULL && page->free == NULL) { + page->free = page->local_free; + page->local_free = NULL; + page->free_is_zero = false; + } + } + if (page->used == 1) { + // all elements are free'd since we skipped the `head` element itself + mi_assert_internal(mi_tf_block(mi_atomic_load_relaxed(&page->xthread_free)) == head); + mi_assert_internal(mi_block_next(page,head) == NULL); + _mi_page_free_collect(page, false); // collect the final element + } +} /* ----------------------------------------------------------- Page fresh and retire ----------------------------------------------------------- */ +/* // called from segments when reclaiming abandoned pages -void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page) { - mi_assert_expensive(mi_page_is_valid_init(page)); +void _mi_page_reclaim(mi_theap_t* theap, mi_page_t* page) { + // mi_page_set_theap(page, theap); + // _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, true); // override never (after theap is set) + _mi_page_free_collect(page, false); // ensure used count is up to date - mi_assert_internal(mi_page_heap(page) == heap); - mi_assert_internal(mi_page_thread_free_flag(page) != MI_NEVER_DELAYED_FREE); - #if MI_HUGE_PAGE_ABANDON - mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE); - #endif + mi_assert_expensive(mi_page_is_valid_init(page)); + // mi_assert_internal(mi_page_theap(page) == theap); + // mi_assert_internal(mi_page_thread_free_flag(page) != MI_NEVER_DELAYED_FREE); // TODO: push on full queue immediately if it is full? - mi_page_queue_t* pq = mi_page_queue(heap, mi_page_block_size(page)); - mi_page_queue_push(heap, pq, page); + mi_page_queue_t* pq = mi_theap_page_queue_of(theap, page); + mi_page_queue_push(theap, pq, page); + mi_assert_expensive(_mi_page_is_valid(page)); +} +*/ + +// called from `mi_free` on a reclaim, and fresh_alloc if we get an abandoned page +void _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page) +{ + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(mi_page_is_abandoned(page)); + + mi_page_set_theap(page,theap); + _mi_page_free_collect(page, false); // ensure used count is up to date + mi_page_queue_t* pq = mi_theap_page_queue_of(theap, page); + mi_page_queue_push_at_end(theap, pq, page); mi_assert_expensive(_mi_page_is_valid(page)); } -// allocate a fresh page from a segment -static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size_t block_size, size_t page_alignment) { +void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq) { + _mi_page_free_collect(page, false); // ensure used count is up to date + if (mi_page_all_free(page)) { + _mi_page_free(page, pq); + } + else { + mi_page_queue_remove(pq, page); + mi_theap_t* theap = page->theap; + mi_page_set_theap(page, NULL); + page->theap = theap; // don't actually set theap to NULL so we can reclaim_on_free within the same theap + _mi_arenas_page_abandon(page, theap); + _mi_arenas_collect(false, false, theap->tld); // allow purging + } +} + + +// allocate a fresh page from an arena +static mi_page_t* mi_page_fresh_alloc(mi_theap_t* theap, mi_page_queue_t* pq, size_t block_size, size_t page_alignment) { #if !MI_HUGE_PAGE_ABANDON mi_assert_internal(pq != NULL); - mi_assert_internal(mi_heap_contains_queue(heap, pq)); - mi_assert_internal(page_alignment > 0 || block_size > MI_MEDIUM_OBJ_SIZE_MAX || block_size == pq->block_size); + mi_assert_internal(mi_theap_contains_queue(theap, pq)); + mi_assert_internal(page_alignment > 0 || block_size > MI_LARGE_MAX_OBJ_SIZE || block_size == pq->block_size); #endif - mi_page_t* page = _mi_segment_page_alloc(heap, block_size, page_alignment, &heap->tld->segments, &heap->tld->os); + mi_page_t* page = _mi_arenas_page_alloc(theap, block_size, page_alignment); if (page == NULL) { - // this may be out-of-memory, or an abandoned page was reclaimed (and in our queue) + // out-of-memory return NULL; } - #if MI_HUGE_PAGE_ABANDON - mi_assert_internal(pq==NULL || _mi_page_segment(page)->page_kind != MI_PAGE_HUGE); - #endif - mi_assert_internal(page_alignment >0 || block_size > MI_MEDIUM_OBJ_SIZE_MAX || _mi_page_segment(page)->kind != MI_SEGMENT_HUGE); + if (mi_page_is_abandoned(page)) { + _mi_theap_page_reclaim(theap, page); + if (!mi_page_immediate_available(page)) { + if (mi_page_is_expandable(page)) { + if (!mi_page_extend_free(theap, page)) { + return NULL; // cannot commit + }; + } + else { + mi_assert(false); // should not happen? + return NULL; + } + } + } + else if (pq != NULL) { + mi_page_queue_push(theap, pq, page); + } mi_assert_internal(pq!=NULL || mi_page_block_size(page) >= block_size); - // a fresh page was found, initialize it - const size_t full_block_size = (pq == NULL || mi_page_is_huge(page) ? mi_page_block_size(page) : block_size); // see also: mi_segment_huge_page_alloc - mi_assert_internal(full_block_size >= block_size); - mi_page_init(heap, page, full_block_size, heap->tld); - mi_heap_stat_increase(heap, pages, 1); - if (pq != NULL) { mi_page_queue_push(heap, pq, page); } mi_assert_expensive(_mi_page_is_valid(page)); return page; } // Get a fresh page to use -static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) { - mi_assert_internal(mi_heap_contains_queue(heap, pq)); - mi_page_t* page = mi_page_fresh_alloc(heap, pq, pq->block_size, 0); +static mi_page_t* mi_page_fresh(mi_theap_t* theap, mi_page_queue_t* pq) { + mi_assert_internal(mi_theap_contains_queue(theap, pq)); + mi_page_t* page = mi_page_fresh_alloc(theap, pq, pq->block_size, 0); if (page==NULL) return NULL; mi_assert_internal(pq->block_size==mi_page_block_size(page)); - mi_assert_internal(pq==mi_page_queue(heap, mi_page_block_size(page))); + mi_assert_internal(pq==mi_theap_page_queue_of(theap, page)); return page; } -/* ----------------------------------------------------------- - Do any delayed frees - (put there by other threads if they deallocated in a full page) ------------------------------------------------------------ */ -void _mi_heap_delayed_free_all(mi_heap_t* heap) { - while (!_mi_heap_delayed_free_partial(heap)) { - mi_atomic_yield(); - } -} - -// returns true if all delayed frees were processed -bool _mi_heap_delayed_free_partial(mi_heap_t* heap) { - // take over the list (note: no atomic exchange since it is often NULL) - mi_block_t* block = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); - while (block != NULL && !mi_atomic_cas_ptr_weak_acq_rel(mi_block_t, &heap->thread_delayed_free, &block, NULL)) { /* nothing */ }; - bool all_freed = true; - - // and free them all - while(block != NULL) { - mi_block_t* next = mi_block_nextx(heap,block, heap->keys); - // use internal free instead of regular one to keep stats etc correct - if (!_mi_free_delayed_block(block)) { - // we might already start delayed freeing while another thread has not yet - // reset the delayed_freeing flag; in that case delay it further by reinserting the current block - // into the delayed free list - all_freed = false; - mi_block_t* dfree = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); - do { - mi_block_set_nextx(heap, block, dfree, heap->keys); - } while (!mi_atomic_cas_ptr_weak_release(mi_block_t,&heap->thread_delayed_free, &dfree, block)); - } - block = next; - } - return all_freed; -} /* ----------------------------------------------------------- Unfull, abandon, free and retire ----------------------------------------------------------- */ -// Move a page from the full list back to a regular list +// Move a page from the full list back to a regular list (called from thread-local mi_free) void _mi_page_unfull(mi_page_t* page) { mi_assert_internal(page != NULL); mi_assert_expensive(_mi_page_is_valid(page)); mi_assert_internal(mi_page_is_in_full(page)); + mi_assert_internal(!mi_page_theap(page)->allow_page_abandon); if (!mi_page_is_in_full(page)) return; - mi_heap_t* heap = mi_page_heap(page); - mi_page_queue_t* pqfull = &heap->pages[MI_BIN_FULL]; + mi_theap_t* theap = mi_page_theap(page); + mi_page_queue_t* pqfull = &theap->pages[MI_BIN_FULL]; mi_page_set_in_full(page, false); // to get the right queue - mi_page_queue_t* pq = mi_heap_page_queue_of(heap, page); + mi_page_queue_t* pq = mi_theap_page_queue_of(theap, page); mi_page_set_in_full(page, true); - mi_page_queue_enqueue_from(pq, pqfull, page); + mi_page_queue_enqueue_from_full(pq, pqfull, page); } static void mi_page_to_full(mi_page_t* page, mi_page_queue_t* pq) { @@ -366,74 +384,48 @@ static void mi_page_to_full(mi_page_t* page, mi_page_queue_t* pq) { mi_assert_internal(!mi_page_immediate_available(page)); mi_assert_internal(!mi_page_is_in_full(page)); - if (mi_page_is_in_full(page)) return; - mi_page_queue_enqueue_from(&mi_page_heap(page)->pages[MI_BIN_FULL], pq, page); - _mi_page_free_collect(page,false); // try to collect right away in case another thread freed just before MI_USE_DELAYED_FREE was set -} - - -// Abandon a page with used blocks at the end of a thread. -// Note: only call if it is ensured that no references exist from -// the `page->heap->thread_delayed_free` into this page. -// Currently only called through `mi_heap_collect_ex` which ensures this. -void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq) { - mi_assert_internal(page != NULL); - mi_assert_expensive(_mi_page_is_valid(page)); - mi_assert_internal(pq == mi_page_queue_of(page)); - mi_assert_internal(mi_page_heap(page) != NULL); - - mi_heap_t* pheap = mi_page_heap(page); - - // remove from our page list - mi_segments_tld_t* segments_tld = &pheap->tld->segments; - mi_page_queue_remove(pq, page); - - // page is no longer associated with our heap - mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE); - mi_page_set_heap(page, NULL); - -#if (MI_DEBUG>1) && !MI_TRACK_ENABLED - // check there are no references left.. - for (mi_block_t* block = (mi_block_t*)pheap->thread_delayed_free; block != NULL; block = mi_block_nextx(pheap, block, pheap->keys)) { - mi_assert_internal(_mi_ptr_page(block) != page); + mi_theap_t* theap = mi_page_theap(page); + if (theap->allow_page_abandon) { + // abandon full pages (this is the usual case in order to allow for sharing of memory between theaps) + _mi_page_abandon(page, pq); + } + else if (!mi_page_is_in_full(page)) { + // put full pages in a theap local queue (this is for theaps that cannot abandon, for example, if the theap can be destroyed) + mi_page_queue_enqueue_from(&mi_page_theap(page)->pages[MI_BIN_FULL], pq, page); + _mi_page_free_collect(page, false); // try to collect right away in case another thread freed just before MI_USE_DELAYED_FREE was set } -#endif - - // and abandon it - mi_assert_internal(mi_page_heap(page) == NULL); - _mi_segment_page_abandon(page,segments_tld); } // Free a page with no more free blocks -void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) { +void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq) { mi_assert_internal(page != NULL); mi_assert_expensive(_mi_page_is_valid(page)); mi_assert_internal(pq == mi_page_queue_of(page)); mi_assert_internal(mi_page_all_free(page)); - mi_assert_internal(mi_page_thread_free_flag(page)!=MI_DELAYED_FREEING); + // mi_assert_internal(mi_page_thread_free_flag(page)!=MI_DELAYED_FREEING); // no more aligned blocks in here - mi_page_set_has_aligned(page, false); - - mi_heap_t* heap = mi_page_heap(page); + mi_page_set_has_interior_pointers(page, false); // remove from the page list - // (no need to do _mi_heap_delayed_free first as all blocks are already free) - mi_segments_tld_t* segments_tld = &heap->tld->segments; + // (no need to do _mi_theap_delayed_free first as all blocks are already free) mi_page_queue_remove(pq, page); // and free it - mi_page_set_heap(page,NULL); - _mi_segment_page_free(page, force, segments_tld); + mi_theap_t* theap = mi_page_theap(page); mi_assert_internal(theap!=NULL); + mi_page_set_theap(page,NULL); + _mi_arenas_page_free(page, theap); + _mi_arenas_collect(false, false, theap->tld); // allow purging } -#define MI_MAX_RETIRE_SIZE MI_MEDIUM_OBJ_SIZE_MAX // should be less than size for MI_BIN_HUGE +#define MI_MAX_RETIRE_SIZE MI_LARGE_OBJ_SIZE_MAX // should be less than size for MI_BIN_HUGE #define MI_RETIRE_CYCLES (16) // Retire a page with no more used blocks // Important to not retire too quickly though as new // allocations might coming. +// // Note: called from `mi_free` and benchmarks often // trigger this due to freeing everything and then // allocating again so careful when changing this. @@ -442,7 +434,8 @@ void _mi_page_retire(mi_page_t* page) mi_attr_noexcept { mi_assert_expensive(_mi_page_is_valid(page)); mi_assert_internal(mi_page_all_free(page)); - mi_page_set_has_aligned(page, false); + if (page->retire_expire!=0) return; // already retired, just keep it retired + mi_page_set_has_interior_pointers(page, false); // don't retire too often.. // (or we end up retiring and re-allocating most of the time) @@ -451,37 +444,41 @@ void _mi_page_retire(mi_page_t* page) mi_attr_noexcept { // how to check this efficiently though... // for now, we don't retire if it is the only page left of this size class. mi_page_queue_t* pq = mi_page_queue_of(page); + #if MI_RETIRE_CYCLES > 0 const size_t bsize = mi_page_block_size(page); if mi_likely( /* bsize < MI_MAX_RETIRE_SIZE && */ !mi_page_queue_is_special(pq)) { // not full or huge queue? if (pq->last==page && pq->first==page) { // the only page in the queue? - mi_stat_counter_increase(_mi_stats_main.page_no_retire,1); - page->retire_expire = (bsize <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4); - mi_heap_t* heap = mi_page_heap(page); - mi_assert_internal(pq >= heap->pages); - const size_t index = pq - heap->pages; + mi_theap_t* theap = mi_page_theap(page); + #if MI_STAT>0 + mi_theap_stat_counter_increase(theap, pages_retire, 1); + #endif + page->retire_expire = (bsize <= MI_SMALL_MAX_OBJ_SIZE ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4); + mi_assert_internal(pq >= theap->pages); + const size_t index = pq - theap->pages; mi_assert_internal(index < MI_BIN_FULL && index < MI_BIN_HUGE); - if (index < heap->page_retired_min) heap->page_retired_min = index; - if (index > heap->page_retired_max) heap->page_retired_max = index; + if (index < theap->page_retired_min) theap->page_retired_min = index; + if (index > theap->page_retired_max) theap->page_retired_max = index; mi_assert_internal(mi_page_all_free(page)); return; // don't free after all } } - _mi_page_free(page, pq, false); + #endif + _mi_page_free(page, pq); } // free retired pages: we don't need to look at the entire queues // since we only retire pages that are at the head position in a queue. -void _mi_heap_collect_retired(mi_heap_t* heap, bool force) { +void _mi_theap_collect_retired(mi_theap_t* theap, bool force) { size_t min = MI_BIN_FULL; size_t max = 0; - for(size_t bin = heap->page_retired_min; bin <= heap->page_retired_max; bin++) { - mi_page_queue_t* pq = &heap->pages[bin]; + for(size_t bin = theap->page_retired_min; bin <= theap->page_retired_max; bin++) { + mi_page_queue_t* pq = &theap->pages[bin]; mi_page_t* page = pq->first; if (page != NULL && page->retire_expire != 0) { if (mi_page_all_free(page)) { page->retire_expire--; - if (force || page->retire_expire == 0) { - _mi_page_free(pq->first, pq, force); + if (page->retire_expire == 0 || force) { + _mi_page_free(page, pq); } else { // keep retired, update min/max @@ -494,9 +491,32 @@ void _mi_heap_collect_retired(mi_heap_t* heap, bool force) { } } } - heap->page_retired_min = min; - heap->page_retired_max = max; + theap->page_retired_min = min; + theap->page_retired_max = max; +} + +/* +static void mi_theap_collect_full_pages(mi_theap_t* theap) { + // note: normally full pages get immediately abandoned and the full queue is always empty + // this path is only used if abandoning is disabled due to a destroy-able theap or options + // set by the user. + mi_page_queue_t* pq = &theap->pages[MI_BIN_FULL]; + for (mi_page_t* page = pq->first; page != NULL; ) { + mi_page_t* next = page->next; // get next in case we free the page + _mi_page_free_collect(page, false); // register concurrent free's + // no longer full? + if (!mi_page_is_full(page)) { + if (mi_page_all_free(page)) { + _mi_page_free(page, pq); + } + else { + _mi_page_unfull(page); + } + } + page = next; + } } +*/ /* ----------------------------------------------------------- @@ -509,9 +529,8 @@ void _mi_heap_collect_retired(mi_heap_t* heap, bool force) { #define MI_MAX_SLICES (1UL << MI_MAX_SLICE_SHIFT) #define MI_MIN_SLICES (2) -static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats) { - MI_UNUSED(stats); - #if (MI_SECURE<=2) +static void mi_page_free_list_extend_secure(mi_theap_t* const theap, mi_page_t* const page, const size_t bsize, const size_t extend) { + #if (MI_SECURE<3) mi_assert_internal(page->free == NULL); mi_assert_internal(page->local_free == NULL); #endif @@ -538,7 +557,7 @@ static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* co // and initialize the free list by randomly threading through them // set up first element - const uintptr_t r = _mi_heap_random_next(heap); + const uintptr_t r = _mi_theap_random_next(theap); size_t current = r % slice_count; counts[current]--; mi_block_t* const free_start = blocks[current]; @@ -566,10 +585,9 @@ static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* co page->free = free_start; } -static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats) +static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, const size_t bsize, const size_t extend) { - MI_UNUSED(stats); - #if (MI_SECURE <= 2) + #if (MI_SECURE<3) mi_assert_internal(page->free == NULL); mi_assert_internal(page->local_free == NULL); #endif @@ -597,10 +615,10 @@ static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, co ----------------------------------------------------------- */ #define MI_MAX_EXTEND_SIZE (4*1024) // heuristic, one OS page seems to work well. -#if (MI_SECURE>0) +#if (MI_SECURE>=2) #define MI_MIN_EXTEND (8*MI_SECURE) // extend at least by this many #else -#define MI_MIN_EXTEND (4) +#define MI_MIN_EXTEND (1) #endif // Extend the capacity (up to reserved) by initializing a free list @@ -608,21 +626,25 @@ static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, co // Note: we also experimented with "bump" allocation on the first // allocations but this did not speed up any benchmark (due to an // extra test in malloc? or cache effects?) -static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld) { - MI_UNUSED(tld); +static bool mi_page_extend_free(mi_theap_t* theap, mi_page_t* page) { mi_assert_expensive(mi_page_is_valid_init(page)); - #if (MI_SECURE<=2) + #if (MI_SECURE<3) mi_assert(page->free == NULL); mi_assert(page->local_free == NULL); - if (page->free != NULL) return; + if (page->free != NULL) return true; #endif - if (page->capacity >= page->reserved) return; + if (page->capacity >= page->reserved) return true; - mi_stat_counter_increase(tld->stats.pages_extended, 1); + size_t page_size; + //uint8_t* page_start = + mi_page_area(page, &page_size); + #if MI_STAT>0 + mi_theap_stat_counter_increase(theap, pages_extended, 1); + #endif // calculate the extend count const size_t bsize = mi_page_block_size(page); - size_t extend = page->reserved - page->capacity; + size_t extend = (size_t)page->reserved - page->capacity; mi_assert_internal(extend > 0); size_t max_extend = (bsize >= MI_MAX_EXTEND_SIZE ? MI_MIN_EXTEND : MI_MAX_EXTEND_SIZE/bsize); @@ -638,73 +660,80 @@ static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld) mi_assert_internal(extend > 0 && extend + page->capacity <= page->reserved); mi_assert_internal(extend < (1UL<<16)); + // commit on demand? + if (page->slice_committed > 0) { + const size_t needed_size = (page->capacity + extend)*bsize; + const size_t needed_commit = _mi_align_up( mi_page_slice_offset_of(page, needed_size), MI_PAGE_MIN_COMMIT_SIZE ); + if (needed_commit > page->slice_committed) { + mi_assert_internal(((needed_commit - page->slice_committed) % _mi_os_page_size()) == 0); + if (!_mi_os_commit(mi_page_slice_start(page) + page->slice_committed, needed_commit - page->slice_committed, NULL)) { + return false; + } + mi_assert_internal(needed_commit < UINT32_MAX); + page->slice_committed = (uint32_t)needed_commit; + } + } + // and append the extend the free list - if (extend < MI_MIN_SLICES || MI_SECURE==0) { //!mi_option_is_enabled(mi_option_secure)) { - mi_page_free_list_extend(page, bsize, extend, &tld->stats ); + if (extend < MI_MIN_SLICES || MI_SECURE<2) { //!mi_option_is_enabled(mi_option_secure)) { + mi_page_free_list_extend(page, bsize, extend ); } else { - mi_page_free_list_extend_secure(heap, page, bsize, extend, &tld->stats); + mi_page_free_list_extend_secure(theap, page, bsize, extend); } // enable the new free list page->capacity += (uint16_t)extend; - mi_stat_increase(tld->stats.page_committed, extend * bsize); + #if MI_STAT>0 + mi_theap_stat_increase(theap, page_committed, extend * bsize); + #endif mi_assert_expensive(mi_page_is_valid_init(page)); + return true; } -// Initialize a fresh page -static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi_tld_t* tld) { +// Initialize a fresh page (that is already partially initialized) +mi_decl_nodiscard bool _mi_page_init(mi_theap_t* theap, mi_page_t* page) { mi_assert(page != NULL); - mi_segment_t* segment = _mi_page_segment(page); - mi_assert(segment != NULL); - mi_assert_internal(block_size > 0); - // set fields - mi_page_set_heap(page, heap); - page->block_size = block_size; + mi_assert(theap!=NULL); + page->heap = (_mi_is_heap_main(_mi_theap_heap(theap)) ? NULL : _mi_theap_heap(theap)); // faster for `mi_page_associated_theap` + mi_page_set_theap(page, theap); + size_t page_size; - page->page_start = _mi_segment_page_start(segment, page, &page_size); - mi_track_mem_noaccess(page->page_start,page_size); - mi_assert_internal(mi_page_block_size(page) <= page_size); - mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE); - mi_assert_internal(page_size / block_size < (1L<<16)); - page->reserved = (uint16_t)(page_size / block_size); + uint8_t* page_start = mi_page_area(page, &page_size); MI_UNUSED(page_start); + mi_track_mem_noaccess(page_start,page_size); + mi_assert_internal(page_size / mi_page_block_size(page) < (1L<<16)); mi_assert_internal(page->reserved > 0); #if (MI_PADDING || MI_ENCODE_FREELIST) - page->keys[0] = _mi_heap_random_next(heap); - page->keys[1] = _mi_heap_random_next(heap); + page->keys[0] = _mi_theap_random_next(theap); + page->keys[1] = _mi_theap_random_next(theap); #endif - page->free_is_zero = page->is_zero_init; #if MI_DEBUG>2 - if (page->is_zero_init) { - mi_track_mem_defined(page->page_start, page_size); - mi_assert_expensive(mi_mem_is_zero(page->page_start, page_size)); + if (page->memid.initially_zero) { + mi_track_mem_defined(mi_page_start(page), mi_page_committed(page)); + mi_assert_expensive(mi_mem_is_zero(page_start, mi_page_committed(page))); } #endif - mi_assert_internal(page->is_committed); - if (block_size > 0 && _mi_is_power_of_two(block_size)) { - page->block_size_shift = (uint8_t)(mi_ctz((uintptr_t)block_size)); - } - else { - page->block_size_shift = 0; - } + mi_assert_internal(page->theap!=NULL); + mi_assert_internal(page->theap == mi_page_theap(page)); mi_assert_internal(page->capacity == 0); mi_assert_internal(page->free == NULL); mi_assert_internal(page->used == 0); - mi_assert_internal(page->xthread_free == 0); + mi_assert_internal(mi_page_is_owned(page)); + mi_assert_internal(page->xthread_free == 1); mi_assert_internal(page->next == NULL); mi_assert_internal(page->prev == NULL); mi_assert_internal(page->retire_expire == 0); - mi_assert_internal(!mi_page_has_aligned(page)); + mi_assert_internal(!mi_page_has_interior_pointers(page)); #if (MI_PADDING || MI_ENCODE_FREELIST) mi_assert_internal(page->keys[0] != 0); mi_assert_internal(page->keys[1] != 0); #endif - mi_assert_internal(page->block_size_shift == 0 || (block_size == ((size_t)1 << page->block_size_shift))); mi_assert_expensive(mi_page_is_valid_init(page)); // initialize an initial free list - mi_page_extend_free(heap,page,tld); + if (!mi_page_extend_free(theap,page)) return false; mi_assert(mi_page_immediate_available(page)); + return true; } @@ -713,85 +742,140 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi -------------------------------------------------------------*/ // Find a page with free blocks of `page->block_size`. -static mi_page_t* mi_page_queue_find_free_ex(mi_heap_t* heap, mi_page_queue_t* pq, bool first_try) +static mi_decl_noinline mi_page_t* mi_page_queue_find_free_ex(mi_theap_t* theap, mi_page_queue_t* pq, bool first_try) { // search through the pages in "next fit" order - #if MI_STAT size_t count = 0; - #endif + long candidate_limit = 0; // we reset this on the first candidate to limit the search + long page_full_retain = (pq->block_size > MI_SMALL_MAX_OBJ_SIZE ? 0 : theap->page_full_retain); // only retain small pages + mi_page_t* page_candidate = NULL; // a page with free space mi_page_t* page = pq->first; + while (page != NULL) { - mi_page_t* next = page->next; // remember next - #if MI_STAT + mi_page_t* next = page->next; // remember next (as this page can move to another queue) count++; - #endif + candidate_limit--; - // 0. collect freed blocks by us and other threads - _mi_page_free_collect(page, false); + // search up to N pages for a best candidate - // 1. if the page contains free blocks, we are done - if (mi_page_immediate_available(page)) { - break; // pick this one + // is the local free list non-empty? + bool immediate_available = mi_page_immediate_available(page); + if (!immediate_available) { + // collect freed blocks by us and other threads to we get a proper use count + _mi_page_free_collect(page, false); + immediate_available = mi_page_immediate_available(page); } - // 2. Try to extend - if (page->capacity < page->reserved) { - mi_page_extend_free(heap, page, heap->tld); - mi_assert_internal(mi_page_immediate_available(page)); - break; + // if the page is completely full, move it to the `mi_pages_full` + // queue so we don't visit long-lived pages too often. + if (!immediate_available && !mi_page_is_expandable(page)) { + page_full_retain--; + if (page_full_retain < 0) { + mi_assert_internal(!mi_page_is_in_full(page) && !mi_page_immediate_available(page)); + mi_page_to_full(page, pq); + } + } + else { + // the page has free space, make it a candidate + // we prefer non-expandable pages with high usage as candidates (to reduce commit, and increase chances of free-ing up pages) + if (page_candidate == NULL) { + page_candidate = page; + candidate_limit = _mi_option_get_fast(mi_option_page_max_candidates); + } + else if (mi_page_all_free(page_candidate)) { + _mi_page_free(page_candidate, pq); + page_candidate = page; + } + // prefer to reuse fuller pages (in the hope the less used page gets freed) + else if (page->used >= page_candidate->used && !mi_page_is_mostly_used(page)) { // && !mi_page_is_expandable(page)) { + page_candidate = page; + } + // if we find a non-expandable candidate, or searched for N pages, return with the best candidate + if (immediate_available || candidate_limit <= 0) { + mi_assert_internal(page_candidate!=NULL); + break; + } + } + + #if 0 + // first-fit algorithm without candidates + // If the page contains free blocks, we are done + if (mi_page_immediate_available(page) || mi_page_is_expandable(page)) { + break; // pick this one } - // 3. If the page is completely full, move it to the `mi_pages_full` + // If the page is completely full, move it to the `mi_pages_full` // queue so we don't visit long-lived pages too often. mi_assert_internal(!mi_page_is_in_full(page) && !mi_page_immediate_available(page)); mi_page_to_full(page, pq); + #endif page = next; } // for each page - mi_heap_stat_counter_increase(heap, searches, count); + mi_theap_stat_counter_increase(theap, page_searches, count); + mi_theap_stat_counter_increase(theap, page_searches_count, 1); + + // set the page to the best candidate + if (page_candidate != NULL) { + page = page_candidate; + } + if (page != NULL) { + if (!mi_page_immediate_available(page)) { + mi_assert_internal(mi_page_is_expandable(page)); + if (!mi_page_extend_free(theap, page)) { + page = NULL; // failed to extend + } + } + mi_assert_internal(page == NULL || mi_page_immediate_available(page)); + } if (page == NULL) { - _mi_heap_collect_retired(heap, false); // perhaps make a page available? - page = mi_page_fresh(heap, pq); + _mi_theap_collect_retired(theap, false); // perhaps make a page available + page = mi_page_fresh(theap, pq); + mi_assert_internal(page == NULL || mi_page_immediate_available(page)); if (page == NULL && first_try) { // out-of-memory _or_ an abandoned page with free blocks was reclaimed, try once again - page = mi_page_queue_find_free_ex(heap, pq, false); + page = mi_page_queue_find_free_ex(theap, pq, false); + mi_assert_internal(page == NULL || mi_page_immediate_available(page)); } } else { - mi_assert(pq->first == page); + mi_assert_internal(page == NULL || mi_page_immediate_available(page)); + // move the page to the front of the queue + mi_page_queue_move_to_front(theap, pq, page); page->retire_expire = 0; + // _mi_theap_collect_retired(theap, false); // update retire counts; note: increases rss on MemoryLoad bench so don't do this } mi_assert_internal(page == NULL || mi_page_immediate_available(page)); + + return page; } // Find a page with free blocks of `size`. -static inline mi_page_t* mi_find_free_page(mi_heap_t* heap, size_t size) { - mi_page_queue_t* pq = mi_page_queue(heap,size); +static mi_page_t* mi_find_free_page(mi_theap_t* theap, mi_page_queue_t* pq) { + // mi_page_queue_t* pq = mi_page_queue(theap, size); + mi_assert_internal(!mi_page_queue_is_huge(pq)); + + // check the first page: we even do this with candidate search or otherwise we re-search every time mi_page_t* page = pq->first; - if (page != NULL) { - #if (MI_SECURE>=3) // in secure mode, we extend half the time to increase randomness - if (page->capacity < page->reserved && ((_mi_heap_random_next(heap) & 1) == 1)) { - mi_page_extend_free(heap, page, heap->tld); + if mi_likely(page != NULL && mi_page_free_quick_collect(page)) { + #if (MI_SECURE>=2) // in secure mode, we extend half the time to increase randomness + if (page->capacity < page->reserved && ((_mi_theap_random_next(theap) & 1) == 1)) { + (void)mi_page_extend_free(theap, page); // ok if this fails mi_assert_internal(mi_page_immediate_available(page)); } - else - #endif - { - _mi_page_free_collect(page,false); - } - - if (mi_page_immediate_available(page)) { - page->retire_expire = 0; - return page; // fast path - } + #endif + page->retire_expire = 0; + return page; // fast path + } + else { + return mi_page_queue_find_free_ex(theap, pq, true); } - return mi_page_queue_find_free_ex(heap, pq, true); } @@ -802,21 +886,24 @@ static inline mi_page_t* mi_find_free_page(mi_heap_t* heap, size_t size) { a certain number of allocations. ----------------------------------------------------------- */ -static mi_deferred_free_fun* volatile deferred_free = NULL; -static _Atomic(void*) deferred_arg; // = NULL - -void _mi_deferred_free(mi_heap_t* heap, bool force) { - heap->tld->heartbeat++; - if (deferred_free != NULL && !heap->tld->recurse) { - heap->tld->recurse = true; - deferred_free(force, heap->tld->heartbeat, mi_atomic_load_ptr_relaxed(void,&deferred_arg)); - heap->tld->recurse = false; +// The program should only install a single deferred free handler before doing allocation. +static _Atomic(void*) deferred_free; // is `mi_deferred_free_fun*` (but some platforms don't support atomic function pointers) +static _Atomic(void*) deferred_arg; + +void _mi_deferred_free(mi_theap_t* theap, bool force) { + theap->heartbeat++; + mi_deferred_free_fun* const fun = (mi_deferred_free_fun*)mi_atomic_load_ptr_acquire(void,&deferred_free); + if (fun != NULL && !theap->tld->recurse) { + theap->tld->recurse = true; + void* const arg = mi_atomic_load_ptr_acquire(void,&deferred_arg); + fun(force, theap->heartbeat, arg); + theap->tld->recurse = false; } } void mi_register_deferred_free(mi_deferred_free_fun* fn, void* arg) mi_attr_noexcept { - deferred_free = fn; mi_atomic_store_ptr_release(void,&deferred_arg, arg); + mi_atomic_store_ptr_release(void,&deferred_free, (void*)fn); } @@ -824,46 +911,30 @@ void mi_register_deferred_free(mi_deferred_free_fun* fn, void* arg) mi_attr_noex General allocation ----------------------------------------------------------- */ -// Large and huge page allocation. -// Huge pages contain just one block, and the segment contains just that page (as `MI_SEGMENT_HUGE`). +// Huge pages contain just one block, and the segment contains just that page. // Huge pages are also use if the requested alignment is very large (> MI_BLOCK_ALIGNMENT_MAX) // so their size is not always `> MI_LARGE_OBJ_SIZE_MAX`. -static mi_page_t* mi_large_huge_page_alloc(mi_heap_t* heap, size_t size, size_t page_alignment) { - size_t block_size = _mi_os_good_alloc_size(size); - mi_assert_internal(mi_bin(block_size) == MI_BIN_HUGE || page_alignment > 0); - bool is_huge = (block_size > MI_LARGE_OBJ_SIZE_MAX || page_alignment > 0); +static mi_page_t* mi_huge_page_alloc(mi_theap_t* theap, size_t size, size_t page_alignment, mi_page_queue_t* pq) { + const size_t block_size = _mi_os_good_alloc_size(size); + // mi_assert_internal(mi_bin(block_size) == MI_BIN_HUGE || page_alignment > 0); #if MI_HUGE_PAGE_ABANDON - mi_page_queue_t* pq = (is_huge ? NULL : mi_page_queue(heap, block_size)); + #error todo. #else - mi_page_queue_t* pq = mi_page_queue(heap, is_huge ? MI_LARGE_OBJ_SIZE_MAX+1 : block_size); - mi_assert_internal(!is_huge || mi_page_queue_is_huge(pq)); + // mi_page_queue_t* pq = mi_page_queue(theap, MI_LARGE_MAX_OBJ_SIZE+1); // always in the huge queue regardless of the block size + mi_assert_internal(mi_page_queue_is_huge(pq)); #endif - mi_page_t* page = mi_page_fresh_alloc(heap, pq, block_size, page_alignment); + mi_page_t* page = mi_page_fresh_alloc(theap, pq, block_size, page_alignment); if (page != NULL) { + mi_assert_internal(mi_page_block_size(page) >= size); mi_assert_internal(mi_page_immediate_available(page)); - - if (is_huge) { - mi_assert_internal(mi_page_is_huge(page)); - mi_assert_internal(_mi_page_segment(page)->kind == MI_SEGMENT_HUGE); - mi_assert_internal(_mi_page_segment(page)->used==1); - #if MI_HUGE_PAGE_ABANDON - mi_assert_internal(_mi_page_segment(page)->thread_id==0); // abandoned, not in the huge queue - mi_page_set_heap(page, NULL); - #endif - } - else { - mi_assert_internal(!mi_page_is_huge(page)); - } - - const size_t bsize = mi_page_usable_block_size(page); // note: not `mi_page_block_size` to account for padding - if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { - mi_heap_stat_increase(heap, large, bsize); - mi_heap_stat_counter_increase(heap, large_count, 1); - } - else { - mi_heap_stat_increase(heap, huge, bsize); - mi_heap_stat_counter_increase(heap, huge_count, 1); - } + mi_assert_internal(mi_page_is_huge(page)); + mi_assert_internal(mi_page_is_singleton(page)); + #if MI_HUGE_PAGE_ABANDON + mi_assert_internal(mi_page_is_abandoned(page)); + mi_page_set_theap(page, NULL); + #endif + mi_theap_stat_increase(theap, malloc_huge, mi_page_block_size(page)); + mi_theap_stat_counter_increase(theap, malloc_huge_count, 1); } return page; } @@ -871,53 +942,78 @@ static mi_page_t* mi_large_huge_page_alloc(mi_heap_t* heap, size_t size, size_t // Allocate a page // Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed. -static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size, size_t huge_alignment) mi_attr_noexcept { - // huge allocation? +static mi_page_t* mi_find_page(mi_theap_t* theap, size_t size, size_t huge_alignment) mi_attr_noexcept { const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size` - if mi_unlikely(req_size > (MI_MEDIUM_OBJ_SIZE_MAX - MI_PADDING_SIZE) || huge_alignment > 0) { - if mi_unlikely(req_size > MI_MAX_ALLOC_SIZE) { - _mi_error_message(EOVERFLOW, "allocation request is too large (%zu bytes)\n", req_size); - return NULL; - } - else { - return mi_large_huge_page_alloc(heap,size,huge_alignment); - } + if mi_unlikely(req_size > MI_MAX_ALLOC_SIZE) { + _mi_error_message(EOVERFLOW, "allocation request is too large (%zu bytes)\n", req_size); + return NULL; + } + mi_page_queue_t* pq = mi_page_queue(theap, (huge_alignment > 0 ? MI_LARGE_MAX_OBJ_SIZE+1 : size)); + // huge allocation? + if mi_unlikely(mi_page_queue_is_huge(pq) || req_size > MI_MAX_ALLOC_SIZE) { + return mi_huge_page_alloc(theap,size,huge_alignment,pq); } else { // otherwise find a page with free blocks in our size segregated queues #if MI_PADDING mi_assert_internal(size >= MI_PADDING_SIZE); #endif - return mi_find_free_page(heap, size); + return mi_find_free_page(theap, pq); } } + // Generic allocation routine if the fast path (`alloc.c:mi_page_malloc`) does not succeed. // Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed. -// The `huge_alignment` is normally 0 but is set to a multiple of MI_SEGMENT_SIZE for -// very large requested alignments in which case we use a huge segment. -void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept +// The `huge_alignment` is normally 0 but is set to a multiple of MI_SLICE_SIZE for +// very large requested alignments in which case we use a huge singleton page. +// Note: we put `bool zero, size_t huge_alignment` into one parameter (with zero in the low bit) +// to use 4 parameters which compiles better on msvc for the malloc fast path. +void* _mi_malloc_generic(mi_theap_t* theap, size_t size, size_t zero_huge_alignment, size_t* usable) mi_attr_noexcept { - mi_assert_internal(heap != NULL); + const bool zero = ((zero_huge_alignment & 1) != 0); + const size_t huge_alignment = (zero_huge_alignment & ~1); + + #if !MI_THEAP_INITASNULL + mi_assert_internal(theap != NULL); + #endif // initialize if necessary - if mi_unlikely(!mi_heap_is_initialized(heap)) { - heap = mi_heap_get_default(); // calls mi_thread_init - if mi_unlikely(!mi_heap_is_initialized(heap)) { return NULL; } + if mi_unlikely(!mi_theap_is_initialized(theap)) { + if (theap==&_mi_theap_empty_wrong) { + // we were unable to allocate a theap for a first-class heap + return NULL; + } + // otherwise we initialize the thread and its default theap + mi_thread_init(); + theap = _mi_theap_default(); + if mi_unlikely(!mi_theap_is_initialized(theap)) { return NULL; } + mi_assert_internal(_mi_theap_default()==theap); + } + mi_assert_internal(mi_theap_is_initialized(theap)); + + // do administrative tasks every N generic mallocs + if mi_unlikely(++theap->generic_count >= 1000) { + theap->generic_collect_count += theap->generic_count; + theap->generic_count = 0; + // call potential deferred free routines + _mi_deferred_free(theap, false); + // free retired pages + _mi_theap_collect_retired(theap, false); + + // collect every once in a while (10000 by default) + const long generic_collect = mi_option_get_clamp(mi_option_generic_collect, 1, 1000000L); + if (theap->generic_collect_count >= generic_collect) { + theap->generic_collect_count = 0; + mi_theap_collect(theap, false /* force? */); + } } - mi_assert_internal(mi_heap_is_initialized(heap)); - - // call potential deferred free routines - _mi_deferred_free(heap, false); - - // free delayed frees from other threads (but skip contended ones) - _mi_heap_delayed_free_partial(heap); // find (or allocate) a page of the right size - mi_page_t* page = mi_find_page(heap, size, huge_alignment); + mi_page_t* page = mi_find_page(theap, size, huge_alignment); if mi_unlikely(page == NULL) { // first time out of memory, try to collect and retry the allocation once more - mi_heap_collect(heap, true /* force */); - page = mi_find_page(heap, size, huge_alignment); + mi_theap_collect(theap, true /* force? */); + page = mi_find_page(theap, size, huge_alignment); } if mi_unlikely(page == NULL) { // out of memory @@ -928,16 +1024,17 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_al mi_assert_internal(mi_page_immediate_available(page)); mi_assert_internal(mi_page_block_size(page) >= size); + mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); + mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page); // and try again, this time succeeding! (i.e. this should never recurse through _mi_page_malloc) - if mi_unlikely(zero && page->block_size == 0) { - // note: we cannot call _mi_page_malloc with zeroing for huge blocks; we zero it afterwards in that case. - void* p = _mi_page_malloc(heap, page, size); - mi_assert_internal(p != NULL); - _mi_memzero_aligned(p, mi_page_usable_block_size(page)); - return p; - } - else { - return _mi_page_malloc_zero(heap, page, size, zero); + if (usable!=NULL) { *usable = mi_page_usable_block_size(page); } + void* const p = _mi_page_malloc_zero(theap,page,size,zero); + mi_assert_internal(p != NULL); + + // move full pages to the full queue + if (mi_page_block_size(page) > MI_SMALL_MAX_OBJ_SIZE && mi_page_is_full(page)) { + mi_page_to_full(page, mi_page_queue_of(page)); } + return p; } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/emscripten/prim.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/emscripten/prim.c index f3797c9e..b52735ae 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/emscripten/prim.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/emscripten/prim.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen, Alon Zakai +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen, Alon Zakai This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -12,6 +12,9 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc/atomic.h" #include "mimalloc/prim.h" +#include // sched_yield +#include // getentropy + // Design // ====== // @@ -58,7 +61,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config) { extern void emmalloc_free(void*); int _mi_prim_free(void* addr, size_t size) { - MI_UNUSED(size); + if (size==0) return 0; emmalloc_free(addr); return 0; } @@ -71,8 +74,8 @@ int _mi_prim_free(void* addr, size_t size) { extern void* emmalloc_memalign(size_t alignment, size_t size); // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. -int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { - MI_UNUSED(try_alignment); MI_UNUSED(allow_large); MI_UNUSED(commit); +int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { + MI_UNUSED(try_alignment); MI_UNUSED(allow_large); MI_UNUSED(commit); MI_UNUSED(hint_addr); *is_large = false; // TODO: Track the highest address ever seen; first uses of it are zeroes. // That assumes no one else uses sbrk but us (they could go up, @@ -114,6 +117,11 @@ int _mi_prim_reset(void* addr, size_t size) { return 0; } +int _mi_prim_reuse(void* addr, size_t size) { + MI_UNUSED(addr); MI_UNUSED(size); + return 0; +} + int _mi_prim_protect(void* addr, size_t size, bool protect) { MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect); return 0; @@ -177,12 +185,12 @@ void _mi_prim_out_stderr( const char* msg) { // Environment //---------------------------------------------------------------- -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { // For code size reasons, do not support environ customization for now. MI_UNUSED(name); MI_UNUSED(result); MI_UNUSED(result_size); - return false; + return 0; // not found } @@ -200,15 +208,15 @@ bool _mi_prim_random_buf(void* buf, size_t buf_len) { // Thread init/done //---------------------------------------------------------------- -#ifdef __EMSCRIPTEN_SHARED_MEMORY__ +#if defined(MI_USE_PTHREADS) // use pthread local storage keys to detect thread ending -// (and used with MI_TLS_PTHREADS for the default heap) +// (and used with MI_TLS_PTHREADS for the default theap) pthread_key_t _mi_heap_default_key = (pthread_key_t)(-1); static void mi_pthread_done(void* value) { if (value!=NULL) { - _mi_thread_done((mi_heap_t*)value); + _mi_thread_done((mi_theap_t*)value); } } @@ -218,12 +226,14 @@ void _mi_prim_thread_init_auto_done(void) { } void _mi_prim_thread_done_auto_done(void) { - // nothing to do + if (_mi_heap_default_key != (pthread_key_t)(-1)) { // do not leak the key, see issue #809 + pthread_key_delete(_mi_heap_default_key); + } } -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { +void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { if (_mi_heap_default_key != (pthread_key_t)(-1)) { // can happen during recursive invocation on freeBSD - pthread_setspecific(_mi_heap_default_key, heap); + pthread_setspecific(_mi_heap_default_key, theap); } } @@ -237,8 +247,15 @@ void _mi_prim_thread_done_auto_done(void) { // nothing } -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { - MI_UNUSED(heap); - +void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); } #endif + +bool _mi_prim_thread_is_in_threadpool(void) { + return false; +} + +void _mi_prim_thread_yield(void) { + sched_yield(); +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/osx/alloc-override-zone.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/osx/alloc-override-zone.c index 1515b886..d66403a3 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/osx/alloc-override-zone.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/osx/alloc-override-zone.c @@ -41,10 +41,18 @@ extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_im malloc zone members ------------------------------------------------------ */ +static bool is_mimalloc_zone( malloc_zone_t* zone ); + static size_t zone_size(malloc_zone_t* zone, const void* p) { - MI_UNUSED(zone); - if (!mi_is_in_heap_region(p)){ return 0; } // not our pointer, bail out - return mi_usable_size(p); + if (mi_any_heap_contains(p)) { + return mi_usable_size(p); + } + else if (!is_mimalloc_zone(zone)) { // can happen due to interpose + return zone->size(zone,p); + } + else { + return 0; + } } static void* zone_malloc(malloc_zone_t* zone, size_t size) { @@ -63,13 +71,24 @@ static void* zone_valloc(malloc_zone_t* zone, size_t size) { } static void zone_free(malloc_zone_t* zone, void* p) { - MI_UNUSED(zone); - mi_cfree(p); + if (mi_any_heap_contains(p)) { + mi_free(p); // with the page_map and pagemap_commit=1 we can use the regular free + } + else if (!is_mimalloc_zone(zone)) { // can happen due to interpose + zone->free(zone,p); + } } static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) { - MI_UNUSED(zone); - return mi_realloc(p, newsize); + if (p == NULL || mi_any_heap_contains(p)) { + return mi_realloc(p, newsize); + } + else if (!is_mimalloc_zone(zone)) { // can happen due to interpose + return zone->realloc(zone,p,newsize); + } + else { + return NULL; + } } static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) { @@ -78,12 +97,13 @@ static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) { } static void zone_destroy(malloc_zone_t* zone) { - MI_UNUSED(zone); - // todo: ignore for now? + if (!is_mimalloc_zone(zone)) { + zone->destroy(zone); + } } static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) { - size_t i; + unsigned i; for (i = 0; i < count; i++) { ps[i] = zone_malloc(zone, size); if (ps[i] == NULL) break; @@ -114,7 +134,6 @@ static boolean_t zone_claimed_address(malloc_zone_t* zone, void* p) { return mi_is_in_heap_region(p); } - /* ------------------------------------------------------ Introspection members ------------------------------------------------------ */ @@ -174,6 +193,14 @@ static boolean_t intro_zone_locked(malloc_zone_t* zone) { return false; } +// Required whenever the zone advertises version >= 9: macOS calls this from the +// atfork_child handler (_malloc_fork_child) without a NULL check. mimalloc keeps +// no zone-level locks that need reinitializing after fork, so a no-op is safe. +// Leaving it NULL makes the forked child jump to address 0 and crash in fork(). +static void intro_reinit_lock(malloc_zone_t* zone) { + MI_UNUSED(zone); +} + /* ------------------------------------------------------ At process start, override the default allocator @@ -198,6 +225,7 @@ static malloc_introspection_t mi_introspect = { #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) && !defined(__ppc__) .statistics = &intro_statistics, .zone_locked = &intro_zone_locked, + .reinit_lock = &intro_reinit_lock, #endif }; @@ -240,6 +268,9 @@ static malloc_zone_t mi_malloc_zone = { } #endif +static bool is_mimalloc_zone( malloc_zone_t* zone ) { + return (zone==NULL || zone==&mi_malloc_zone); +} #if defined(MI_OSX_INTERPOSE) && defined(MI_SHARED_LIB_EXPORT) @@ -253,11 +284,8 @@ static malloc_zone_t mi_malloc_zone = { // `malloc_zone_calloc` etc. see // ------------------------------------------------------ -static inline malloc_zone_t* mi_get_default_zone(void) -{ - static bool init; - if mi_unlikely(!init) { - init = true; +static inline malloc_zone_t* mi_get_default_zone(void) { + mi_atomic_do_once { malloc_zone_register(&mi_malloc_zone); // by calling register we avoid a zone error on free (see ) } return &mi_malloc_zone; @@ -328,7 +356,7 @@ static bool zone_check(malloc_zone_t* zone) { static malloc_zone_t* zone_from_ptr(const void* p) { MI_UNUSED(p); - return mi_get_default_zone(); + return (mi_any_heap_contains(p) ? mi_get_default_zone() : NULL); } static void zone_log(malloc_zone_t* zone, void* p) { @@ -418,9 +446,9 @@ static inline malloc_zone_t* mi_get_default_zone(void) } #if defined(__clang__) -__attribute__((constructor(0))) +__attribute__((constructor(101))) // highest priority #else -__attribute__((constructor)) // seems not supported by g++-11 on the M1 +__attribute__((constructor)) // priority level is not supported by gcc #endif __attribute__((used)) static void _mi_macos_override_malloc(void) { diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/prim.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/prim.c index 3b7d3736..9e2afdfc 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/prim.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/prim.c @@ -25,3 +25,52 @@ terms of the MIT license. A copy of the license can be found in the file #include "unix/prim.c" // mmap() (Linux, macOSX, BSD, Illumnos, Haiku, DragonFly, etc.) #endif + +// Generic process initialization +#if !defined(MI_PRIM_HAS_PROCESS_ATTACH) +#if defined(__GNUC__) || defined(__clang__) + // gcc,clang: use the constructor/destructor attribute + // which for both seem to run before regular constructors/destructors + #if defined(__clang__) + #define mi_attr_constructor __attribute__((constructor(101))) + #define mi_attr_destructor __attribute__((destructor(101))) + #else + #define mi_attr_constructor __attribute__((constructor)) + #define mi_attr_destructor __attribute__((destructor)) + #endif + static void mi_attr_constructor mi_process_attach(void) { + _mi_auto_process_init(); + } + static void mi_attr_destructor mi_process_detach(void) { + _mi_auto_process_done(); + } +#elif defined(__cplusplus) + // C++: use static initialization to detect process start/end + // This is not guaranteed to be first/last but the best we can generally do? + struct mi_init_done_t { + mi_init_done_t() { + _mi_auto_process_init(); + } + ~mi_init_done_t() { + _mi_auto_process_done(); + } + }; + static mi_init_done_t mi_init_done; + #else + #pragma message("define a way to call _mi_auto_process_init/done on your platform") +#endif +#endif + +// Generic allocator init/done callback +#ifndef MI_PRIM_HAS_ALLOCATOR_INIT +bool _mi_is_redirected(void) { + return false; +} +bool _mi_allocator_init(const char** message) { + if (message != NULL) { *message = NULL; } + return true; +} +void _mi_allocator_done(void) { + // nothing to do +} +#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/unix/prim.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/unix/prim.c index d69f5c6d..3b817c83 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/unix/prim.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/unix/prim.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -22,23 +22,32 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/atomic.h" #include "mimalloc/prim.h" #include // mmap -#include // sysconf +#include // sysconf, sleep #include // open, close, read, access +#include // getenv, arc4random_buf #if defined(__linux__) #include - #if defined(MI_NO_THP) - #include + #include // THP disable, PR_SET_VMA + #include // sysinfo + #if defined(__GLIBC__) && !defined(PR_SET_VMA) + #include #endif #if defined(__GLIBC__) - #include // linux mmap flags + #include // linux mmap flags #else #include #endif + #if defined(__riscv) || defined(_M_RISCV) + #if defined(MI_HAS_SYS_HWPROBEH) + #include + #elif defined(MI_HAS_ASM_HWPROBEH) + #include + #endif + #endif #elif defined(__APPLE__) #include #include @@ -48,6 +57,7 @@ terms of the MIT license. A copy of the license can be found in the file #if !defined(MAC_OS_X_VERSION_10_7) #define MAC_OS_X_VERSION_10_7 1070 #endif + #include #elif defined(__FreeBSD__) || defined(__DragonFly__) #include #if __FreeBSD_version >= 1200000 @@ -57,11 +67,19 @@ terms of the MIT license. A copy of the license can be found in the file #include #endif -#if defined(__linux__) || defined(__FreeBSD__) +#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__) #define MI_HAS_SYSCALL_H #include #endif +#if !defined(MADV_DONTNEED) && defined(POSIX_MADV_DONTNEED) // QNX +#define MADV_DONTNEED POSIX_MADV_DONTNEED +#endif +#if !defined(MADV_FREE) && defined(POSIX_MADV_FREE) // QNX +#define MADV_FREE POSIX_MADV_FREE +#endif + +#define MI_UNIX_LARGE_PAGE_SIZE (2*MI_MiB) // TODO: can we query the OS for this? //------------------------------------------------------------------------------------ // Use syscalls for some primitives to allow for libraries that override open/read/close etc. @@ -70,22 +88,22 @@ terms of the MIT license. A copy of the license can be found in the file // Declare inline to avoid unused function warnings. //------------------------------------------------------------------------------------ -//#if defined(MI_HAS_SYSCALL_H) && defined(SYS_open) && defined(SYS_close) && defined(SYS_read) && defined(SYS_access) -// -//static inline int mi_prim_open(const char* fpath, int open_flags) { -// return syscall(SYS_open,fpath,open_flags,0); -//} -//static inline ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) { -// return syscall(SYS_read,fd,buf,bufsize); -//} -//static inline int mi_prim_close(int fd) { -// return syscall(SYS_close,fd); -//} -//static inline int mi_prim_access(const char *fpath, int mode) { -// return syscall(SYS_access,fpath,mode); -//} -// -//#else +#if defined(MI_HAS_SYSCALL_H) && defined(SYS_open) && defined(SYS_close) && defined(SYS_read) && defined(SYS_access) + +static inline int mi_prim_open(const char* fpath, int open_flags) { + return syscall(SYS_open,fpath,open_flags,0); +} +static inline ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) { + return syscall(SYS_read,fd,buf,bufsize); +} +static inline int mi_prim_close(int fd) { + return syscall(SYS_close,fd); +} +static inline int mi_prim_access(const char *fpath, int mode) { + return syscall(SYS_access,fpath,mode); +} + +#else static inline int mi_prim_open(const char* fpath, int open_flags) { return open(fpath,open_flags); @@ -100,7 +118,7 @@ static inline int mi_prim_access(const char *fpath, int mode) { return access(fpath,mode); } -//#endif +#endif @@ -110,56 +128,149 @@ static inline int mi_prim_access(const char *fpath, int mode) { static bool unix_detect_overcommit(void) { bool os_overcommit = true; -#if defined(__linux__) - int fd = mi_prim_open("/proc/sys/vm/overcommit_memory", O_RDONLY); - if (fd >= 0) { - char buf[32]; + #if defined(__linux__) + int fd = mi_prim_open("/proc/sys/vm/overcommit_memory", O_RDONLY); + if (fd >= 0) { + char buf[32]; + ssize_t nread = mi_prim_read(fd, &buf, sizeof(buf)); + mi_prim_close(fd); + // + // 0: heuristic overcommit, 1: always overcommit, 2: never overcommit (ignore NORESERVE) + if (nread >= 1) { + os_overcommit = (buf[0] == '0' || buf[0] == '1'); + } + } + #elif defined(__FreeBSD__) + int val = 0; + size_t olen = sizeof(val); + if (sysctlbyname("vm.overcommit", &val, &olen, NULL, 0) == 0) { + os_overcommit = (val != 0); + } + #else + // default: overcommit is true + #endif + return os_overcommit; +} + +static bool unix_detect_thp(void) { + bool thp_enabled = false; + #if defined(__linux__) + int fd = mi_prim_open("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY); + if (fd >= 0) { + char buf[64]; ssize_t nread = mi_prim_read(fd, &buf, sizeof(buf)); mi_prim_close(fd); - // - // 0: heuristic overcommit, 1: always overcommit, 2: never overcommit (ignore NORESERVE) + // + // between brackets is the current value, for example: always [madvise] never if (nread >= 1) { - os_overcommit = (buf[0] == '0' || buf[0] == '1'); + if (nread > 64) { nread = 64; } + thp_enabled = (_mi_strnstr(buf,nread,"[never]") == NULL); } } -#elif defined(__FreeBSD__) - int val = 0; - size_t olen = sizeof(val); - if (sysctlbyname("vm.overcommit", &val, &olen, NULL, 0) == 0) { - os_overcommit = (val != 0); - } -#else - // default: overcommit is true -#endif - return os_overcommit; + #endif + return thp_enabled; +} + +// try to detect the physical memory dynamically (if possible) +static void unix_detect_physical_memory( size_t page_size, size_t* physical_memory_in_kib ) { + #if defined(CTL_HW) && (defined(HW_PHYSMEM64) || defined(HW_MEMSIZE)) // freeBSD, macOS + MI_UNUSED(page_size); + int64_t physical_memory = 0; + size_t length = sizeof(int64_t); + #if defined(HW_PHYSMEM64) + int mib[2] = { CTL_HW, HW_PHYSMEM64 }; + #else + int mib[2] = { CTL_HW, HW_MEMSIZE }; + #endif + const int err = sysctl(mib, 2, &physical_memory, &length, NULL, 0); + if (err==0 && physical_memory > 0) { + const int64_t phys_in_kib = physical_memory / MI_KiB; + if (phys_in_kib > 0 && (uint64_t)phys_in_kib <= SIZE_MAX) { + *physical_memory_in_kib = (size_t)phys_in_kib; + } + } + #elif defined(__linux__) + MI_UNUSED(page_size); + struct sysinfo info; _mi_memzero_var(info); + const int err = sysinfo(&info); + if (err==0 && info.mem_unit > 0 && info.totalram <= SIZE_MAX) { + if (info.mem_unit==MI_KiB) { + *physical_memory_in_kib = (size_t)info.totalram; + } + else { + size_t total = 0; + if (!mi_mul_overflow((size_t)info.totalram, (size_t)info.mem_unit, &total)) { + *physical_memory_in_kib = (total / MI_KiB); + } + } + } + #elif defined(_SC_PHYS_PAGES) // do not use by default as it might cause allocation (by using `fopen` to parse /proc/meminfo) (issue #1100) + const long pphys = sysconf(_SC_PHYS_PAGES); + const size_t psize_in_kib = page_size / MI_KiB; + if (psize_in_kib > 0 && pphys > 0 && (unsigned long)pphys <= SIZE_MAX && (size_t)pphys <= (SIZE_MAX/psize_in_kib)) { + *physical_memory_in_kib = (size_t)pphys * psize_in_kib; + } + #endif +} + +// Detect the virtual address bits (currently Linux/RISC-V only) +static size_t unix_detect_virtual_address_bits(void) { + #if defined(__riscv) || defined(_M_RISCV) + #if defined(RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS) + struct riscv_hwprobe probe = { .key = RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS, }; + // Prefer the GNU libc interface if available, as it can also use the VDSO + #if defined(MI_HAS_SYS_HWPROBEH) + if (__riscv_hwprobe(&probe, 1, 0, NULL, 0) == 0) + #else + if (syscall(__NR_riscv_hwprobe, &probe, 1, 0, NULL, 0) == 0) + #endif + { + if (probe.key != -1) { // If a key is unknown to the kernel, its key field will be cleared to -1. + return (MI_SIZE_BITS - mi_clz((uintptr_t)probe.value)); + } + } + #endif + // Fallback to checking /proc/cpuinfo for older kernels + const int fd = mi_prim_open("/proc/cpuinfo", O_RDONLY); + if (fd >= 0) { + char buf[2048]; + const ssize_t nread = mi_prim_read(fd, &buf, sizeof(buf)); + mi_prim_close(fd); + if ((nread >= 1) && (nread <= (ssize_t)sizeof(buf))) { + if (_mi_strnstr(buf, nread, "sv39")) { return 39; } + else if (_mi_strnstr(buf, nread, "sv48")) { return 48; } + else if (_mi_strnstr(buf, nread, "sv57")) { return 57; } + } + } + #endif // riscv + // default + return MI_MAX_VABITS; } void _mi_prim_mem_init( mi_os_mem_config_t* config ) { long psize = sysconf(_SC_PAGESIZE); - if (psize > 0) { + if (psize > 0 && (unsigned long)psize < SIZE_MAX) { config->page_size = (size_t)psize; config->alloc_granularity = (size_t)psize; + unix_detect_physical_memory(config->page_size, &config->physical_memory_in_kib); } - config->large_page_size = 2*MI_MiB; // TODO: can we query the OS for this? + config->large_page_size = MI_UNIX_LARGE_PAGE_SIZE; config->has_overcommit = unix_detect_overcommit(); config->has_partial_free = true; // mmap can free in parts config->has_virtual_reserve = true; // todo: check if this true for NetBSD? (for anonymous mmap with PROT_NONE) + config->has_transparent_huge_pages = unix_detect_thp(); + config->virtual_address_bits = unix_detect_virtual_address_bits(); // disable transparent huge pages for this process? #if (defined(__linux__) || defined(__ANDROID__)) && defined(PR_GET_THP_DISABLE) - #if defined(MI_NO_THP) - if (true) - #else - if (!mi_option_is_enabled(mi_option_allow_large_os_pages)) // disable THP also if large OS pages are not allowed in the options - #endif + if (!mi_option_is_enabled(mi_option_allow_thp)) // disable THP if requested through an option { - int val = 0; - if (prctl(PR_GET_THP_DISABLE, &val, 0, 0, 0) != 0) { + config->has_transparent_huge_pages = false; + if (prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0) == 0) { // -1 on error, 1 if already disabled // Most likely since distros often come with always/madvise settings. - val = 1; // Disabling only for mimalloc process rather than touching system wide settings - (void)prctl(PR_SET_THP_DISABLE, &val, 0, 0, 0); + (void)prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0); } } #endif @@ -171,6 +282,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config ) //--------------------------------------------- int _mi_prim_free(void* addr, size_t size ) { + if (size==0) return 0; bool err = (munmap(addr, size) == -1); return (err ? errno : 0); } @@ -180,22 +292,38 @@ int _mi_prim_free(void* addr, size_t size ) { // mmap //--------------------------------------------- +// return errno on failure static int unix_madvise(void* addr, size_t size, int advice) { #if defined(__sun) - return madvise((caddr_t)addr, size, advice); // Solaris needs cast (issue #520) + const int res = madvise((caddr_t)addr, size, advice); // Solaris needs cast (issue #520) + return (res==0 ? 0 : errno); + #elif defined(__QNX__) + return posix_madvise(addr, size, advice); // posix returns errno #else - return madvise(addr, size, advice); + const int res = madvise(addr, size, advice); // linux returns -1 on failure and sets errno + return (res==0 ? 0 : errno); #endif } -static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) { +static void* unix_mmap_prim(void* addr, size_t size, int protect_flags, int flags, int fd) { + void* p = mmap(addr, size, protect_flags, flags, fd, 0 /* offset */); + #if defined(__linux__) && defined(PR_SET_VMA) + if (p!=MAP_FAILED && p!=NULL) { + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, size, "mimalloc"); + } + #endif + return p; +} + +static void* unix_mmap_prim_aligned(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) { MI_UNUSED(try_alignment); void* p = NULL; #if defined(MAP_ALIGNED) // BSD if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) { - size_t n = mi_bsr(try_alignment); + size_t n = 0; + mi_bsr(try_alignment, &n); if (((size_t)1 << n) == try_alignment && n >= 12 && n <= 30) { // alignment is a power of 2 and 4096 <= alignment <= 1GiB - p = mmap(addr, size, protect_flags, flags | MAP_ALIGNED(n), fd, 0); + p = unix_mmap_prim(addr, size, protect_flags, flags | MAP_ALIGNED(n), fd); if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) { int err = errno; _mi_trace_message("unable to directly request aligned OS memory (error: %d (0x%x), size: 0x%zx bytes, alignment: 0x%zx, hint address: %p)\n", err, err, size, try_alignment, addr); @@ -206,7 +334,7 @@ static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int p } #elif defined(MAP_ALIGN) // Solaris if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) { - p = mmap((void*)try_alignment, size, protect_flags, flags | MAP_ALIGN, fd, 0); // addr parameter is the required alignment + p = unix_mmap_prim((void*)try_alignment, size, protect_flags, flags | MAP_ALIGN, fd); // addr parameter is the required alignment if (p!=MAP_FAILED) return p; // fall back to regular mmap } @@ -216,7 +344,7 @@ static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int p if (addr == NULL) { void* hint = _mi_os_get_aligned_hint(try_alignment, size); if (hint != NULL) { - p = mmap(hint, size, protect_flags, flags, fd, 0); + p = unix_mmap_prim(hint, size, protect_flags, flags, fd); if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) { #if MI_TRACK_ENABLED // asan sometimes does not instrument errno correctly? int err = 0; @@ -231,7 +359,7 @@ static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int p } #endif // regular mmap - p = mmap(addr, size, protect_flags, flags, fd, 0); + p = unix_mmap_prim(addr, size, protect_flags, flags, fd); if (p!=MAP_FAILED) return p; // failed to allocate return NULL; @@ -241,7 +369,7 @@ static int unix_mmap_fd(void) { #if defined(VM_MAKE_TAG) // macOS: tracking anonymous page with a specific ID. (All up to 98 are taken officially but LLVM sanitizers had taken 99) int os_tag = (int)mi_option_get(mi_option_os_tag); - if (os_tag < 100 || os_tag > 255) { os_tag = 100; } + if (os_tag < 100 || os_tag > 255) { os_tag = 254; } return VM_MAKE_TAG(os_tag); #else return -1; @@ -265,7 +393,7 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD #endif // huge page allocation - if ((large_only || _mi_os_use_large_page(size, try_alignment)) && allow_large) { + if (allow_large && (large_only || (_mi_os_canuse_large_page(size, try_alignment) && mi_option_is_enabled(mi_option_allow_large_os_pages)))) { static _Atomic(size_t) large_page_try_ok; // = 0; size_t try_ok = mi_atomic_load_acquire(&large_page_try_ok); if (!large_only && try_ok > 0) { @@ -285,8 +413,8 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec lflags |= MAP_HUGETLB; #endif #ifdef MAP_HUGE_1GB - static bool mi_huge_pages_available = true; - if ((size % MI_GiB) == 0 && mi_huge_pages_available) { + static _Atomic(size_t) mi_huge_1gib_pages_unavailable; + if (large_only && (size % MI_GiB) == 0 && (mi_atomic_load_relaxed(&mi_huge_1gib_pages_unavailable)==0)) { lflags |= MAP_HUGE_1GB; } else @@ -302,13 +430,15 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec if (large_only || lflags != flags) { // try large OS page allocation *is_large = true; - p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd); + p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, lflags, lfd); #ifdef MAP_HUGE_1GB if (p == NULL && (lflags & MAP_HUGE_1GB) == MAP_HUGE_1GB) { - mi_huge_pages_available = false; // don't try huge 1GiB pages again - _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (errno: %i)\n", errno); + mi_atomic_store_relaxed(&mi_huge_1gib_pages_unavailable,1); // don't try huge 1GiB pages again + if (large_only) { + _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (errno: %i)\n", errno); + } lflags = ((lflags & ~MAP_HUGE_1GB) | MAP_HUGE_2MB); - p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd); + p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, lflags, lfd); } #endif if (large_only) return p; @@ -321,8 +451,9 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec // regular allocation if (p == NULL) { *is_large = false; - p = unix_mmap_prim(addr, size, try_alignment, protect_flags, flags, fd); - if (p != NULL) { + p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, flags, fd); + #if !defined(MI_NO_THP) + if (p != NULL && allow_large && mi_option_is_enabled(mi_option_allow_thp) && _mi_os_canuse_large_page(size, try_alignment)) { #if defined(MADV_HUGEPAGE) // Many Linux systems don't allow MAP_HUGETLB but they support instead // transparent huge pages (THP). Generally, it is not required to call `madvise` with MADV_HUGE @@ -330,35 +461,31 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec // in that case -- in particular for our large regions (in `memory.c`). // However, some systems only allow THP if called with explicit `madvise`, so // when large OS pages are enabled for mimalloc, we call `madvise` anyways. - if (allow_large && _mi_os_use_large_page(size, try_alignment)) { - if (unix_madvise(p, size, MADV_HUGEPAGE) == 0) { - *is_large = true; // possibly - }; - } + if (unix_madvise(p, size, MADV_HUGEPAGE) == 0) { + // *is_large = true; // possibly + }; #elif defined(__sun) - if (allow_large && _mi_os_use_large_page(size, try_alignment)) { - struct memcntl_mha cmd = {0}; - cmd.mha_pagesize = _mi_os_large_page_size(); - cmd.mha_cmd = MHA_MAPSIZE_VA; - if (memcntl((caddr_t)p, size, MC_HAT_ADVISE, (caddr_t)&cmd, 0, 0) == 0) { - *is_large = true; - } + struct memcntl_mha cmd = {0}; + cmd.mha_pagesize = _mi_os_large_page_size(); + cmd.mha_cmd = MHA_MAPSIZE_VA; + if (memcntl((caddr_t)p, size, MC_HAT_ADVISE, (caddr_t)&cmd, 0, 0) == 0) { + // *is_large = true; // possibly } #endif } + #endif } return p; } // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. -int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { +int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); mi_assert_internal(commit || !allow_large); mi_assert_internal(try_alignment > 0); - *is_zero = true; int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE); - *addr = unix_mmap(NULL, size, try_alignment, protect_flags, false, allow_large, is_large); + *addr = unix_mmap(hint_addr, size, try_alignment, protect_flags, false, allow_large, is_large); return (*addr != NULL ? 0 : errno); } @@ -368,7 +495,7 @@ int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_la //--------------------------------------------- static void unix_mprotect_hint(int err) { - #if defined(__linux__) && (MI_SECURE>=2) // guard page around every mimalloc page + #if defined(__linux__) && (MI_SECURE>=5) // guard page around every mimalloc page if (err == ENOMEM) { _mi_warning_message("The next warning may be caused by a low memory map limit.\n" " On Linux this is controlled by the vm.max_map_count -- maybe increase it?\n" @@ -379,6 +506,10 @@ static void unix_mprotect_hint(int err) { #endif } + + + + int _mi_prim_commit(void* start, size_t size, bool* is_zero) { // commit: ensure we can access the area // note: we may think that *is_zero can be true since the memory @@ -394,43 +525,66 @@ int _mi_prim_commit(void* start, size_t size, bool* is_zero) { return err; } +int _mi_prim_reuse(void* start, size_t size) { + MI_UNUSED(start); MI_UNUSED(size); + #if defined(__APPLE__) && defined(MADV_FREE_REUSE) + return unix_madvise(start, size, MADV_FREE_REUSE); + #endif + return 0; +} + int _mi_prim_decommit(void* start, size_t size, bool* needs_recommit) { int err = 0; - // decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE) - err = unix_madvise(start, size, MADV_DONTNEED); - #if !MI_DEBUG && !MI_SECURE - *needs_recommit = false; + #if 1 + #if defined(__APPLE__) && defined(MADV_FREE_REUSABLE) + // decommit on macOS: use MADV_FREE_REUSABLE as it does immediate rss accounting (issue #1097) + err = unix_madvise(start, size, MADV_FREE_REUSABLE); + if (err) { err = unix_madvise(start, size, MADV_DONTNEED); } + #else + // decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE) + err = unix_madvise(start, size, MADV_DONTNEED); + #endif + #if !MI_DEBUG && MI_SECURE<=2 + *needs_recommit = false; + #else + *needs_recommit = true; + mprotect(start, size, PROT_NONE); + #endif #else + // decommit: use mmap with MAP_FIXED and PROT_NONE to discard the existing memory (and reduce rss) *needs_recommit = true; - mprotect(start, size, PROT_NONE); + const int fd = unix_mmap_fd(); + void* p = mmap(start, size, PROT_NONE, (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE), fd, 0); + if (p != start) { err = errno; } #endif - /* - // decommit: use mmap with MAP_FIXED and PROT_NONE to discard the existing memory (and reduce rss) - *needs_recommit = true; - const int fd = unix_mmap_fd(); - void* p = mmap(start, size, PROT_NONE, (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE), fd, 0); - if (p != start) { err = errno; } - */ return err; } int _mi_prim_reset(void* start, size_t size) { - // We try to use `MADV_FREE` as that is the fastest. A drawback though is that it + int err = 0; + + // on macOS can use MADV_FREE_REUSABLE (but we disable this for now as it seems slower) + #if 0 && defined(__APPLE__) && defined(MADV_FREE_REUSABLE) + err = unix_madvise(start, size, MADV_FREE_REUSABLE); + if (err==0) return 0; + // fall through + #endif + + #if defined(MADV_FREE) + // Otherwise, we try to use `MADV_FREE` as that is the fastest. A drawback though is that it // will not reduce the `rss` stats in tools like `top` even though the memory is available // to other processes. With the default `MIMALLOC_PURGE_DECOMMITS=1` we ensure that by // default `MADV_DONTNEED` is used though. - #if defined(MADV_FREE) static _Atomic(size_t) advice = MI_ATOMIC_VAR_INIT(MADV_FREE); int oadvice = (int)mi_atomic_load_relaxed(&advice); - int err; - while ((err = unix_madvise(start, size, oadvice)) != 0 && errno == EAGAIN) { errno = 0; }; - if (err != 0 && errno == EINVAL && oadvice == MADV_FREE) { + while ((err = unix_madvise(start, size, oadvice)) != 0 && err == EAGAIN) { /* try again */ }; + if (err == EINVAL && oadvice == MADV_FREE) { // if MADV_FREE is not supported, fall back to MADV_DONTNEED from now on mi_atomic_store_release(&advice, (size_t)MADV_DONTNEED); err = unix_madvise(start, size, MADV_DONTNEED); } #else - int err = unix_madvise(start, size, MADV_DONTNEED); + err = unix_madvise(start, size, MADV_DONTNEED); #endif return err; } @@ -468,7 +622,7 @@ static long mi_prim_mbind(void* start, unsigned long len, unsigned long mode, co int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) { bool is_large = true; *is_zero = true; - *addr = unix_mmap(hint_addr, size, MI_SEGMENT_SIZE, PROT_READ | PROT_WRITE, true, true, &is_large); + *addr = unix_mmap(hint_addr, size, MI_ARENA_SLICE_ALIGN, PROT_READ | PROT_WRITE, true, true, &is_large); if (*addr != NULL && numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes unsigned long numa_mask = (1UL << numa_node); // TODO: does `mbind` work correctly for huge OS pages? should we @@ -477,7 +631,7 @@ int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bo long err = mi_prim_mbind(*addr, size, MPOL_PREFERRED, &numa_mask, 8*MI_INTPTR_SIZE, 0); if (err != 0) { err = errno; - _mi_warning_message("failed to bind huge (1GiB) pages to numa node %d (error: %d (0x%x))\n", numa_node, err, err); + _mi_warning_message("failed to bind huge (1GiB) pages to numa node %d (error: %ld (0x%lx))\n", numa_node, err, err); } } return (*addr != NULL ? 0 : errno); @@ -576,35 +730,39 @@ size_t _mi_prim_numa_node_count(void) { #include -#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) - -mi_msecs_t _mi_prim_clock_now(void) { - struct timespec t; - #ifdef CLOCK_MONOTONIC - clock_gettime(CLOCK_MONOTONIC, &t); +// low resolution timer +static mi_msecs_t mi_prim_clock_now_lowres(void) { + const int64_t ticks = (int64_t)clock(); + #if !defined(CLOCKS_PER_SEC) + return ticks; #else - clock_gettime(CLOCK_REALTIME, &t); + if (CLOCKS_PER_SEC <= 0 || CLOCKS_PER_SEC == 1000) { + return ticks; + } + else if (CLOCKS_PER_SEC > 0 && CLOCKS_PER_SEC < 1000) { + return ticks * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + } + else { + return ticks / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + } #endif - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); } -#else - -// low resolution timer mi_msecs_t _mi_prim_clock_now(void) { - #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) - return (mi_msecs_t)clock(); - #elif (CLOCKS_PER_SEC < 1000) - return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); - #else - return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); - #endif + #if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) + #ifdef CLOCK_MONOTONIC + const clockid_t clockid = CLOCK_MONOTONIC; + #else + const clockid_t clockid = CLOCK_REALTIME; + #endif + struct timespec t; + if (clock_gettime(clockid,&t) == 0) { + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000L); + } + #endif + return mi_prim_clock_now_lowres(); } -#endif - - - //---------------------------------------------------------------- // Process info @@ -711,28 +869,28 @@ static char** mi_get_environ(void) { return environ; } #endif -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { - if (name==NULL) return false; +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { + if (name==NULL) return -1; const size_t len = _mi_strlen(name); - if (len == 0) return false; + if (len == 0) return -1; char** env = mi_get_environ(); - if (env == NULL) return false; + if (env == NULL) return -1; // compare up to 10000 entries for (int i = 0; i < 10000 && env[i] != NULL; i++) { const char* s = env[i]; if (_mi_strnicmp(name, s, len) == 0 && s[len] == '=') { // case insensitive // found it _mi_strlcpy(result, s + len + 1, result_size); - return true; + return 1; // success } } - return false; + return 0; // not found } #else // fallback: use standard C `getenv` but this cannot be used while initializing the C runtime -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { // cannot call getenv() when still initializing the C runtime. - if (_mi_preloading()) return false; + if (_mi_preloading()) return -1; // error, try again later const char* s = getenv(name); if (s == NULL) { // we check the upper case name too. @@ -744,9 +902,9 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { buf[len] = 0; s = getenv(buf); } - if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; + if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return 0; // not found _mi_strlcpy(result, s, result_size); - return true; + return 1; // success } #endif // !MI_USE_ENVIRON @@ -760,7 +918,7 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { #include bool _mi_prim_random_buf(void* buf, size_t buf_len) { - // We prefere CCRandomGenerateBytes as it returns an error code while arc4random_buf + // We prefer CCRandomGenerateBytes as it returns an error code while arc4random_buf // may fail silently on macOS. See PR #390, and return (CCRandomGenerateBytes(buf, buf_len) == kCCSuccess); } @@ -770,7 +928,6 @@ bool _mi_prim_random_buf(void* buf, size_t buf_len) { defined(__sun) || \ (defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)) -#include bool _mi_prim_random_buf(void* buf, size_t buf_len) { arc4random_buf(buf, buf_len); return true; @@ -835,18 +992,22 @@ bool _mi_prim_random_buf(void* buf, size_t buf_len) { #if defined(MI_USE_PTHREADS) // use pthread local storage keys to detect thread ending -// (and used with MI_TLS_PTHREADS for the default heap) +// (and used with MI_TLS_PTHREADS for the default theap) pthread_key_t _mi_heap_default_key = (pthread_key_t)(-1); static void mi_pthread_done(void* value) { if (value!=NULL) { - _mi_thread_done((mi_heap_t*)value); + _mi_thread_done((mi_theap_t*)value); } } void _mi_prim_thread_init_auto_done(void) { mi_assert_internal(_mi_heap_default_key == (pthread_key_t)(-1)); - pthread_key_create(&_mi_heap_default_key, &mi_pthread_done); + const int err = pthread_key_create(&_mi_heap_default_key, &mi_pthread_done); + if (err!=0) { + _mi_error_message(err,"unable to create a pthread thread local key (error %d (0x%x))", err, err); + _mi_heap_default_key = (pthread_key_t)(-1); + }; } void _mi_prim_thread_done_auto_done(void) { @@ -855,9 +1016,9 @@ void _mi_prim_thread_done_auto_done(void) { } } -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { +void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { if (_mi_heap_default_key != (pthread_key_t)(-1)) { // can happen during recursive invocation on freeBSD - pthread_setspecific(_mi_heap_default_key, heap); + pthread_setspecific(_mi_heap_default_key, theap); } } @@ -871,8 +1032,16 @@ void _mi_prim_thread_done_auto_done(void) { // nothing } -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { - MI_UNUSED(heap); +void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); } #endif + +bool _mi_prim_thread_is_in_threadpool(void) { + return false; +} + +void _mi_prim_thread_yield(void) { + sleep(0); +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/wasi/prim.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/wasi/prim.c index e95f67f5..2d69b724 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/wasi/prim.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/wasi/prim.c @@ -9,11 +9,11 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/atomic.h" #include "mimalloc/prim.h" #include // fputs #include // getenv +#include // sbrk, sleep //--------------------------------------------- // Initialize @@ -22,7 +22,7 @@ terms of the MIT license. A copy of the license can be found in the file void _mi_prim_mem_init( mi_os_mem_config_t* config ) { config->page_size = 64*MI_KiB; // WebAssembly has a fixed page size: 64KiB config->alloc_granularity = 16; - config->has_overcommit = false; + config->has_overcommit = false; config->has_partial_free = false; config->has_virtual_reserve = false; } @@ -33,7 +33,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config ) { int _mi_prim_free(void* addr, size_t size ) { MI_UNUSED(addr); MI_UNUSED(size); - // wasi heap cannot be shrunk + // wasi theap cannot be shrunk return 0; } @@ -43,8 +43,6 @@ int _mi_prim_free(void* addr, size_t size ) { //--------------------------------------------- #if defined(MI_USE_SBRK) - #include // for sbrk - static void* mi_memory_grow( size_t size ) { void* p = sbrk(size); if (p == (void*)(-1)) return NULL; @@ -63,7 +61,7 @@ int _mi_prim_free(void* addr, size_t size ) { #endif #if defined(MI_USE_PTHREADS) -static pthread_mutex_t mi_heap_grow_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t mi_theap_grow_mutex = PTHREAD_MUTEX_INITIALIZER; #endif static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { @@ -71,11 +69,11 @@ static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { if (try_alignment <= 1) { // `sbrk` is not thread safe in general so try to protect it (we could skip this on WASM but leave it in for now) #if defined(MI_USE_PTHREADS) - pthread_mutex_lock(&mi_heap_grow_mutex); + pthread_mutex_lock(&mi_theap_grow_mutex); #endif p = mi_memory_grow(size); #if defined(MI_USE_PTHREADS) - pthread_mutex_unlock(&mi_heap_grow_mutex); + pthread_mutex_unlock(&mi_theap_grow_mutex); #endif } else { @@ -85,21 +83,21 @@ static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { // between getting the current size and actual allocation // (also, `sbrk` is not thread safe in general) #if defined(MI_USE_PTHREADS) - pthread_mutex_lock(&mi_heap_grow_mutex); + pthread_mutex_lock(&mi_theap_grow_mutex); #endif { void* current = mi_memory_grow(0); // get current size if (current != NULL) { - void* aligned_current = mi_align_up_ptr(current, try_alignment); // and align from there to minimize wasted space + void* aligned_current = _mi_align_up_ptr(current, try_alignment); // and align from there to minimize wasted space alloc_size = _mi_align_up( ((uint8_t*)aligned_current - (uint8_t*)current) + size, _mi_os_page_size()); base = mi_memory_grow(alloc_size); } } #if defined(MI_USE_PTHREADS) - pthread_mutex_unlock(&mi_heap_grow_mutex); + pthread_mutex_unlock(&mi_theap_grow_mutex); #endif if (base != NULL) { - p = mi_align_up_ptr(base, try_alignment); + p = _mi_align_up_ptr(base, try_alignment); if ((uint8_t*)p + size > (uint8_t*)base + alloc_size) { // another thread used wasm_memory_grow/sbrk in-between and we do not have enough // space after alignment. Give up (and waste the space as we cannot shrink :-( ) @@ -120,8 +118,8 @@ static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { } // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. -int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { - MI_UNUSED(allow_large); MI_UNUSED(commit); +int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { + MI_UNUSED(allow_large); MI_UNUSED(commit); MI_UNUSED(hint_addr); *is_large = false; *is_zero = false; *addr = mi_prim_mem_grow(size, try_alignment); @@ -134,7 +132,7 @@ int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_la //--------------------------------------------- int _mi_prim_commit(void* addr, size_t size, bool* is_zero) { - MI_UNUSED(addr); MI_UNUSED(size); + MI_UNUSED(addr); MI_UNUSED(size); *is_zero = false; return 0; } @@ -150,6 +148,11 @@ int _mi_prim_reset(void* addr, size_t size) { return 0; } +int _mi_prim_reuse(void* addr, size_t size) { + MI_UNUSED(addr); MI_UNUSED(size); + return 0; +} + int _mi_prim_protect(void* addr, size_t size, bool protect) { MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect); return 0; @@ -182,33 +185,39 @@ size_t _mi_prim_numa_node_count(void) { #include -#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) - -mi_msecs_t _mi_prim_clock_now(void) { - struct timespec t; - #ifdef CLOCK_MONOTONIC - clock_gettime(CLOCK_MONOTONIC, &t); +// low resolution timer +static mi_msecs_t mi_prim_clock_now_lowres(void) { + const int64_t ticks = (int64_t)clock(); + #if !defined(CLOCKS_PER_SEC) + return ticks; #else - clock_gettime(CLOCK_REALTIME, &t); + if (CLOCKS_PER_SEC <= 0 || CLOCKS_PER_SEC == 1000) { + return ticks; + } + else if (CLOCKS_PER_SEC > 0 && CLOCKS_PER_SEC < 1000) { + return ticks * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + } + else { + return ticks / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + } #endif - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); } -#else - -// low resolution timer mi_msecs_t _mi_prim_clock_now(void) { - #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) - return (mi_msecs_t)clock(); - #elif (CLOCKS_PER_SEC < 1000) - return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); - #else - return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); - #endif + #if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) + #ifdef CLOCK_MONOTONIC + const clockid_t clockid = CLOCK_MONOTONIC; + #else + const clockid_t clockid = CLOCK_REALTIME; + #endif + struct timespec t; + if (clock_gettime(clockid,&t) == 0) { + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000L); + } + #endif + return mi_prim_clock_now_lowres(); } -#endif - //---------------------------------------------------------------- // Process info @@ -234,9 +243,9 @@ void _mi_prim_out_stderr( const char* msg ) { // Environment //---------------------------------------------------------------- -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { // cannot call getenv() when still initializing the C runtime. - if (_mi_preloading()) return false; + if (_mi_preloading()) return -1; // error, try again later const char* s = getenv(name); if (s == NULL) { // we check the upper case name too. @@ -248,9 +257,9 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { buf[len] = 0; s = getenv(buf); } - if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; + if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return 0; // not found _mi_strlcpy(result, s, result_size); - return true; + return 1; // found } @@ -275,6 +284,14 @@ void _mi_prim_thread_done_auto_done(void) { // nothing } -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { - MI_UNUSED(heap); +void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); +} + +bool _mi_prim_thread_is_in_threadpool(void) { + return false; +} + +void _mi_prim_thread_yield(void) { + sleep(0); } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/windows/prim.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/windows/prim.c index 5074ad4c..6c4a51dd 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/windows/prim.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/prim/windows/prim.c @@ -9,15 +9,26 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" -#include "mimalloc/atomic.h" #include "mimalloc/prim.h" #include // fputs, stderr +#include // atexit +// xbox has no console IO +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define MI_HAS_CONSOLE_IO +#endif //--------------------------------------------- // Dynamically bind Windows API points for portability //--------------------------------------------- +#if defined(_MSC_VER) +#pragma warning(disable:4996) // don't use GetVersionExW +#endif + +static DWORD win_major_version = 6; +static DWORD win_minor_version = 0; + // We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016. // So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility) // NtAllocateVirtualAllocEx is used for huge OS page allocation (1GiB) @@ -46,32 +57,44 @@ typedef struct MI_MEM_ADDRESS_REQUIREMENTS_S { #define MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE 0x00000010 #include -typedef PVOID (__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG); -typedef NTSTATUS (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG); +typedef PVOID (__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG); +typedef LONG (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG); // avoid NTSTATUS as it is not defined on xbox (pr #1084) static PVirtualAlloc2 pVirtualAlloc2 = NULL; static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL; -// Similarly, GetNumaProcesorNodeEx is only supported since Windows 7 +// Similarly, GetNumaProcessorNodeEx is only supported since Windows 7 (and GetNumaNodeProcessorMask is not supported on xbox) typedef struct MI_PROCESSOR_NUMBER_S { WORD Group; BYTE Number; BYTE Reserved; } MI_PROCESSOR_NUMBER; typedef VOID (__stdcall *PGetCurrentProcessorNumberEx)(MI_PROCESSOR_NUMBER* ProcNumber); typedef BOOL (__stdcall *PGetNumaProcessorNodeEx)(MI_PROCESSOR_NUMBER* Processor, PUSHORT NodeNumber); typedef BOOL (__stdcall* PGetNumaNodeProcessorMaskEx)(USHORT Node, PGROUP_AFFINITY ProcessorMask); typedef BOOL (__stdcall *PGetNumaProcessorNode)(UCHAR Processor, PUCHAR NodeNumber); +typedef BOOL (__stdcall* PGetNumaNodeProcessorMask)(UCHAR Node, PULONGLONG ProcessorMask); +typedef BOOL (__stdcall* PGetNumaHighestNodeNumber)(PULONG Node); static PGetCurrentProcessorNumberEx pGetCurrentProcessorNumberEx = NULL; static PGetNumaProcessorNodeEx pGetNumaProcessorNodeEx = NULL; static PGetNumaNodeProcessorMaskEx pGetNumaNodeProcessorMaskEx = NULL; static PGetNumaProcessorNode pGetNumaProcessorNode = NULL; +static PGetNumaNodeProcessorMask pGetNumaNodeProcessorMask = NULL; +static PGetNumaHighestNodeNumber pGetNumaHighestNodeNumber = NULL; + +// Not available on xbox +typedef SIZE_T(__stdcall* PGetLargePageMinimum)(VOID); +static PGetLargePageMinimum pGetLargePageMinimum = NULL; + +// Available after Windows XP +typedef BOOL (__stdcall *PGetPhysicallyInstalledSystemMemory)( PULONGLONG TotalMemoryInKilobytes ); +typedef BOOL (__stdcall* PGetVersionExW)(LPOSVERSIONINFOW lpVersionInformation); + + //--------------------------------------------- // Enable large page support dynamically (if possible) //--------------------------------------------- -static bool win_enable_large_os_pages(size_t* large_page_size) +static bool win_enable_large_os_pages_once(size_t* large_page_size) { - static bool large_initialized = false; - if (large_initialized) return (_mi_os_large_page_size() > 0); - large_initialized = true; + if (pGetLargePageMinimum==NULL) return false; // no large page support (xbox etc.) // Try to see if large OS pages are supported // To use large pages on Windows, we first need access permission @@ -90,8 +113,8 @@ static bool win_enable_large_os_pages(size_t* large_page_size) if (ok) { err = GetLastError(); ok = (err == ERROR_SUCCESS); - if (ok && large_page_size != NULL) { - *large_page_size = GetLargePageMinimum(); + if (ok && large_page_size != NULL && pGetLargePageMinimum != NULL) { + *large_page_size = (*pGetLargePageMinimum)(); } } } @@ -104,24 +127,42 @@ static bool win_enable_large_os_pages(size_t* large_page_size) return (ok!=0); } +static bool win_enable_large_os_pages(size_t* large_page_size) { + mi_atomic_do_once { + win_enable_large_os_pages_once(large_page_size); + } + return (_mi_os_large_page_size() > 0); +} + //--------------------------------------------- // Initialize //--------------------------------------------- +static DWORD win_allocation_granularity = 64*MI_KiB; + void _mi_prim_mem_init( mi_os_mem_config_t* config ) { config->has_overcommit = false; config->has_partial_free = false; config->has_virtual_reserve = true; + // get the page size - SYSTEM_INFO si; + SYSTEM_INFO si; _mi_memzero_var(si); GetSystemInfo(&si); if (si.dwPageSize > 0) { config->page_size = si.dwPageSize; } - if (si.dwAllocationGranularity > 0) { config->alloc_granularity = si.dwAllocationGranularity; } + if (si.dwAllocationGranularity > 0) { + config->alloc_granularity = si.dwAllocationGranularity; + win_allocation_granularity = si.dwAllocationGranularity; + } + // get virtual address bits + if ((uintptr_t)si.lpMaximumApplicationAddress > 0) { + const size_t vbits = MI_SIZE_BITS - mi_clz((uintptr_t)si.lpMaximumApplicationAddress); + config->virtual_address_bits = vbits; + } + // get the VirtualAlloc2 function - HINSTANCE hDll; - hDll = LoadLibrary(TEXT("kernelbase.dll")); + HINSTANCE hDll = LoadLibrary(TEXT("kernelbase.dll")); if (hDll != NULL) { // use VirtualAlloc2FromApp if possible as it is available to Windows store apps pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2FromApp"); @@ -141,8 +182,32 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config ) pGetNumaProcessorNodeEx = (PGetNumaProcessorNodeEx)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNodeEx"); pGetNumaNodeProcessorMaskEx = (PGetNumaNodeProcessorMaskEx)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMaskEx"); pGetNumaProcessorNode = (PGetNumaProcessorNode)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNode"); + pGetNumaNodeProcessorMask = (PGetNumaNodeProcessorMask)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMask"); + pGetNumaHighestNodeNumber = (PGetNumaHighestNodeNumber)(void (*)(void))GetProcAddress(hDll, "GetNumaHighestNodeNumber"); + pGetLargePageMinimum = (PGetLargePageMinimum)(void (*)(void))GetProcAddress(hDll, "GetLargePageMinimum"); + // Get physical memory (not available on XP, so check dynamically) + PGetPhysicallyInstalledSystemMemory pGetPhysicallyInstalledSystemMemory = (PGetPhysicallyInstalledSystemMemory)(void (*)(void))GetProcAddress(hDll,"GetPhysicallyInstalledSystemMemory"); + if (pGetPhysicallyInstalledSystemMemory != NULL) { + ULONGLONG memInKiB = 0; + if ((*pGetPhysicallyInstalledSystemMemory)(&memInKiB)) { + if (memInKiB > 0 && memInKiB <= SIZE_MAX) { + config->physical_memory_in_kib = (size_t)memInKiB; + } + } + } + // Get Windows version + PGetVersionExW pGetVersionExW = (PGetVersionExW)(void (*)(void))GetProcAddress(hDll, "GetVersionExW"); + if (pGetVersionExW != NULL) { + OSVERSIONINFOW version; _mi_memzero_var(version); + version.dwOSVersionInfoSize = sizeof(version); + if ((*pGetVersionExW)(&version)) { + win_major_version = version.dwMajorVersion; + win_minor_version = version.dwMinorVersion; + } + } FreeLibrary(hDll); } + // Enable large/huge OS page support? if (mi_option_is_enabled(mi_option_allow_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) { win_enable_large_os_pages(&config->large_page_size); } @@ -162,9 +227,9 @@ int _mi_prim_free(void* addr, size_t size ) { // In mi_os_mem_alloc_aligned the fallback path may have returned a pointer inside // the memory region returned by VirtualAlloc; in that case we need to free using // the start of the region. - MEMORY_BASIC_INFORMATION info = { 0 }; + MEMORY_BASIC_INFORMATION info; _mi_memzero_var(info); VirtualQuery(addr, &info, sizeof(info)); - if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < (ptrdiff_t)MI_SEGMENT_SIZE) { + if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < (ptrdiff_t)(4*MI_MiB)) { errcode = 0; err = (VirtualFree(info.AllocationBase, 0, MEM_RELEASE) == 0); if (err) { errcode = GetLastError(); } @@ -192,7 +257,7 @@ static void* win_virtual_alloc_prim_once(void* addr, size_t size, size_t try_ali } #endif // on modern Windows try use VirtualAlloc2 for aligned allocation - if (try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0 && pVirtualAlloc2 != NULL) { + if (addr == NULL && try_alignment > win_allocation_granularity && (try_alignment % _mi_os_page_size()) == 0 && pVirtualAlloc2 != NULL) { MI_MEM_ADDRESS_REQUIREMENTS reqs = { 0, 0, 0 }; reqs.Alignment = try_alignment; MI_MEM_EXTENDED_PARAMETER param = { {0, 0}, {0} }; @@ -228,10 +293,10 @@ static void* win_virtual_alloc_prim(void* addr, size_t size, size_t try_alignmen // success, return the address return p; } - else if (max_retry_msecs > 0 && (try_alignment <= 2*MI_SEGMENT_ALIGN) && + else if (max_retry_msecs > 0 && (try_alignment <= 8*MI_MiB) && (flags&MEM_COMMIT) != 0 && (flags&MEM_LARGE_PAGES) == 0 && win_is_out_of_memory_error(GetLastError())) { - // if committing regular memory and being out-of-memory, + // if committing regular memory and being out-of-memory, // keep trying for a bit in case memory frees up after all. See issue #894 _mi_warning_message("out-of-memory on OS allocation, try again... (attempt %lu, 0x%zx bytes, error code: 0x%x, address: %p, alignment: 0x%zx, flags: 0x%x)\n", tries, size, GetLastError(), addr, try_alignment, flags); long sleep_msecs = tries*40; // increasing waits @@ -252,8 +317,9 @@ static void* win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DW static _Atomic(size_t) large_page_try_ok; // = 0; void* p = NULL; // Try to allocate large OS pages (2MiB) if allowed or required. - if ((large_only || _mi_os_use_large_page(size, try_alignment)) - && allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) { + if ((large_only || (_mi_os_canuse_large_page(size, try_alignment) && mi_option_is_enabled(mi_option_allow_large_os_pages))) + && allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) + { size_t try_ok = mi_atomic_load_acquire(&large_page_try_ok); if (!large_only && try_ok > 0) { // if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive. @@ -280,14 +346,14 @@ static void* win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DW return p; } -int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { +int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) { mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); mi_assert_internal(commit || !allow_large); mi_assert_internal(try_alignment > 0); *is_zero = true; int flags = MEM_RESERVE; if (commit) { flags |= MEM_COMMIT; } - *addr = win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large); + *addr = win_virtual_alloc(hint_addr, size, try_alignment, flags, false, allow_large, is_large); return (*addr != NULL ? 0 : (int)GetLastError()); } @@ -316,7 +382,7 @@ int _mi_prim_commit(void* addr, size_t size, bool* is_zero) { return 0; } -int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit) { +int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit) { BOOL ok = VirtualFree(addr, size, MEM_DECOMMIT); *needs_recommit = true; // for safety, assume always decommitted even in the case of an error. return (ok ? 0 : (int)GetLastError()); @@ -333,6 +399,11 @@ int _mi_prim_reset(void* addr, size_t size) { return (p != NULL ? 0 : (int)GetLastError()); } +int _mi_prim_reuse(void* addr, size_t size) { + MI_UNUSED(addr); MI_UNUSED(size); + return 0; +} + int _mi_prim_protect(void* addr, size_t size, bool protect) { DWORD oldprotect = 0; BOOL ok = VirtualProtect(addr, size, protect ? PAGE_NOACCESS : PAGE_READWRITE, &oldprotect); @@ -352,8 +423,8 @@ static void* _mi_prim_alloc_huge_os_pagesx(void* hint_addr, size_t size, int num MI_MEM_EXTENDED_PARAMETER params[3] = { {{0,0},{0}},{{0,0},{0}},{{0,0},{0}} }; // on modern Windows try use NtAllocateVirtualMemoryEx for 1GiB huge pages - static bool mi_huge_pages_available = true; - if (pNtAllocateVirtualMemoryEx != NULL && mi_huge_pages_available) { + static _Atomic(size_t) mi_huge_pages_available = MI_ATOMIC_VAR_INIT(1); + if (pNtAllocateVirtualMemoryEx != NULL && mi_atomic_load_acquire(&mi_huge_pages_available) != 0) { params[0].Type.Type = MiMemExtendedParameterAttributeFlags; params[0].Arg.ULong64 = MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE; ULONG param_count = 1; @@ -364,13 +435,13 @@ static void* _mi_prim_alloc_huge_os_pagesx(void* hint_addr, size_t size, int num } SIZE_T psize = size; void* base = hint_addr; - NTSTATUS err = (*pNtAllocateVirtualMemoryEx)(GetCurrentProcess(), &base, &psize, flags, PAGE_READWRITE, params, param_count); + LONG err = (*pNtAllocateVirtualMemoryEx)(GetCurrentProcess(), &base, &psize, flags, PAGE_READWRITE, params, param_count); if (err == 0 && base != NULL) { return base; } else { // fall back to regular large pages - mi_huge_pages_available = false; // don't try further huge pages + mi_atomic_store_release(&mi_huge_pages_available,0); // don't try further huge pages _mi_warning_message("unable to allocate using huge (1GiB) pages, trying large (2MiB) pages instead (status 0x%lx)\n", err); } } @@ -418,9 +489,11 @@ size_t _mi_prim_numa_node(void) { size_t _mi_prim_numa_node_count(void) { ULONG numa_max = 0; - GetNumaHighestNodeNumber(&numa_max); + if (pGetNumaHighestNodeNumber!=NULL) { + (*pGetNumaHighestNodeNumber)(&numa_max); + } // find the highest node number that has actual processors assigned to it. Issue #282 - while(numa_max > 0) { + while (numa_max > 0) { if (pGetNumaNodeProcessorMaskEx != NULL) { // Extended API is supported GROUP_AFFINITY affinity; @@ -431,8 +504,10 @@ size_t _mi_prim_numa_node_count(void) { else { // Vista or earlier, use older API that is limited to 64 processors. ULONGLONG mask; - if (GetNumaNodeProcessorMask((UCHAR)numa_max, &mask)) { - if (mask != 0) break; // found the maximum non-empty node + if (pGetNumaNodeProcessorMask != NULL) { + if ((*pGetNumaNodeProcessorMask)((UCHAR)numa_max, &mask)) { + if (mask != 0) break; // found the maximum non-empty node + } }; } // max node was invalid or had no processor assigned, try again @@ -468,7 +543,6 @@ mi_msecs_t _mi_prim_clock_now(void) { // Process Info //---------------------------------------------------------------- -#include #include static mi_msecs_t filetime_msecs(const FILETIME* ftime) { @@ -491,21 +565,21 @@ void _mi_prim_process_info(mi_process_info_t* pinfo) GetProcessTimes(GetCurrentProcess(), &ct, &et, &st, &ut); pinfo->utime = filetime_msecs(&ut); pinfo->stime = filetime_msecs(&st); - + // load psapi on demand - if (pGetProcessMemoryInfo == NULL) { + mi_atomic_do_once { HINSTANCE hDll = LoadLibrary(TEXT("psapi.dll")); if (hDll != NULL) { pGetProcessMemoryInfo = (PGetProcessMemoryInfo)(void (*)(void))GetProcAddress(hDll, "GetProcessMemoryInfo"); + // FreeLibrary(hDll); // don't free } } // get process info - PROCESS_MEMORY_COUNTERS info; - memset(&info, 0, sizeof(info)); + PROCESS_MEMORY_COUNTERS info; _mi_memzero_var(info); if (pGetProcessMemoryInfo != NULL) { pGetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); - } + } pinfo->current_rss = (size_t)info.WorkingSetSize; pinfo->peak_rss = (size_t)info.PeakWorkingSetSize; pinfo->current_commit = (size_t)info.PagefileUsage; @@ -517,24 +591,28 @@ void _mi_prim_process_info(mi_process_info_t* pinfo) // Output //---------------------------------------------------------------- -void _mi_prim_out_stderr( const char* msg ) +void _mi_prim_out_stderr( const char* msg ) { // on windows with redirection, the C runtime cannot handle locale dependent output // after the main thread closes so we use direct console output. if (!_mi_preloading()) { // _cputs(msg); // _cputs cannot be used as it aborts when failing to lock the console static HANDLE hcon = INVALID_HANDLE_VALUE; - static bool hconIsConsole; + static bool hconIsConsole = false; if (hcon == INVALID_HANDLE_VALUE) { - CONSOLE_SCREEN_BUFFER_INFO sbi; hcon = GetStdHandle(STD_ERROR_HANDLE); + #ifdef MI_HAS_CONSOLE_IO + CONSOLE_SCREEN_BUFFER_INFO sbi; hconIsConsole = ((hcon != INVALID_HANDLE_VALUE) && GetConsoleScreenBufferInfo(hcon, &sbi)); + #endif } const size_t len = _mi_strlen(msg); if (len > 0 && len < UINT32_MAX) { DWORD written = 0; if (hconIsConsole) { + #ifdef MI_HAS_CONSOLE_IO WriteConsoleA(hcon, msg, (DWORD)len, &written, NULL); + #endif } else if (hcon != INVALID_HANDLE_VALUE) { // use direct write if stderr was redirected @@ -557,14 +635,13 @@ void _mi_prim_out_stderr( const char* msg ) // reliably even when this is invoked before the C runtime is initialized. // i.e. when `_mi_preloading() == true`. // Note: on windows, environment names are not case sensitive. -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { result[0] = 0; - size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size); - return (len > 0 && len < result_size); + const size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size); + return (len < result_size ? (len > 0 ? 1 /* success */ : 0 /* not found */) : -1 /* error */); } - //---------------------------------------------------------------- // Random //---------------------------------------------------------------- @@ -593,71 +670,455 @@ typedef LONG (NTAPI *PBCryptGenRandom)(HANDLE, PUCHAR, ULONG, ULONG); static PBCryptGenRandom pBCryptGenRandom = NULL; bool _mi_prim_random_buf(void* buf, size_t buf_len) { - if (pBCryptGenRandom == NULL) { + mi_assert(buf_len <= ULONG_MAX); + if (buf_len > ULONG_MAX) return false; + mi_atomic_do_once { HINSTANCE hDll = LoadLibrary(TEXT("bcrypt.dll")); if (hDll != NULL) { pBCryptGenRandom = (PBCryptGenRandom)(void (*)(void))GetProcAddress(hDll, "BCryptGenRandom"); + // FreeLibrary(hDll); // don't free } - if (pBCryptGenRandom == NULL) return false; } - return (pBCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0); + if (pBCryptGenRandom == NULL) return false; + return (pBCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0); } #endif // MI_USE_RTLGENRANDOM + //---------------------------------------------------------------- -// Thread init/done +// Thread pool? //---------------------------------------------------------------- -#if !defined(MI_SHARED_LIB) - -// use thread local storage keys to detect thread ending -// note: another design could be to use special linker sections (see issue #869) -#include -#if (_WIN32_WINNT < 0x600) // before Windows Vista -WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback ); -WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex ); -WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData ); -WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex); +bool _mi_prim_thread_is_in_threadpool(void) { +#if (MI_ARCH_X64 || MI_ARCH_X86 || MI_ARCH_ARM64) + if (win_major_version >= 6) { + // check if this thread belongs to a windows threadpool + // see: + struct _TEB* const teb = NtCurrentTeb(); + void* const pool_data = *((void**)((uint8_t*)teb + (MI_SIZE_BITS == 32 ? 0x0F90 : 0x1778))); + return (pool_data != NULL); + } #endif + return false; +} + +void _mi_prim_thread_yield(void) { + SwitchToThread(); +} -static DWORD mi_fls_key = (DWORD)(-1); +//---------------------------------------------------------------- +// Process & Thread Init/Done +//---------------------------------------------------------------- + +//static void mi_debug_out(const char* s) { +// HANDLE h = GetStdHandle(STD_ERROR_HANDLE); +// WriteConsole(h, s, (DWORD)_mi_strlen(s), NULL, NULL); +//} -static void NTAPI mi_fls_done(PVOID value) { - mi_heap_t* heap = (mi_heap_t*)value; - if (heap != NULL) { - _mi_thread_done(heap); - FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main heap, issue #672 +static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { + MI_UNUSED(reserved); + MI_UNUSED(module); + if (reason==DLL_PROCESS_ATTACH) { + _mi_auto_process_init(); + } + else if (reason==DLL_PROCESS_DETACH) { + _mi_auto_process_done(); + } + else if (reason==DLL_THREAD_DETACH && !_mi_is_redirected()) { + _mi_thread_done(NULL); } } -void _mi_prim_thread_init_auto_done(void) { - mi_fls_key = FlsAlloc(&mi_fls_done); -} -void _mi_prim_thread_done_auto_done(void) { - // call thread-done on all threads (except the main thread) to prevent - // dangling callback pointer if statically linked with a DLL; Issue #208 - FlsFree(mi_fls_key); -} +/* ----------------------------------------------------------------------- + Auto initialize and finalize mimalloc on process and thread start/end. + By default we use a combination of CRT init and TLS sections for + both static and dynamic linkage (`MI_WIN_INIT_USE_CRT_TLS`). +------------------------------------------------------------------------- */ +#if !defined(MI_WIN_INIT_USE_CRT_TLS) && !defined(MI_WIN_INIT_USE_RAW_DLLMAIN) && !defined(MI_WIN_INIT_USE_TLS_DLLMAIN) && !defined(MI_WIN_INIT_USE_FLS) + #if !defined(__INTEL_LLVM_COMPILER) && !defined(__INTEL_COMPILER) + #define MI_WIN_INIT_USE_CRT_TLS 1 + #else + #define MI_WIN_INIT_USE_TLS_DLLMAIN 1 /* default for Intel ICX, see issue #1268 */ + #endif +#endif -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { - mi_assert_internal(mi_fls_key != (DWORD)(-1)); - FlsSetValue(mi_fls_key, heap); -} +#if defined(MI_WIN_INIT_USE_CRT_TLS) + #define MI_PRIM_HAS_PROCESS_ATTACH 1 + // nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event. + void _mi_prim_thread_init_auto_done(void) {} + void _mi_prim_thread_done_auto_done(void) {} + void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); + } -#else + static bool mi_module_is_dll(PVOID mod) { + if (mod==NULL) return false; + PIMAGE_DOS_HEADER imageDosHeader = (PIMAGE_DOS_HEADER)mod; + PIMAGE_NT_HEADERS imageNtHeaders = (PIMAGE_NT_HEADERS)((unsigned char*)imageDosHeader + imageDosHeader->e_lfanew); + return ((imageNtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL); + } -// Dll; nothing to do as in that case thread_done is handled through the DLL_THREAD_DETACH event. + static bool mi_current_module_is_dll(void) { + HMODULE mod = NULL; + const BOOL ok = GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)mi_current_module_is_dll, &mod); + return (ok && mi_module_is_dll(mod)); + } -void _mi_prim_thread_init_auto_done(void) { -} + // Hook into CRT initialization and finalization. + static void mi_cdecl mi_crt_done(void) { + // mi_debug_out(mi_current_module_is_dll() ? "crt dll done\n" : "crt exe done\n"); + mi_win_main(NULL, DLL_PROCESS_DETACH, 0); + } -void _mi_prim_thread_done_auto_done(void) { -} + static int mi_cdecl mi_crt_init(void) { + // mi_debug_out(mi_current_module_is_dll() ? "crt dll init\n" : "crt exe init\n"); + if (mi_current_module_is_dll()) { + // in a dll, atexit (crt_done) is called after tls process detach + atexit(&mi_crt_done); + } + return 0; + } -void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { - MI_UNUSED(heap); -} + // We also hook into the Windows loader TLS initialization and finalization. + static void NTAPI mi_tls_attach(PVOID module, DWORD reason, LPVOID reserved) { + if (reason == DLL_THREAD_ATTACH) { + //mi_debug_out("tls thread attach\n"); + mi_win_main(module, reason, reserved); + } + else if (reason == DLL_PROCESS_ATTACH) { + // mi_debug_out(mi_module_is_dll(module) ? "tls dll process attach\n" : "tls exe process attach\n"); + // tls process attach is always called before crt init + mi_win_main(module, reason, reserved); + } + } + + static void NTAPI mi_tls_detach(PVOID module, DWORD reason, LPVOID reserved) { + if (reason == DLL_THREAD_DETACH) { + //mi_debug_out("tls thread detach\n"); + mi_win_main(module, reason, reserved); + } + else if (reason == DLL_PROCESS_DETACH) { + // mi_debug_out(mi_module_is_dll(module) ? "tls dll process detach\n" : "tls exe process detach\n"); + if (!mi_module_is_dll(module)) { + // in an exe, tls process detach is called after atexit (mi_crt_done) + mi_win_main(module, reason, reserved); + } + } + } + + // Set up TLS callbacks in a statically linked library by using special data sections. + // See + // We use 2 entries to ensure we call attach events before constructors + // are called, and detach events after destructors are called. + // We also set up a CRT initialization callback. + #if defined(__cplusplus) + extern "C" { + #endif + + typedef int (mi_cdecl* mi_crt_callback_t)(void); + #if defined(_WIN64) + #pragma comment(linker, "/INCLUDE:_tls_used") + #pragma comment(linker, "/INCLUDE:_mi_tls_callback_pre") + #pragma comment(linker, "/INCLUDE:_mi_tls_callback_post") + #pragma const_seg(".CRT$XLB") + extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[]; + const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_tls_attach }; + #pragma const_seg() + #pragma const_seg(".CRT$XLY") + extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[]; + const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_tls_detach }; + #pragma const_seg() + #pragma const_seg(".CRT$XIB") + extern const mi_crt_callback_t _mi_crt_callback_init[]; + const mi_crt_callback_t _mi_crt_callback_init[] = { &mi_crt_init }; + #pragma const_seg() + #else + #pragma comment(linker, "/INCLUDE:__tls_used") + #pragma comment(linker, "/INCLUDE:__mi_tls_callback_pre") + #pragma comment(linker, "/INCLUDE:__mi_tls_callback_post") + #pragma data_seg(".CRT$XLB") + PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_tls_attach }; + #pragma data_seg() + #pragma data_seg(".CRT$XLY") + PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_tls_detach }; + #pragma data_seg() + #pragma data_seg(".CRT$XIB") + mi_crt_callback_t _mi_crt_callback_init[] = { &mi_crt_init }; + #pragma data_seg() + #endif + + #if defined(__cplusplus) + } + #endif + +/* ---------------------------------------------------------------- + Here we use a combination of the raw DLL main and TLS sections for + both static and dynamic linkage. This is perhaps the most robust as + the raw DLL main ensure we can close mimalloc latest possible. + However, it can lead to link errors with on `_pRawDllMain` if other + libraries also try to hook into this (like Boost). +*/ +#elif defined(MI_WIN_INIT_USE_RAW_DLLMAIN) + #define MI_PRIM_HAS_PROCESS_ATTACH 1 + // nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event. + void _mi_prim_thread_init_auto_done(void) {} + void _mi_prim_thread_done_auto_done(void) {} + void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); + } + // If linked into a DLL module, this raw entry is called before the CRT attach and + // after the CRT detach through the CRT _pRawDllMain pointer. + static BOOL NTAPI mi_dll_main_raw(PVOID module, DWORD reason, LPVOID reserved) { + mi_win_main(module, reason, reserved); + return TRUE; + } + + // Set the value of the CRT _pRawDllMain pointer + #if defined(__cplusplus) + extern "C" + #endif + PVOID _pRawDllMain = (PVOID)&mi_dll_main_raw; + + // We also hook into the Windows loader TLS initialization and finalization. + // If we are linked into an EXE module we rely on these as `mi_dll_main_raw` + // is not called (and otherwise we ignore the TLS callbacks by checking if we are in a DLL). + static bool mi_module_is_dll(PVOID mod) { + if (mod==NULL) return false; + PIMAGE_DOS_HEADER imageDosHeader = (PIMAGE_DOS_HEADER)mod; + PIMAGE_NT_HEADERS imageNtHeaders = (PIMAGE_NT_HEADERS)((unsigned char*)imageDosHeader + imageDosHeader->e_lfanew); + return ((imageNtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL); + } + + static void NTAPI mi_tls_attach(PVOID module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_ATTACH || reason == DLL_THREAD_ATTACH) { + if (!mi_module_is_dll(module)) { + mi_win_main(module, reason, reserved); + } + } + } + + static void NTAPI mi_tls_detach(PVOID module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_DETACH || reason == DLL_THREAD_DETACH) { + if (!mi_module_is_dll(module)) { + mi_win_main(module, reason, reserved); + } + } + } + + // Set up TLS callbacks in a statically linked library by using special data sections. + // See + // We use 2 entries to ensure we call attach events before constructors + // are called, and detach events after destructors are called. + #if defined(__cplusplus) + extern "C" { + #endif + + #if defined(_WIN64) + #pragma comment(linker, "/INCLUDE:_tls_used") + #pragma comment(linker, "/INCLUDE:_mi_tls_callback_pre") + #pragma comment(linker, "/INCLUDE:_mi_tls_callback_post") + #pragma const_seg(".CRT$XLB") + extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[]; + const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_tls_attach }; + #pragma const_seg() + #pragma const_seg(".CRT$XLY") + extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[]; + const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_tls_detach }; + #pragma const_seg() + #else + #pragma comment(linker, "/INCLUDE:__tls_used") + #pragma comment(linker, "/INCLUDE:__mi_tls_callback_pre") + #pragma comment(linker, "/INCLUDE:__mi_tls_callback_post") + #pragma data_seg(".CRT$XLB") + PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_tls_attach }; + #pragma data_seg() + #pragma data_seg(".CRT$XLY") + PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_tls_detach }; + #pragma data_seg() + #endif + + #if defined(__cplusplus) + } + #endif + +/* -------------------------------------------------------------------- + Legacy options: DllMain, TLS, and FLS + --------------------------------------------------------------------*/ +#elif defined(MI_WIN_INIT_USE_TLS_DLLMAIN) && defined(MI_SHARED_LIB) + #define MI_PRIM_HAS_PROCESS_ATTACH 1 + + // Windows DLL: easy to hook into process_init and thread_done + BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { + mi_win_main((PVOID)inst,reason,reserved); + return TRUE; + } + + // nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event. + void _mi_prim_thread_init_auto_done(void) { } + void _mi_prim_thread_done_auto_done(void) { } + void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); + } + +#elif defined(MI_WIN_INIT_USE_TLS_DLLMAIN) + #define MI_PRIM_HAS_PROCESS_ATTACH 1 + + static void NTAPI mi_win_main_attach(PVOID module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_ATTACH || reason == DLL_THREAD_ATTACH) { + mi_win_main(module, reason, reserved); + } + } + static void NTAPI mi_win_main_detach(PVOID module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_DETACH || reason == DLL_THREAD_DETACH) { + mi_win_main(module, reason, reserved); + } + } + + // Set up TLS callbacks in a statically linked library by using special data sections. + // See + // We use 2 entries to ensure we call attach events before constructors + // are called, and detach events after destructors are called. + #if defined(__cplusplus) + extern "C" { + #endif + + #if defined(_WIN64) + #pragma comment(linker, "/INCLUDE:_tls_used") + #pragma comment(linker, "/INCLUDE:_mi_tls_callback_pre") + #pragma comment(linker, "/INCLUDE:_mi_tls_callback_post") + #pragma const_seg(".CRT$XLB") + extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[]; + const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_win_main_attach }; + #pragma const_seg() + #pragma const_seg(".CRT$XLY") + extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[]; + const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_win_main_detach }; + #pragma const_seg() + #else + #pragma comment(linker, "/INCLUDE:__tls_used") + #pragma comment(linker, "/INCLUDE:__mi_tls_callback_pre") + #pragma comment(linker, "/INCLUDE:__mi_tls_callback_post") + #pragma data_seg(".CRT$XLB") + PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_win_main_attach }; + #pragma data_seg() + #pragma data_seg(".CRT$XIY") + PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_win_main_detach }; + #pragma data_seg() + #endif + + #if defined(__cplusplus) + } + #endif + + // nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event. + void _mi_prim_thread_init_auto_done(void) { } + void _mi_prim_thread_done_auto_done(void) { } + void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + MI_UNUSED(theap); + } + +#elif defined(MI_WIN_INIT_USE_FLS) // deprecated: statically linked, use fiber api + + #if defined(_MSC_VER) // on clang/gcc use the constructor attribute (in `src/prim/prim.c`) + // MSVC: use data section magic for static libraries + // See + #define MI_PRIM_HAS_PROCESS_ATTACH 1 + + static int mi_process_attach(void) { + mi_win_main(NULL,DLL_PROCESS_ATTACH,NULL); + atexit(&_mi_auto_process_done); + return 0; + } + typedef int(*mi_crt_callback_t)(void); + #if defined(_WIN64) + #pragma comment(linker, "/INCLUDE:_mi_tls_callback") + #pragma section(".CRT$XIU", long, read) + #else + #pragma comment(linker, "/INCLUDE:__mi_tls_callback") + #endif + #pragma data_seg(".CRT$XIU") + mi_decl_externc mi_crt_callback_t _mi_tls_callback[] = { &mi_process_attach }; + #pragma data_seg() + #endif + + // use the fiber api for calling `_mi_thread_done`. + #include + #if (_WIN32_WINNT < 0x600) // before Windows Vista + WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback ); + WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex ); + WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData ); + WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex); + #endif + + static DWORD mi_fls_key = (DWORD)(-1); + + static void NTAPI mi_fls_done(PVOID value) { + mi_theap_t* theap = (mi_theap_t*)value; + if (theap != NULL) { + _mi_thread_done(theap); + FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main theap, issue #672 + } + } + + void _mi_prim_thread_init_auto_done(void) { + mi_fls_key = FlsAlloc(&mi_fls_done); + } + + void _mi_prim_thread_done_auto_done(void) { + // call thread-done on all threads (except the main thread) to prevent + // dangling callback pointer if statically linked with a DLL; Issue #208 + FlsFree(mi_fls_key); + } + + void _mi_prim_thread_associate_default_theap(mi_theap_t* theap) { + mi_assert_internal(mi_fls_key != (DWORD)(-1)); + FlsSetValue(mi_fls_key, theap); + } +#else +#error "define windows process and thread auto initialization" +#endif + +// ---------------------------------------------------- +// Communicate with the redirection module on Windows +// ---------------------------------------------------- +#if defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT) + #define MI_PRIM_HAS_ALLOCATOR_INIT 1 + + static bool mi_redirected = false; // true if malloc redirects to mi_malloc + + bool _mi_is_redirected(void) { + return mi_redirected; + } + + #ifdef __cplusplus + extern "C" { + #endif + mi_decl_export void _mi_redirect_entry(DWORD reason) { + // called on redirection; careful as this may be called before DllMain + if (reason == DLL_PROCESS_ATTACH) { + mi_redirected = true; + } + else if (reason == DLL_PROCESS_DETACH) { + mi_redirected = false; + } + else if (reason == DLL_THREAD_DETACH) { + // mi_debug_out("redirect thread detach\n"); + _mi_thread_done(NULL); + } + } + __declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message); + __declspec(dllimport) void mi_cdecl mi_allocator_done(void); + #ifdef __cplusplus + } + #endif + bool _mi_allocator_init(const char** message) { + return mi_allocator_init(message); + } + void _mi_allocator_done(void) { + mi_allocator_done(); + } #endif + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/random.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/random.c index 4fc8b2f8..75b5cfbb 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/random.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/random.c @@ -7,7 +7,6 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc.h" #include "mimalloc/internal.h" #include "mimalloc/prim.h" // _mi_prim_random_buf -#include // memset /* ---------------------------------------------------------------------------- We use our own PRNG to keep predictable performance of random number generation @@ -33,15 +32,11 @@ The implementation uses regular C code which compiles very well on modern compil (gcc x64 has no register spills, and clang 6+ uses SSE instructions) -----------------------------------------------------------------------------*/ -static inline uint32_t rotl(uint32_t x, uint32_t shift) { - return (x << shift) | (x >> (32 - shift)); -} - static inline void qround(uint32_t x[16], size_t a, size_t b, size_t c, size_t d) { - x[a] += x[b]; x[d] = rotl(x[d] ^ x[a], 16); - x[c] += x[d]; x[b] = rotl(x[b] ^ x[c], 12); - x[a] += x[b]; x[d] = rotl(x[d] ^ x[a], 8); - x[c] += x[d]; x[b] = rotl(x[b] ^ x[c], 7); + x[a] += x[b]; x[d] = mi_rotl32(x[d] ^ x[a], 16); + x[c] += x[d]; x[b] = mi_rotl32(x[b] ^ x[c], 12); + x[a] += x[b]; x[d] = mi_rotl32(x[d] ^ x[a], 8); + x[c] += x[d]; x[b] = mi_rotl32(x[b] ^ x[c], 7); } static void chacha_block(mi_random_ctx_t* ctx) @@ -99,7 +94,8 @@ static void chacha_init(mi_random_ctx_t* ctx, const uint8_t key[32], uint64_t no // since we only use chacha for randomness (and not encryption) we // do not _need_ to read 32-bit values as little endian but we do anyways // just for being compatible :-) - memset(ctx, 0, sizeof(*ctx)); + ctx->output_available = 0; + _mi_memzero(ctx->output,sizeof(ctx->output)); for (size_t i = 0; i < 4; i++) { const uint8_t* sigma = (uint8_t*)"expand 32-byte k"; ctx->input[i] = read32(sigma,i); @@ -114,7 +110,8 @@ static void chacha_init(mi_random_ctx_t* ctx, const uint8_t key[32], uint64_t no } static void chacha_split(mi_random_ctx_t* ctx, uint64_t nonce, mi_random_ctx_t* ctx_new) { - memset(ctx_new, 0, sizeof(*ctx_new)); + _mi_memzero(ctx_new, sizeof(*ctx_new)); + ctx_new->weak = ctx->weak; _mi_memcpy(ctx_new->input, ctx->input, sizeof(ctx_new->input)); ctx_new->input[12] = 0; ctx_new->input[13] = 0; @@ -143,13 +140,17 @@ void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* ctx_new) { uintptr_t _mi_random_next(mi_random_ctx_t* ctx) { mi_assert_internal(mi_random_is_initialized(ctx)); - #if MI_INTPTR_SIZE <= 4 - return chacha_next32(ctx); - #elif MI_INTPTR_SIZE == 8 - return (((uintptr_t)chacha_next32(ctx) << 32) | chacha_next32(ctx)); - #else - # error "define mi_random_next for this platform" - #endif + uintptr_t r; + do { + #if MI_INTPTR_SIZE <= 4 + r = chacha_next32(ctx); + #elif MI_INTPTR_SIZE == 8 + r = (((uintptr_t)chacha_next32(ctx) << 32) | chacha_next32(ctx)); + #else + # error "define mi_random_next for this platform" + #endif + } while (r==0); + return r; } @@ -160,10 +161,10 @@ If we cannot get good randomness, we fall back to weak randomness based on a tim uintptr_t _mi_os_random_weak(uintptr_t extra_seed) { uintptr_t x = (uintptr_t)&_mi_os_random_weak ^ extra_seed; // ASLR makes the address random - x ^= _mi_prim_clock_now(); + x ^= _mi_prim_clock_now(); // and do a few randomization steps uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1; - for (uintptr_t i = 0; i < max; i++) { + for (uintptr_t i = 0; i < max || x==0; i++, x++) { x = _mi_random_shuffle(x); } mi_assert_internal(x != 0); @@ -179,9 +180,12 @@ static void mi_random_init_ex(mi_random_ctx_t* ctx, bool use_weak) { if (!use_weak) { _mi_warning_message("unable to use secure randomness\n"); } #endif uintptr_t x = _mi_os_random_weak(0); - for (size_t i = 0; i < 8; i++) { // key is eight 32-bit words. + for (size_t i = 0; i < 32; i+=4, x++) { x = _mi_random_shuffle(x); - ((uint32_t*)key)[i] = (uint32_t)x; + key[i] = (uint8_t)(x); + key[i+1] = (uint8_t)(x>>8); + key[i+2] = (uint8_t)(x>>16); + key[i+3] = (uint8_t)(x>>24); } ctx->weak = true; } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/segment-map.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/segment-map.c deleted file mode 100644 index 1efb1e23..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/segment-map.c +++ /dev/null @@ -1,155 +0,0 @@ -/* ---------------------------------------------------------------------------- -Copyright (c) 2019-2023, Microsoft Research, Daan Leijen -This is free software; you can redistribute it and/or modify it under the -terms of the MIT license. A copy of the license can be found in the file -"LICENSE" at the root of this distribution. ------------------------------------------------------------------------------*/ - -/* ----------------------------------------------------------- - The following functions are to reliably find the segment or - block that encompasses any pointer p (or NULL if it is not - in any of our segments). - We maintain a bitmap of all memory with 1 bit per MI_SEGMENT_SIZE (64MiB) - set to 1 if it contains the segment meta data. ------------------------------------------------------------ */ -#include "mimalloc.h" -#include "mimalloc/internal.h" -#include "mimalloc/atomic.h" - -#if (MI_INTPTR_SIZE>=8) && MI_TRACK_ASAN -#define MI_MAX_ADDRESS ((size_t)140 << 40) // 140TB (see issue #881) -#elif (MI_INTPTR_SIZE >= 8) -#define MI_MAX_ADDRESS ((size_t)40 << 40) // 40TB (to include huge page areas) -#else -#define MI_MAX_ADDRESS ((size_t)2 << 30) // 2Gb -#endif - -#define MI_SEGMENT_MAP_BITS (MI_MAX_ADDRESS / MI_SEGMENT_SIZE) -#define MI_SEGMENT_MAP_SIZE (MI_SEGMENT_MAP_BITS / 8) -#define MI_SEGMENT_MAP_WSIZE (MI_SEGMENT_MAP_SIZE / MI_INTPTR_SIZE) - -static _Atomic(uintptr_t) mi_segment_map[MI_SEGMENT_MAP_WSIZE + 1]; // 2KiB per TB with 64MiB segments - -static size_t mi_segment_map_index_of(const mi_segment_t* segment, size_t* bitidx) { - // note: segment can be invalid or NULL. - mi_assert_internal(_mi_ptr_segment(segment + 1) == segment); // is it aligned on MI_SEGMENT_SIZE? - if ((uintptr_t)segment >= MI_MAX_ADDRESS) { - *bitidx = 0; - return MI_SEGMENT_MAP_WSIZE; - } - else { - const uintptr_t segindex = ((uintptr_t)segment) / MI_SEGMENT_SIZE; - *bitidx = segindex % MI_INTPTR_BITS; - const size_t mapindex = segindex / MI_INTPTR_BITS; - mi_assert_internal(mapindex < MI_SEGMENT_MAP_WSIZE); - return mapindex; - } -} - -void _mi_segment_map_allocated_at(const mi_segment_t* segment) { - size_t bitidx; - size_t index = mi_segment_map_index_of(segment, &bitidx); - mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE); - if (index==MI_SEGMENT_MAP_WSIZE) return; - uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); - uintptr_t newmask; - do { - newmask = (mask | ((uintptr_t)1 << bitidx)); - } while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask)); -} - -void _mi_segment_map_freed_at(const mi_segment_t* segment) { - size_t bitidx; - size_t index = mi_segment_map_index_of(segment, &bitidx); - mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE); - if (index == MI_SEGMENT_MAP_WSIZE) return; - uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); - uintptr_t newmask; - do { - newmask = (mask & ~((uintptr_t)1 << bitidx)); - } while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask)); -} - -// Determine the segment belonging to a pointer or NULL if it is not in a valid segment. -static mi_segment_t* _mi_segment_of(const void* p) { - if (p == NULL) return NULL; - mi_segment_t* segment = _mi_ptr_segment(p); // segment can be NULL - size_t bitidx; - size_t index = mi_segment_map_index_of(segment, &bitidx); - // fast path: for any pointer to valid small/medium/large object or first MI_SEGMENT_SIZE in huge - const uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); - if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) { - return segment; // yes, allocated by us - } - if (index==MI_SEGMENT_MAP_WSIZE) return NULL; - - // TODO: maintain max/min allocated range for efficiency for more efficient rejection of invalid pointers? - - // search downwards for the first segment in case it is an interior pointer - // could be slow but searches in MI_INTPTR_SIZE * MI_SEGMENT_SIZE (512MiB) steps trough - // valid huge objects - // note: we could maintain a lowest index to speed up the path for invalid pointers? - size_t lobitidx; - size_t loindex; - uintptr_t lobits = mask & (((uintptr_t)1 << bitidx) - 1); - if (lobits != 0) { - loindex = index; - lobitidx = mi_bsr(lobits); // lobits != 0 - } - else if (index == 0) { - return NULL; - } - else { - mi_assert_internal(index > 0); - uintptr_t lomask = mask; - loindex = index; - do { - loindex--; - lomask = mi_atomic_load_relaxed(&mi_segment_map[loindex]); - } while (lomask != 0 && loindex > 0); - if (lomask == 0) return NULL; - lobitidx = mi_bsr(lomask); // lomask != 0 - } - mi_assert_internal(loindex < MI_SEGMENT_MAP_WSIZE); - // take difference as the addresses could be larger than the MAX_ADDRESS space. - size_t diff = (((index - loindex) * (8*MI_INTPTR_SIZE)) + bitidx - lobitidx) * MI_SEGMENT_SIZE; - segment = (mi_segment_t*)((uint8_t*)segment - diff); - - if (segment == NULL) return NULL; - mi_assert_internal((void*)segment < p); - bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie); - mi_assert_internal(cookie_ok); - if mi_unlikely(!cookie_ok) return NULL; - if (((uint8_t*)segment + mi_segment_size(segment)) <= (uint8_t*)p) return NULL; // outside the range - mi_assert_internal(p >= (void*)segment && (uint8_t*)p < (uint8_t*)segment + mi_segment_size(segment)); - return segment; -} - -// Is this a valid pointer in our heap? -static bool mi_is_valid_pointer(const void* p) { - return ((_mi_segment_of(p) != NULL) || (_mi_arena_contains(p))); -} - -mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { - return mi_is_valid_pointer(p); -} - -/* -// Return the full segment range belonging to a pointer -static void* mi_segment_range_of(const void* p, size_t* size) { - mi_segment_t* segment = _mi_segment_of(p); - if (segment == NULL) { - if (size != NULL) *size = 0; - return NULL; - } - else { - if (size != NULL) *size = segment->segment_size; - return segment; - } - mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld)); - mi_assert_internal(page == NULL || (mi_segment_page_size(_mi_page_segment(page)) - (MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= block_size); - mi_reset_delayed(tld); - mi_assert_internal(page == NULL || mi_page_not_in_queue(page, tld)); - return page; -} -*/ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/segment.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/segment.c deleted file mode 100644 index 4e4dcb80..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/segment.c +++ /dev/null @@ -1,1524 +0,0 @@ -/* ---------------------------------------------------------------------------- -Copyright (c) 2018-2024, Microsoft Research, Daan Leijen -This is free software; you can redistribute it and/or modify it under the -terms of the MIT license. A copy of the license can be found in the file -"LICENSE" at the root of this distribution. ------------------------------------------------------------------------------*/ -#include "mimalloc.h" -#include "mimalloc/internal.h" -#include "mimalloc/atomic.h" - -#include // memset -#include - -// ------------------------------------------------------------------- -// Segments -// mimalloc pages reside in segments. See `mi_segment_valid` for invariants. -// ------------------------------------------------------------------- - - -static void mi_segment_try_purge(mi_segment_t* segment, bool force, mi_stats_t* stats); - - -// ------------------------------------------------------------------- -// commit mask -// ------------------------------------------------------------------- - -static bool mi_commit_mask_all_set(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - if ((commit->mask[i] & cm->mask[i]) != cm->mask[i]) return false; - } - return true; -} - -static bool mi_commit_mask_any_set(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - if ((commit->mask[i] & cm->mask[i]) != 0) return true; - } - return false; -} - -static void mi_commit_mask_create_intersect(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm, mi_commit_mask_t* res) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - res->mask[i] = (commit->mask[i] & cm->mask[i]); - } -} - -static void mi_commit_mask_clear(mi_commit_mask_t* res, const mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - res->mask[i] &= ~(cm->mask[i]); - } -} - -static void mi_commit_mask_set(mi_commit_mask_t* res, const mi_commit_mask_t* cm) { - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - res->mask[i] |= cm->mask[i]; - } -} - -static void mi_commit_mask_create(size_t bitidx, size_t bitcount, mi_commit_mask_t* cm) { - mi_assert_internal(bitidx < MI_COMMIT_MASK_BITS); - mi_assert_internal((bitidx + bitcount) <= MI_COMMIT_MASK_BITS); - if (bitcount == MI_COMMIT_MASK_BITS) { - mi_assert_internal(bitidx==0); - mi_commit_mask_create_full(cm); - } - else if (bitcount == 0) { - mi_commit_mask_create_empty(cm); - } - else { - mi_commit_mask_create_empty(cm); - size_t i = bitidx / MI_COMMIT_MASK_FIELD_BITS; - size_t ofs = bitidx % MI_COMMIT_MASK_FIELD_BITS; - while (bitcount > 0) { - mi_assert_internal(i < MI_COMMIT_MASK_FIELD_COUNT); - size_t avail = MI_COMMIT_MASK_FIELD_BITS - ofs; - size_t count = (bitcount > avail ? avail : bitcount); - size_t mask = (count >= MI_COMMIT_MASK_FIELD_BITS ? ~((size_t)0) : (((size_t)1 << count) - 1) << ofs); - cm->mask[i] = mask; - bitcount -= count; - ofs = 0; - i++; - } - } -} - -size_t _mi_commit_mask_committed_size(const mi_commit_mask_t* cm, size_t total) { - mi_assert_internal((total%MI_COMMIT_MASK_BITS)==0); - size_t count = 0; - for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { - size_t mask = cm->mask[i]; - if (~mask == 0) { - count += MI_COMMIT_MASK_FIELD_BITS; - } - else { - for (; mask != 0; mask >>= 1) { // todo: use popcount - if ((mask&1)!=0) count++; - } - } - } - // we use total since for huge segments each commit bit may represent a larger size - return ((total / MI_COMMIT_MASK_BITS) * count); -} - - -size_t _mi_commit_mask_next_run(const mi_commit_mask_t* cm, size_t* idx) { - size_t i = (*idx) / MI_COMMIT_MASK_FIELD_BITS; - size_t ofs = (*idx) % MI_COMMIT_MASK_FIELD_BITS; - size_t mask = 0; - // find first ones - while (i < MI_COMMIT_MASK_FIELD_COUNT) { - mask = cm->mask[i]; - mask >>= ofs; - if (mask != 0) { - while ((mask&1) == 0) { - mask >>= 1; - ofs++; - } - break; - } - i++; - ofs = 0; - } - if (i >= MI_COMMIT_MASK_FIELD_COUNT) { - // not found - *idx = MI_COMMIT_MASK_BITS; - return 0; - } - else { - // found, count ones - size_t count = 0; - *idx = (i*MI_COMMIT_MASK_FIELD_BITS) + ofs; - do { - mi_assert_internal(ofs < MI_COMMIT_MASK_FIELD_BITS && (mask&1) == 1); - do { - count++; - mask >>= 1; - } while ((mask&1) == 1); - if ((((*idx + count) % MI_COMMIT_MASK_FIELD_BITS) == 0)) { - i++; - if (i >= MI_COMMIT_MASK_FIELD_COUNT) break; - mask = cm->mask[i]; - ofs = 0; - } - } while ((mask&1) == 1); - mi_assert_internal(count > 0); - return count; - } -} - - -/* -------------------------------------------------------------------------------- - Segment allocation --------------------------------------------------------------------------------- */ - - -/* ----------------------------------------------------------- - Slices ------------------------------------------------------------ */ - - -static const mi_slice_t* mi_segment_slices_end(const mi_segment_t* segment) { - return &segment->slices[segment->slice_entries]; -} - -static uint8_t* mi_slice_start(const mi_slice_t* slice) { - mi_segment_t* segment = _mi_ptr_segment(slice); - mi_assert_internal(slice >= segment->slices && slice < mi_segment_slices_end(segment)); - return ((uint8_t*)segment + ((slice - segment->slices)*MI_SEGMENT_SLICE_SIZE)); -} - - -/* ----------------------------------------------------------- - Bins ------------------------------------------------------------ */ -// Use bit scan forward to quickly find the first zero bit if it is available - -static inline size_t mi_slice_bin8(size_t slice_count) { - if (slice_count<=1) return slice_count; - mi_assert_internal(slice_count <= MI_SLICES_PER_SEGMENT); - slice_count--; - size_t s = mi_bsr(slice_count); // slice_count > 1 - if (s <= 2) return slice_count + 1; - size_t bin = ((s << 2) | ((slice_count >> (s - 2))&0x03)) - 4; - return bin; -} - -static inline size_t mi_slice_bin(size_t slice_count) { - mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_SEGMENT_SIZE); - mi_assert_internal(mi_slice_bin8(MI_SLICES_PER_SEGMENT) <= MI_SEGMENT_BIN_MAX); - size_t bin = mi_slice_bin8(slice_count); - mi_assert_internal(bin <= MI_SEGMENT_BIN_MAX); - return bin; -} - -static inline size_t mi_slice_index(const mi_slice_t* slice) { - mi_segment_t* segment = _mi_ptr_segment(slice); - ptrdiff_t index = slice - segment->slices; - mi_assert_internal(index >= 0 && index < (ptrdiff_t)segment->slice_entries); - return index; -} - - -/* ----------------------------------------------------------- - Slice span queues ------------------------------------------------------------ */ - -static void mi_span_queue_push(mi_span_queue_t* sq, mi_slice_t* slice) { - // todo: or push to the end? - mi_assert_internal(slice->prev == NULL && slice->next==NULL); - slice->prev = NULL; // paranoia - slice->next = sq->first; - sq->first = slice; - if (slice->next != NULL) slice->next->prev = slice; - else sq->last = slice; - slice->block_size = 0; // free -} - -static mi_span_queue_t* mi_span_queue_for(size_t slice_count, mi_segments_tld_t* tld) { - size_t bin = mi_slice_bin(slice_count); - mi_span_queue_t* sq = &tld->spans[bin]; - mi_assert_internal(sq->slice_count >= slice_count); - return sq; -} - -static void mi_span_queue_delete(mi_span_queue_t* sq, mi_slice_t* slice) { - mi_assert_internal(slice->block_size==0 && slice->slice_count>0 && slice->slice_offset==0); - // should work too if the queue does not contain slice (which can happen during reclaim) - if (slice->prev != NULL) slice->prev->next = slice->next; - if (slice == sq->first) sq->first = slice->next; - if (slice->next != NULL) slice->next->prev = slice->prev; - if (slice == sq->last) sq->last = slice->prev; - slice->prev = NULL; - slice->next = NULL; - slice->block_size = 1; // no more free -} - - -/* ----------------------------------------------------------- - Invariant checking ------------------------------------------------------------ */ - -static bool mi_slice_is_used(const mi_slice_t* slice) { - return (slice->block_size > 0); -} - - -#if (MI_DEBUG>=3) -static bool mi_span_queue_contains(mi_span_queue_t* sq, mi_slice_t* slice) { - for (mi_slice_t* s = sq->first; s != NULL; s = s->next) { - if (s==slice) return true; - } - return false; -} - -static bool mi_segment_is_valid(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_assert_internal(segment != NULL); - mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie); - mi_assert_internal(segment->abandoned <= segment->used); - mi_assert_internal(segment->thread_id == 0 || segment->thread_id == _mi_thread_id()); - mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask)); // can only decommit committed blocks - //mi_assert_internal(segment->segment_info_size % MI_SEGMENT_SLICE_SIZE == 0); - mi_slice_t* slice = &segment->slices[0]; - const mi_slice_t* end = mi_segment_slices_end(segment); - size_t used_count = 0; - mi_span_queue_t* sq; - while(slice < end) { - mi_assert_internal(slice->slice_count > 0); - mi_assert_internal(slice->slice_offset == 0); - size_t index = mi_slice_index(slice); - size_t maxindex = (index + slice->slice_count >= segment->slice_entries ? segment->slice_entries : index + slice->slice_count) - 1; - if (mi_slice_is_used(slice)) { // a page in use, we need at least MAX_SLICE_OFFSET_COUNT valid back offsets - used_count++; - mi_assert_internal(slice->is_huge == (segment->kind == MI_SEGMENT_HUGE)); - for (size_t i = 0; i <= MI_MAX_SLICE_OFFSET_COUNT && index + i <= maxindex; i++) { - mi_assert_internal(segment->slices[index + i].slice_offset == i*sizeof(mi_slice_t)); - mi_assert_internal(i==0 || segment->slices[index + i].slice_count == 0); - mi_assert_internal(i==0 || segment->slices[index + i].block_size == 1); - } - // and the last entry as well (for coalescing) - const mi_slice_t* last = slice + slice->slice_count - 1; - if (last > slice && last < mi_segment_slices_end(segment)) { - mi_assert_internal(last->slice_offset == (slice->slice_count-1)*sizeof(mi_slice_t)); - mi_assert_internal(last->slice_count == 0); - mi_assert_internal(last->block_size == 1); - } - } - else { // free range of slices; only last slice needs a valid back offset - mi_slice_t* last = &segment->slices[maxindex]; - if (segment->kind != MI_SEGMENT_HUGE || slice->slice_count <= (segment->slice_entries - segment->segment_info_slices)) { - mi_assert_internal((uint8_t*)slice == (uint8_t*)last - last->slice_offset); - } - mi_assert_internal(slice == last || last->slice_count == 0 ); - mi_assert_internal(last->block_size == 0 || (segment->kind==MI_SEGMENT_HUGE && last->block_size==1)); - if (segment->kind != MI_SEGMENT_HUGE && segment->thread_id != 0) { // segment is not huge or abandoned - sq = mi_span_queue_for(slice->slice_count,tld); - mi_assert_internal(mi_span_queue_contains(sq,slice)); - } - } - slice = &segment->slices[maxindex+1]; - } - mi_assert_internal(slice == end); - mi_assert_internal(used_count == segment->used + 1); - return true; -} -#endif - -/* ----------------------------------------------------------- - Segment size calculations ------------------------------------------------------------ */ - -static size_t mi_segment_info_size(mi_segment_t* segment) { - return segment->segment_info_slices * MI_SEGMENT_SLICE_SIZE; -} - -static uint8_t* _mi_segment_page_start_from_slice(const mi_segment_t* segment, const mi_slice_t* slice, size_t block_size, size_t* page_size) -{ - const ptrdiff_t idx = slice - segment->slices; - const size_t psize = (size_t)slice->slice_count * MI_SEGMENT_SLICE_SIZE; - uint8_t* const pstart = (uint8_t*)segment + (idx*MI_SEGMENT_SLICE_SIZE); - // make the start not OS page aligned for smaller blocks to avoid page/cache effects - // note: the offset must always be a block_size multiple since we assume small allocations - // are aligned (see `mi_heap_malloc_aligned`). - size_t start_offset = 0; - if (block_size > 0 && block_size <= MI_MAX_ALIGN_GUARANTEE) { - // for small objects, ensure the page start is aligned with the block size (PR#66 by kickunderscore) - const size_t adjust = block_size - ((uintptr_t)pstart % block_size); - if (adjust < block_size && psize >= block_size + adjust) { - start_offset += adjust; - } - } - if (block_size >= MI_INTPTR_SIZE) { - if (block_size <= 64) { start_offset += 3*block_size; } - else if (block_size <= 512) { start_offset += block_size; } - } - if (page_size != NULL) { *page_size = psize - start_offset; } - return (pstart + start_offset); -} - -// Start of the page available memory; can be used on uninitialized pages -uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) -{ - const mi_slice_t* slice = mi_page_to_slice((mi_page_t*)page); - uint8_t* p = _mi_segment_page_start_from_slice(segment, slice, mi_page_block_size(page), page_size); - mi_assert_internal(mi_page_block_size(page) > 0 || _mi_ptr_page(p) == page); - mi_assert_internal(_mi_ptr_segment(p) == segment); - return p; -} - - -static size_t mi_segment_calculate_slices(size_t required, size_t* info_slices) { - size_t page_size = _mi_os_page_size(); - size_t isize = _mi_align_up(sizeof(mi_segment_t), page_size); - size_t guardsize = 0; - - if (MI_SECURE>0) { - // in secure mode, we set up a protected page in between the segment info - // and the page data (and one at the end of the segment) - guardsize = page_size; - if (required > 0) { - required = _mi_align_up(required, MI_SEGMENT_SLICE_SIZE) + page_size; - } - } - - isize = _mi_align_up(isize + guardsize, MI_SEGMENT_SLICE_SIZE); - if (info_slices != NULL) *info_slices = isize / MI_SEGMENT_SLICE_SIZE; - size_t segment_size = (required==0 ? MI_SEGMENT_SIZE : _mi_align_up( required + isize + guardsize, MI_SEGMENT_SLICE_SIZE) ); - mi_assert_internal(segment_size % MI_SEGMENT_SLICE_SIZE == 0); - return (segment_size / MI_SEGMENT_SLICE_SIZE); -} - - -/* ---------------------------------------------------------------------------- -Segment caches -We keep a small segment cache per thread to increase local -reuse and avoid setting/clearing guard pages in secure mode. -------------------------------------------------------------------------------- */ - -static void mi_segments_track_size(long segment_size, mi_segments_tld_t* tld) { - if (segment_size>=0) _mi_stat_increase(&tld->stats->segments,1); - else _mi_stat_decrease(&tld->stats->segments,1); - tld->count += (segment_size >= 0 ? 1 : -1); - if (tld->count > tld->peak_count) tld->peak_count = tld->count; - tld->current_size += segment_size; - if (tld->current_size > tld->peak_size) tld->peak_size = tld->current_size; -} - -static void mi_segment_os_free(mi_segment_t* segment, mi_segments_tld_t* tld) { - segment->thread_id = 0; - _mi_segment_map_freed_at(segment); - mi_segments_track_size(-((long)mi_segment_size(segment)),tld); - if (segment->was_reclaimed) { - tld->reclaim_count--; - segment->was_reclaimed = false; - } - if (MI_SECURE>0) { - // _mi_os_unprotect(segment, mi_segment_size(segment)); // ensure no more guard pages are set - // unprotect the guard pages; we cannot just unprotect the whole segment size as part may be decommitted - size_t os_pagesize = _mi_os_page_size(); - _mi_os_unprotect((uint8_t*)segment + mi_segment_info_size(segment) - os_pagesize, os_pagesize); - uint8_t* end = (uint8_t*)segment + mi_segment_size(segment) - os_pagesize; - _mi_os_unprotect(end, os_pagesize); - } - - // purge delayed decommits now? (no, leave it to the arena) - // mi_segment_try_purge(segment,true,tld->stats); - - const size_t size = mi_segment_size(segment); - const size_t csize = _mi_commit_mask_committed_size(&segment->commit_mask, size); - - _mi_abandoned_await_readers(); // wait until safe to free - _mi_arena_free(segment, mi_segment_size(segment), csize, segment->memid, tld->stats); -} - -/* ----------------------------------------------------------- - Commit/Decommit ranges ------------------------------------------------------------ */ - -static void mi_segment_commit_mask(mi_segment_t* segment, bool conservative, uint8_t* p, size_t size, uint8_t** start_p, size_t* full_size, mi_commit_mask_t* cm) { - mi_assert_internal(_mi_ptr_segment(p + 1) == segment); - mi_assert_internal(segment->kind != MI_SEGMENT_HUGE); - mi_commit_mask_create_empty(cm); - if (size == 0 || size > MI_SEGMENT_SIZE || segment->kind == MI_SEGMENT_HUGE) return; - const size_t segstart = mi_segment_info_size(segment); - const size_t segsize = mi_segment_size(segment); - if (p >= (uint8_t*)segment + segsize) return; - - size_t pstart = (p - (uint8_t*)segment); - mi_assert_internal(pstart + size <= segsize); - - size_t start; - size_t end; - if (conservative) { - // decommit conservative - start = _mi_align_up(pstart, MI_COMMIT_SIZE); - end = _mi_align_down(pstart + size, MI_COMMIT_SIZE); - mi_assert_internal(start >= segstart); - mi_assert_internal(end <= segsize); - } - else { - // commit liberal - start = _mi_align_down(pstart, MI_MINIMAL_COMMIT_SIZE); - end = _mi_align_up(pstart + size, MI_MINIMAL_COMMIT_SIZE); - } - if (pstart >= segstart && start < segstart) { // note: the mask is also calculated for an initial commit of the info area - start = segstart; - } - if (end > segsize) { - end = segsize; - } - - mi_assert_internal(start <= pstart && (pstart + size) <= end); - mi_assert_internal(start % MI_COMMIT_SIZE==0 && end % MI_COMMIT_SIZE == 0); - *start_p = (uint8_t*)segment + start; - *full_size = (end > start ? end - start : 0); - if (*full_size == 0) return; - - size_t bitidx = start / MI_COMMIT_SIZE; - mi_assert_internal(bitidx < MI_COMMIT_MASK_BITS); - - size_t bitcount = *full_size / MI_COMMIT_SIZE; // can be 0 - if (bitidx + bitcount > MI_COMMIT_MASK_BITS) { - _mi_warning_message("commit mask overflow: idx=%zu count=%zu start=%zx end=%zx p=0x%p size=%zu fullsize=%zu\n", bitidx, bitcount, start, end, p, size, *full_size); - } - mi_assert_internal((bitidx + bitcount) <= MI_COMMIT_MASK_BITS); - mi_commit_mask_create(bitidx, bitcount, cm); -} - -static bool mi_segment_commit(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { - mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask)); - - // commit liberal - uint8_t* start = NULL; - size_t full_size = 0; - mi_commit_mask_t mask; - mi_segment_commit_mask(segment, false /* conservative? */, p, size, &start, &full_size, &mask); - if (mi_commit_mask_is_empty(&mask) || full_size == 0) return true; - - if (!mi_commit_mask_all_set(&segment->commit_mask, &mask)) { - // committing - bool is_zero = false; - mi_commit_mask_t cmask; - mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); - _mi_stat_decrease(&_mi_stats_main.committed, _mi_commit_mask_committed_size(&cmask, MI_SEGMENT_SIZE)); // adjust for overlap - if (!_mi_os_commit(start, full_size, &is_zero, stats)) return false; - mi_commit_mask_set(&segment->commit_mask, &mask); - } - - // increase purge expiration when using part of delayed purges -- we assume more allocations are coming soon. - if (mi_commit_mask_any_set(&segment->purge_mask, &mask)) { - segment->purge_expire = _mi_clock_now() + mi_option_get(mi_option_purge_delay); - } - - // always clear any delayed purges in our range (as they are either committed now) - mi_commit_mask_clear(&segment->purge_mask, &mask); - return true; -} - -static bool mi_segment_ensure_committed(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { - mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask)); - // note: assumes commit_mask is always full for huge segments as otherwise the commit mask bits can overflow - if (mi_commit_mask_is_full(&segment->commit_mask) && mi_commit_mask_is_empty(&segment->purge_mask)) return true; // fully committed - mi_assert_internal(segment->kind != MI_SEGMENT_HUGE); - return mi_segment_commit(segment, p, size, stats); -} - -static bool mi_segment_purge(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { - mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask)); - if (!segment->allow_purge) return true; - - // purge conservative - uint8_t* start = NULL; - size_t full_size = 0; - mi_commit_mask_t mask; - mi_segment_commit_mask(segment, true /* conservative? */, p, size, &start, &full_size, &mask); - if (mi_commit_mask_is_empty(&mask) || full_size==0) return true; - - if (mi_commit_mask_any_set(&segment->commit_mask, &mask)) { - // purging - mi_assert_internal((void*)start != (void*)segment); - mi_assert_internal(segment->allow_decommit); - const bool decommitted = _mi_os_purge(start, full_size, stats); // reset or decommit - if (decommitted) { - mi_commit_mask_t cmask; - mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); - _mi_stat_increase(&_mi_stats_main.committed, full_size - _mi_commit_mask_committed_size(&cmask, MI_SEGMENT_SIZE)); // adjust for double counting - mi_commit_mask_clear(&segment->commit_mask, &mask); - } - } - - // always clear any scheduled purges in our range - mi_commit_mask_clear(&segment->purge_mask, &mask); - return true; -} - -static void mi_segment_schedule_purge(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { - if (!segment->allow_purge) return; - - if (mi_option_get(mi_option_purge_delay) == 0) { - mi_segment_purge(segment, p, size, stats); - } - else { - // register for future purge in the purge mask - uint8_t* start = NULL; - size_t full_size = 0; - mi_commit_mask_t mask; - mi_segment_commit_mask(segment, true /*conservative*/, p, size, &start, &full_size, &mask); - if (mi_commit_mask_is_empty(&mask) || full_size==0) return; - - // update delayed commit - mi_assert_internal(segment->purge_expire > 0 || mi_commit_mask_is_empty(&segment->purge_mask)); - mi_commit_mask_t cmask; - mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); // only purge what is committed; span_free may try to decommit more - mi_commit_mask_set(&segment->purge_mask, &cmask); - mi_msecs_t now = _mi_clock_now(); - if (segment->purge_expire == 0) { - // no previous purgess, initialize now - segment->purge_expire = now + mi_option_get(mi_option_purge_delay); - } - else if (segment->purge_expire <= now) { - // previous purge mask already expired - if (segment->purge_expire + mi_option_get(mi_option_purge_extend_delay) <= now) { - mi_segment_try_purge(segment, true, stats); - } - else { - segment->purge_expire = now + mi_option_get(mi_option_purge_extend_delay); // (mi_option_get(mi_option_purge_delay) / 8); // wait a tiny bit longer in case there is a series of free's - } - } - else { - // previous purge mask is not yet expired, increase the expiration by a bit. - segment->purge_expire += mi_option_get(mi_option_purge_extend_delay); - } - } -} - -static void mi_segment_try_purge(mi_segment_t* segment, bool force, mi_stats_t* stats) { - if (!segment->allow_purge || segment->purge_expire == 0 || mi_commit_mask_is_empty(&segment->purge_mask)) return; - mi_msecs_t now = _mi_clock_now(); - if (!force && now < segment->purge_expire) return; - - mi_commit_mask_t mask = segment->purge_mask; - segment->purge_expire = 0; - mi_commit_mask_create_empty(&segment->purge_mask); - - size_t idx; - size_t count; - mi_commit_mask_foreach(&mask, idx, count) { - // if found, decommit that sequence - if (count > 0) { - uint8_t* p = (uint8_t*)segment + (idx*MI_COMMIT_SIZE); - size_t size = count * MI_COMMIT_SIZE; - mi_segment_purge(segment, p, size, stats); - } - } - mi_commit_mask_foreach_end() - mi_assert_internal(mi_commit_mask_is_empty(&segment->purge_mask)); -} - -// called from `mi_heap_collect_ex` -// this can be called per-page so it is important that try_purge has fast exit path -void _mi_segment_collect(mi_segment_t* segment, bool force, mi_segments_tld_t* tld) { - mi_segment_try_purge(segment, force, tld->stats); -} - -/* ----------------------------------------------------------- - Span free ------------------------------------------------------------ */ - -static bool mi_segment_is_abandoned(mi_segment_t* segment) { - return (mi_atomic_load_relaxed(&segment->thread_id) == 0); -} - -// note: can be called on abandoned segments -static void mi_segment_span_free(mi_segment_t* segment, size_t slice_index, size_t slice_count, bool allow_purge, mi_segments_tld_t* tld) { - mi_assert_internal(slice_index < segment->slice_entries); - mi_span_queue_t* sq = (segment->kind == MI_SEGMENT_HUGE || mi_segment_is_abandoned(segment) - ? NULL : mi_span_queue_for(slice_count,tld)); - if (slice_count==0) slice_count = 1; - mi_assert_internal(slice_index + slice_count - 1 < segment->slice_entries); - - // set first and last slice (the intermediates can be undetermined) - mi_slice_t* slice = &segment->slices[slice_index]; - slice->slice_count = (uint32_t)slice_count; - mi_assert_internal(slice->slice_count == slice_count); // no overflow? - slice->slice_offset = 0; - if (slice_count > 1) { - mi_slice_t* last = slice + slice_count - 1; - mi_slice_t* end = (mi_slice_t*)mi_segment_slices_end(segment); - if (last > end) { last = end; } - last->slice_count = 0; - last->slice_offset = (uint32_t)(sizeof(mi_page_t)*(slice_count - 1)); - last->block_size = 0; - } - - // perhaps decommit - if (allow_purge) { - mi_segment_schedule_purge(segment, mi_slice_start(slice), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats); - } - - // and push it on the free page queue (if it was not a huge page) - if (sq != NULL) mi_span_queue_push( sq, slice ); - else slice->block_size = 0; // mark huge page as free anyways -} - -/* -// called from reclaim to add existing free spans -static void mi_segment_span_add_free(mi_slice_t* slice, mi_segments_tld_t* tld) { - mi_segment_t* segment = _mi_ptr_segment(slice); - mi_assert_internal(slice->xblock_size==0 && slice->slice_count>0 && slice->slice_offset==0); - size_t slice_index = mi_slice_index(slice); - mi_segment_span_free(segment,slice_index,slice->slice_count,tld); -} -*/ - -static void mi_segment_span_remove_from_queue(mi_slice_t* slice, mi_segments_tld_t* tld) { - mi_assert_internal(slice->slice_count > 0 && slice->slice_offset==0 && slice->block_size==0); - mi_assert_internal(_mi_ptr_segment(slice)->kind != MI_SEGMENT_HUGE); - mi_span_queue_t* sq = mi_span_queue_for(slice->slice_count, tld); - mi_span_queue_delete(sq, slice); -} - -// note: can be called on abandoned segments -static mi_slice_t* mi_segment_span_free_coalesce(mi_slice_t* slice, mi_segments_tld_t* tld) { - mi_assert_internal(slice != NULL && slice->slice_count > 0 && slice->slice_offset == 0); - mi_segment_t* const segment = _mi_ptr_segment(slice); - const bool is_abandoned = (segment->thread_id == 0); // mi_segment_is_abandoned(segment); - - // for huge pages, just mark as free but don't add to the queues - if (segment->kind == MI_SEGMENT_HUGE) { - // issue #691: segment->used can be 0 if the huge page block was freed while abandoned (reclaim will get here in that case) - mi_assert_internal((segment->used==0 && slice->block_size==0) || segment->used == 1); // decreased right after this call in `mi_segment_page_clear` - slice->block_size = 0; // mark as free anyways - // we should mark the last slice `xblock_size=0` now to maintain invariants but we skip it to - // avoid a possible cache miss (and the segment is about to be freed) - return slice; - } - - // otherwise coalesce the span and add to the free span queues - size_t slice_count = slice->slice_count; - mi_slice_t* next = slice + slice->slice_count; - mi_assert_internal(next <= mi_segment_slices_end(segment)); - if (next < mi_segment_slices_end(segment) && next->block_size==0) { - // free next block -- remove it from free and merge - mi_assert_internal(next->slice_count > 0 && next->slice_offset==0); - slice_count += next->slice_count; // extend - if (!is_abandoned) { mi_segment_span_remove_from_queue(next, tld); } - } - if (slice > segment->slices) { - mi_slice_t* prev = mi_slice_first(slice - 1); - mi_assert_internal(prev >= segment->slices); - if (prev->block_size==0) { - // free previous slice -- remove it from free and merge - mi_assert_internal(prev->slice_count > 0 && prev->slice_offset==0); - slice_count += prev->slice_count; - if (!is_abandoned) { mi_segment_span_remove_from_queue(prev, tld); } - slice = prev; - } - } - - // and add the new free page - mi_segment_span_free(segment, mi_slice_index(slice), slice_count, true, tld); - return slice; -} - - - -/* ----------------------------------------------------------- - Page allocation ------------------------------------------------------------ */ - -// Note: may still return NULL if committing the memory failed -static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_index, size_t slice_count, mi_segments_tld_t* tld) { - mi_assert_internal(slice_index < segment->slice_entries); - mi_slice_t* const slice = &segment->slices[slice_index]; - mi_assert_internal(slice->block_size==0 || slice->block_size==1); - - // commit before changing the slice data - if (!mi_segment_ensure_committed(segment, _mi_segment_page_start_from_slice(segment, slice, 0, NULL), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats)) { - return NULL; // commit failed! - } - - // convert the slices to a page - slice->slice_offset = 0; - slice->slice_count = (uint32_t)slice_count; - mi_assert_internal(slice->slice_count == slice_count); - const size_t bsize = slice_count * MI_SEGMENT_SLICE_SIZE; - slice->block_size = bsize; - mi_page_t* page = mi_slice_to_page(slice); - mi_assert_internal(mi_page_block_size(page) == bsize); - - // set slice back pointers for the first MI_MAX_SLICE_OFFSET_COUNT entries - size_t extra = slice_count-1; - if (extra > MI_MAX_SLICE_OFFSET_COUNT) extra = MI_MAX_SLICE_OFFSET_COUNT; - if (slice_index + extra >= segment->slice_entries) extra = segment->slice_entries - slice_index - 1; // huge objects may have more slices than avaiable entries in the segment->slices - - mi_slice_t* slice_next = slice + 1; - for (size_t i = 1; i <= extra; i++, slice_next++) { - slice_next->slice_offset = (uint32_t)(sizeof(mi_slice_t)*i); - slice_next->slice_count = 0; - slice_next->block_size = 1; - } - - // and also for the last one (if not set already) (the last one is needed for coalescing and for large alignments) - // note: the cast is needed for ubsan since the index can be larger than MI_SLICES_PER_SEGMENT for huge allocations (see #543) - mi_slice_t* last = slice + slice_count - 1; - mi_slice_t* end = (mi_slice_t*)mi_segment_slices_end(segment); - if (last > end) last = end; - if (last > slice) { - last->slice_offset = (uint32_t)(sizeof(mi_slice_t) * (last - slice)); - last->slice_count = 0; - last->block_size = 1; - } - - // and initialize the page - page->is_committed = true; - page->is_huge = (segment->kind == MI_SEGMENT_HUGE); - segment->used++; - return page; -} - -static void mi_segment_slice_split(mi_segment_t* segment, mi_slice_t* slice, size_t slice_count, mi_segments_tld_t* tld) { - mi_assert_internal(_mi_ptr_segment(slice) == segment); - mi_assert_internal(slice->slice_count >= slice_count); - mi_assert_internal(slice->block_size > 0); // no more in free queue - if (slice->slice_count <= slice_count) return; - mi_assert_internal(segment->kind != MI_SEGMENT_HUGE); - size_t next_index = mi_slice_index(slice) + slice_count; - size_t next_count = slice->slice_count - slice_count; - mi_segment_span_free(segment, next_index, next_count, false /* don't purge left-over part */, tld); - slice->slice_count = (uint32_t)slice_count; -} - -static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld) { - mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_LARGE_OBJ_SIZE_MAX); - // search from best fit up - mi_span_queue_t* sq = mi_span_queue_for(slice_count, tld); - if (slice_count == 0) slice_count = 1; - while (sq <= &tld->spans[MI_SEGMENT_BIN_MAX]) { - for (mi_slice_t* slice = sq->first; slice != NULL; slice = slice->next) { - if (slice->slice_count >= slice_count) { - // found one - mi_segment_t* segment = _mi_ptr_segment(slice); - if (_mi_arena_memid_is_suitable(segment->memid, req_arena_id)) { - // found a suitable page span - mi_span_queue_delete(sq, slice); - - if (slice->slice_count > slice_count) { - mi_segment_slice_split(segment, slice, slice_count, tld); - } - mi_assert_internal(slice != NULL && slice->slice_count == slice_count && slice->block_size > 0); - mi_page_t* page = mi_segment_span_allocate(segment, mi_slice_index(slice), slice->slice_count, tld); - if (page == NULL) { - // commit failed; return NULL but first restore the slice - mi_segment_span_free_coalesce(slice, tld); - return NULL; - } - return page; - } - } - } - sq++; - } - // could not find a page.. - return NULL; -} - - -/* ----------------------------------------------------------- - Segment allocation ------------------------------------------------------------ */ - -static mi_segment_t* mi_segment_os_alloc( size_t required, size_t page_alignment, bool eager_delayed, mi_arena_id_t req_arena_id, - size_t* psegment_slices, size_t* pinfo_slices, - bool commit, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) - -{ - mi_memid_t memid; - bool allow_large = (!eager_delayed && (MI_SECURE == 0)); // only allow large OS pages once we are no longer lazy - size_t align_offset = 0; - size_t alignment = MI_SEGMENT_ALIGN; - - if (page_alignment > 0) { - // mi_assert_internal(huge_page != NULL); - mi_assert_internal(page_alignment >= MI_SEGMENT_ALIGN); - alignment = page_alignment; - const size_t info_size = (*pinfo_slices) * MI_SEGMENT_SLICE_SIZE; - align_offset = _mi_align_up( info_size, MI_SEGMENT_ALIGN ); - const size_t extra = align_offset - info_size; - // recalculate due to potential guard pages - *psegment_slices = mi_segment_calculate_slices(required + extra, pinfo_slices); - mi_assert_internal(*psegment_slices > 0 && *psegment_slices <= UINT32_MAX); - } - - const size_t segment_size = (*psegment_slices) * MI_SEGMENT_SLICE_SIZE; - mi_segment_t* segment = (mi_segment_t*)_mi_arena_alloc_aligned(segment_size, alignment, align_offset, commit, allow_large, req_arena_id, &memid, os_tld); - if (segment == NULL) { - return NULL; // failed to allocate - } - - // ensure metadata part of the segment is committed - mi_commit_mask_t commit_mask; - if (memid.initially_committed) { - mi_commit_mask_create_full(&commit_mask); - } - else { - // at least commit the info slices - const size_t commit_needed = _mi_divide_up((*pinfo_slices)*MI_SEGMENT_SLICE_SIZE, MI_COMMIT_SIZE); - mi_assert_internal(commit_needed>0); - mi_commit_mask_create(0, commit_needed, &commit_mask); - mi_assert_internal(commit_needed*MI_COMMIT_SIZE >= (*pinfo_slices)*MI_SEGMENT_SLICE_SIZE); - if (!_mi_os_commit(segment, commit_needed*MI_COMMIT_SIZE, NULL, tld->stats)) { - _mi_arena_free(segment,segment_size,0,memid,tld->stats); - return NULL; - } - } - mi_assert_internal(segment != NULL && (uintptr_t)segment % MI_SEGMENT_SIZE == 0); - - segment->memid = memid; - segment->allow_decommit = !memid.is_pinned; - segment->allow_purge = segment->allow_decommit && (mi_option_get(mi_option_purge_delay) >= 0); - segment->segment_size = segment_size; - segment->commit_mask = commit_mask; - segment->purge_expire = 0; - mi_commit_mask_create_empty(&segment->purge_mask); - - mi_segments_track_size((long)(segment_size), tld); - _mi_segment_map_allocated_at(segment); - return segment; -} - - -// Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` . -static mi_segment_t* mi_segment_alloc(size_t required, size_t page_alignment, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page) -{ - mi_assert_internal((required==0 && huge_page==NULL) || (required>0 && huge_page != NULL)); - - // calculate needed sizes first - size_t info_slices; - size_t segment_slices = mi_segment_calculate_slices(required, &info_slices); - mi_assert_internal(segment_slices > 0 && segment_slices <= UINT32_MAX); - - // Commit eagerly only if not the first N lazy segments (to reduce impact of many threads that allocate just a little) - const bool eager_delay = (// !_mi_os_has_overcommit() && // never delay on overcommit systems - _mi_current_thread_count() > 1 && // do not delay for the first N threads - tld->count < (size_t)mi_option_get(mi_option_eager_commit_delay)); - const bool eager = !eager_delay && mi_option_is_enabled(mi_option_eager_commit); - bool commit = eager || (required > 0); - - // Allocate the segment from the OS - mi_segment_t* segment = mi_segment_os_alloc(required, page_alignment, eager_delay, req_arena_id, - &segment_slices, &info_slices, commit, tld, os_tld); - if (segment == NULL) return NULL; - - // zero the segment info? -- not always needed as it may be zero initialized from the OS - if (!segment->memid.initially_zero) { - ptrdiff_t ofs = offsetof(mi_segment_t, next); - size_t prefix = offsetof(mi_segment_t, slices) - ofs; - size_t zsize = prefix + (sizeof(mi_slice_t) * (segment_slices + 1)); // one more - _mi_memzero((uint8_t*)segment + ofs, zsize); - } - - // initialize the rest of the segment info - const size_t slice_entries = (segment_slices > MI_SLICES_PER_SEGMENT ? MI_SLICES_PER_SEGMENT : segment_slices); - segment->segment_slices = segment_slices; - segment->segment_info_slices = info_slices; - segment->thread_id = _mi_thread_id(); - segment->cookie = _mi_ptr_cookie(segment); - segment->slice_entries = slice_entries; - segment->kind = (required == 0 ? MI_SEGMENT_NORMAL : MI_SEGMENT_HUGE); - - // _mi_memzero(segment->slices, sizeof(mi_slice_t)*(info_slices+1)); - _mi_stat_increase(&tld->stats->page_committed, mi_segment_info_size(segment)); - - // set up guard pages - size_t guard_slices = 0; - if (MI_SECURE>0) { - // in secure mode, we set up a protected page in between the segment info - // and the page data, and at the end of the segment. - size_t os_pagesize = _mi_os_page_size(); - _mi_os_protect((uint8_t*)segment + mi_segment_info_size(segment) - os_pagesize, os_pagesize); - uint8_t* end = (uint8_t*)segment + mi_segment_size(segment) - os_pagesize; - mi_segment_ensure_committed(segment, end, os_pagesize, tld->stats); - _mi_os_protect(end, os_pagesize); - if (slice_entries == segment_slices) segment->slice_entries--; // don't use the last slice :-( - guard_slices = 1; - } - - // reserve first slices for segment info - mi_page_t* page0 = mi_segment_span_allocate(segment, 0, info_slices, tld); - mi_assert_internal(page0!=NULL); if (page0==NULL) return NULL; // cannot fail as we always commit in advance - mi_assert_internal(segment->used == 1); - segment->used = 0; // don't count our internal slices towards usage - - // initialize initial free pages - if (segment->kind == MI_SEGMENT_NORMAL) { // not a huge page - mi_assert_internal(huge_page==NULL); - mi_segment_span_free(segment, info_slices, segment->slice_entries - info_slices, false /* don't purge */, tld); - } - else { - mi_assert_internal(huge_page!=NULL); - mi_assert_internal(mi_commit_mask_is_empty(&segment->purge_mask)); - mi_assert_internal(mi_commit_mask_is_full(&segment->commit_mask)); - *huge_page = mi_segment_span_allocate(segment, info_slices, segment_slices - info_slices - guard_slices, tld); - mi_assert_internal(*huge_page != NULL); // cannot fail as we commit in advance - } - - mi_assert_expensive(mi_segment_is_valid(segment,tld)); - return segment; -} - - -static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t* tld) { - MI_UNUSED(force); - mi_assert_internal(segment != NULL); - mi_assert_internal(segment->next == NULL); - mi_assert_internal(segment->used == 0); - - // Remove the free pages - mi_slice_t* slice = &segment->slices[0]; - const mi_slice_t* end = mi_segment_slices_end(segment); - #if MI_DEBUG>1 - size_t page_count = 0; - #endif - while (slice < end) { - mi_assert_internal(slice->slice_count > 0); - mi_assert_internal(slice->slice_offset == 0); - mi_assert_internal(mi_slice_index(slice)==0 || slice->block_size == 0); // no more used pages .. - if (slice->block_size == 0 && segment->kind != MI_SEGMENT_HUGE) { - mi_segment_span_remove_from_queue(slice, tld); - } - #if MI_DEBUG>1 - page_count++; - #endif - slice = slice + slice->slice_count; - } - mi_assert_internal(page_count == 2); // first page is allocated by the segment itself - - // stats - _mi_stat_decrease(&tld->stats->page_committed, mi_segment_info_size(segment)); - - // return it to the OS - mi_segment_os_free(segment, tld); -} - - -/* ----------------------------------------------------------- - Page Free ------------------------------------------------------------ */ - -static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld); - -// note: can be called on abandoned pages -static mi_slice_t* mi_segment_page_clear(mi_page_t* page, mi_segments_tld_t* tld) { - mi_assert_internal(page->block_size > 0); - mi_assert_internal(mi_page_all_free(page)); - mi_segment_t* segment = _mi_ptr_segment(page); - mi_assert_internal(segment->used > 0); - - size_t inuse = page->capacity * mi_page_block_size(page); - _mi_stat_decrease(&tld->stats->page_committed, inuse); - _mi_stat_decrease(&tld->stats->pages, 1); - - // reset the page memory to reduce memory pressure? - if (segment->allow_decommit && mi_option_is_enabled(mi_option_deprecated_page_reset)) { - size_t psize; - uint8_t* start = _mi_segment_page_start(segment, page, &psize); - _mi_os_reset(start, psize, tld->stats); - } - - // zero the page data, but not the segment fields and heap tag - page->is_zero_init = false; - uint8_t heap_tag = page->heap_tag; - ptrdiff_t ofs = offsetof(mi_page_t, capacity); - _mi_memzero((uint8_t*)page + ofs, sizeof(*page) - ofs); - page->block_size = 1; - page->heap_tag = heap_tag; - - // and free it - mi_slice_t* slice = mi_segment_span_free_coalesce(mi_page_to_slice(page), tld); - segment->used--; - // cannot assert segment valid as it is called during reclaim - // mi_assert_expensive(mi_segment_is_valid(segment, tld)); - return slice; -} - -void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld) -{ - mi_assert(page != NULL); - - mi_segment_t* segment = _mi_page_segment(page); - mi_assert_expensive(mi_segment_is_valid(segment,tld)); - - // mark it as free now - mi_segment_page_clear(page, tld); - mi_assert_expensive(mi_segment_is_valid(segment, tld)); - - if (segment->used == 0) { - // no more used pages; remove from the free list and free the segment - mi_segment_free(segment, force, tld); - } - else if (segment->used == segment->abandoned) { - // only abandoned pages; remove from free list and abandon - mi_segment_abandon(segment,tld); - } - else { - // perform delayed purges - mi_segment_try_purge(segment, false /* force? */, tld->stats); - } -} - - -/* ----------------------------------------------------------- -Abandonment - -When threads terminate, they can leave segments with -live blocks (reachable through other threads). Such segments -are "abandoned" and will be reclaimed by other threads to -reuse their pages and/or free them eventually. The -`thread_id` of such segments is 0. - -When a block is freed in an abandoned segment, the segment -is reclaimed into that thread. - -Moreover, if threads are looking for a fresh segment, they -will first consider abondoned segments -- these can be found -by scanning the arena memory -(segments outside arena memoryare only reclaimed by a free). ------------------------------------------------------------ */ - -// legacy: Wait until there are no more pending reads on segments that used to be in the abandoned list -void _mi_abandoned_await_readers(void) { - // nothing needed -} - -/* ----------------------------------------------------------- - Abandon segment/page ------------------------------------------------------------ */ - -static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_assert_internal(segment->used == segment->abandoned); - mi_assert_internal(segment->used > 0); - mi_assert_internal(segment->abandoned_visits == 0); - mi_assert_expensive(mi_segment_is_valid(segment,tld)); - - // remove the free pages from the free page queues - mi_slice_t* slice = &segment->slices[0]; - const mi_slice_t* end = mi_segment_slices_end(segment); - while (slice < end) { - mi_assert_internal(slice->slice_count > 0); - mi_assert_internal(slice->slice_offset == 0); - if (slice->block_size == 0) { // a free page - mi_segment_span_remove_from_queue(slice,tld); - slice->block_size = 0; // but keep it free - } - slice = slice + slice->slice_count; - } - - // perform delayed decommits (forcing is much slower on mstress) - // Only abandoned segments in arena memory can be reclaimed without a free - // so if a segment is not from an arena we force purge here to be conservative. - const bool force_purge = (segment->memid.memkind != MI_MEM_ARENA) || mi_option_is_enabled(mi_option_abandoned_page_purge); - mi_segment_try_purge(segment, force_purge, tld->stats); - - // all pages in the segment are abandoned; add it to the abandoned list - _mi_stat_increase(&tld->stats->segments_abandoned, 1); - mi_segments_track_size(-((long)mi_segment_size(segment)), tld); - segment->thread_id = 0; - segment->abandoned_visits = 1; // from 0 to 1 to signify it is abandoned - if (segment->was_reclaimed) { - tld->reclaim_count--; - segment->was_reclaimed = false; - } - _mi_arena_segment_mark_abandoned(segment); -} - -void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld) { - mi_assert(page != NULL); - mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE); - mi_assert_internal(mi_page_heap(page) == NULL); - mi_segment_t* segment = _mi_page_segment(page); - - mi_assert_expensive(mi_segment_is_valid(segment,tld)); - segment->abandoned++; - - _mi_stat_increase(&tld->stats->pages_abandoned, 1); - mi_assert_internal(segment->abandoned <= segment->used); - if (segment->used == segment->abandoned) { - // all pages are abandoned, abandon the entire segment - mi_segment_abandon(segment, tld); - } -} - -/* ----------------------------------------------------------- - Reclaim abandoned pages ------------------------------------------------------------ */ - -static mi_slice_t* mi_slices_start_iterate(mi_segment_t* segment, const mi_slice_t** end) { - mi_slice_t* slice = &segment->slices[0]; - *end = mi_segment_slices_end(segment); - mi_assert_internal(slice->slice_count>0 && slice->block_size>0); // segment allocated page - slice = slice + slice->slice_count; // skip the first segment allocated page - return slice; -} - -// Possibly free pages and check if free space is available -static bool mi_segment_check_free(mi_segment_t* segment, size_t slices_needed, size_t block_size, mi_segments_tld_t* tld) -{ - mi_assert_internal(mi_segment_is_abandoned(segment)); - bool has_page = false; - - // for all slices - const mi_slice_t* end; - mi_slice_t* slice = mi_slices_start_iterate(segment, &end); - while (slice < end) { - mi_assert_internal(slice->slice_count > 0); - mi_assert_internal(slice->slice_offset == 0); - if (mi_slice_is_used(slice)) { // used page - // ensure used count is up to date and collect potential concurrent frees - mi_page_t* const page = mi_slice_to_page(slice); - _mi_page_free_collect(page, false); - if (mi_page_all_free(page)) { - // if this page is all free now, free it without adding to any queues (yet) - mi_assert_internal(page->next == NULL && page->prev==NULL); - _mi_stat_decrease(&tld->stats->pages_abandoned, 1); - segment->abandoned--; - slice = mi_segment_page_clear(page, tld); // re-assign slice due to coalesce! - mi_assert_internal(!mi_slice_is_used(slice)); - if (slice->slice_count >= slices_needed) { - has_page = true; - } - } - else if (mi_page_block_size(page) == block_size && mi_page_has_any_available(page)) { - // a page has available free blocks of the right size - has_page = true; - } - } - else { - // empty span - if (slice->slice_count >= slices_needed) { - has_page = true; - } - } - slice = slice + slice->slice_count; - } - return has_page; -} - -// Reclaim an abandoned segment; returns NULL if the segment was freed -// set `right_page_reclaimed` to `true` if it reclaimed a page of the right `block_size` that was not full. -static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap, size_t requested_block_size, bool* right_page_reclaimed, mi_segments_tld_t* tld) { - if (right_page_reclaimed != NULL) { *right_page_reclaimed = false; } - // can be 0 still with abandoned_next, or already a thread id for segments outside an arena that are reclaimed on a free. - mi_assert_internal(mi_atomic_load_relaxed(&segment->thread_id) == 0 || mi_atomic_load_relaxed(&segment->thread_id) == _mi_thread_id()); - mi_atomic_store_release(&segment->thread_id, _mi_thread_id()); - segment->abandoned_visits = 0; - segment->was_reclaimed = true; - tld->reclaim_count++; - mi_segments_track_size((long)mi_segment_size(segment), tld); - mi_assert_internal(segment->next == NULL); - _mi_stat_decrease(&tld->stats->segments_abandoned, 1); - - // for all slices - const mi_slice_t* end; - mi_slice_t* slice = mi_slices_start_iterate(segment, &end); - while (slice < end) { - mi_assert_internal(slice->slice_count > 0); - mi_assert_internal(slice->slice_offset == 0); - if (mi_slice_is_used(slice)) { - // in use: reclaim the page in our heap - mi_page_t* page = mi_slice_to_page(slice); - mi_assert_internal(page->is_committed); - mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE); - mi_assert_internal(mi_page_heap(page) == NULL); - mi_assert_internal(page->next == NULL && page->prev==NULL); - _mi_stat_decrease(&tld->stats->pages_abandoned, 1); - segment->abandoned--; - // set the heap again and allow heap thread delayed free again. - mi_heap_t* target_heap = _mi_heap_by_tag(heap, page->heap_tag); // allow custom heaps to separate objects - if (target_heap == NULL) { - target_heap = heap; - _mi_error_message(EINVAL, "page with tag %u cannot be reclaimed by a heap with the same tag (using %u instead)\n", page->heap_tag, heap->tag ); - } - mi_page_set_heap(page, target_heap); - _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, true); // override never (after heap is set) - _mi_page_free_collect(page, false); // ensure used count is up to date - if (mi_page_all_free(page)) { - // if everything free by now, free the page - slice = mi_segment_page_clear(page, tld); // set slice again due to coalesceing - } - else { - // otherwise reclaim it into the heap - _mi_page_reclaim(target_heap, page); - if (requested_block_size == mi_page_block_size(page) && mi_page_has_any_available(page) && heap == target_heap) { - if (right_page_reclaimed != NULL) { *right_page_reclaimed = true; } - } - } - } - else { - // the span is free, add it to our page queues - slice = mi_segment_span_free_coalesce(slice, tld); // set slice again due to coalesceing - } - mi_assert_internal(slice->slice_count>0 && slice->slice_offset==0); - slice = slice + slice->slice_count; - } - - mi_assert(segment->abandoned == 0); - mi_assert_expensive(mi_segment_is_valid(segment, tld)); - if (segment->used == 0) { // due to page_clear - mi_assert_internal(right_page_reclaimed == NULL || !(*right_page_reclaimed)); - mi_segment_free(segment, false, tld); - return NULL; - } - else { - return segment; - } -} - -// attempt to reclaim a particular segment (called from multi threaded free `alloc.c:mi_free_block_mt`) -bool _mi_segment_attempt_reclaim(mi_heap_t* heap, mi_segment_t* segment) { - if (mi_atomic_load_relaxed(&segment->thread_id) != 0) return false; // it is not abandoned - // don't reclaim more from a free than half the current segments - // this is to prevent a pure free-ing thread to start owning too many segments - if (heap->tld->segments.reclaim_count * 2 > heap->tld->segments.count) return false; - if (_mi_arena_segment_clear_abandoned(segment)) { // atomically unabandon - mi_segment_t* res = mi_segment_reclaim(segment, heap, 0, NULL, &heap->tld->segments); - mi_assert_internal(res == segment); - return (res != NULL); - } - return false; -} - -void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld) { - mi_segment_t* segment; - mi_arena_field_cursor_t current; _mi_arena_field_cursor_init(heap, ¤t); - while ((segment = _mi_arena_segment_clear_abandoned_next(¤t)) != NULL) { - mi_segment_reclaim(segment, heap, 0, NULL, tld); - } -} - -static long mi_segment_get_reclaim_tries(void) { - // limit the tries to 10% (default) of the abandoned segments with at least 8 and at most 1024 tries. - const size_t perc = (size_t)mi_option_get_clamp(mi_option_max_segment_reclaim, 0, 100); - if (perc <= 0) return 0; - const size_t total_count = _mi_arena_segment_abandoned_count(); - if (total_count == 0) return 0; - const size_t relative_count = (total_count > 10000 ? (total_count / 100) * perc : (total_count * perc) / 100); // avoid overflow - long max_tries = (long)(relative_count <= 1 ? 1 : (relative_count > 1024 ? 1024 : relative_count)); - if (max_tries < 8 && total_count > 8) { max_tries = 8; } - return max_tries; -} - -static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slices, size_t block_size, bool* reclaimed, mi_segments_tld_t* tld) -{ - *reclaimed = false; - long max_tries = mi_segment_get_reclaim_tries(); - if (max_tries <= 0) return NULL; - - mi_segment_t* segment; - mi_arena_field_cursor_t current; _mi_arena_field_cursor_init(heap, ¤t); - while ((max_tries-- > 0) && ((segment = _mi_arena_segment_clear_abandoned_next(¤t)) != NULL)) - { - segment->abandoned_visits++; - // todo: should we respect numa affinity for abondoned reclaim? perhaps only for the first visit? - // todo: an arena exclusive heap will potentially visit many abandoned unsuitable segments and use many tries - // Perhaps we can skip non-suitable ones in a better way? - bool is_suitable = _mi_heap_memid_is_suitable(heap, segment->memid); - bool has_page = mi_segment_check_free(segment,needed_slices,block_size,tld); // try to free up pages (due to concurrent frees) - if (segment->used == 0) { - // free the segment (by forced reclaim) to make it available to other threads. - // note1: we prefer to free a segment as that might lead to reclaiming another - // segment that is still partially used. - // note2: we could in principle optimize this by skipping reclaim and directly - // freeing but that would violate some invariants temporarily) - mi_segment_reclaim(segment, heap, 0, NULL, tld); - } - else if (has_page && is_suitable) { - // found a large enough free span, or a page of the right block_size with free space - // we return the result of reclaim (which is usually `segment`) as it might free - // the segment due to concurrent frees (in which case `NULL` is returned). - return mi_segment_reclaim(segment, heap, block_size, reclaimed, tld); - } - else if (segment->abandoned_visits > 3 && is_suitable) { - // always reclaim on 3rd visit to limit the abandoned queue length. - mi_segment_reclaim(segment, heap, 0, NULL, tld); - } - else { - // otherwise, push on the visited list so it gets not looked at too quickly again - mi_segment_try_purge(segment, false /* true force? */, tld->stats); // force purge if needed as we may not visit soon again - _mi_arena_segment_mark_abandoned(segment); - } - } - return NULL; -} - - -void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* tld) -{ - mi_segment_t* segment; - mi_arena_field_cursor_t current; _mi_arena_field_cursor_init(heap, ¤t); - long max_tries = (force ? (long)_mi_arena_segment_abandoned_count() : 1024); // limit latency - while ((max_tries-- > 0) && ((segment = _mi_arena_segment_clear_abandoned_next(¤t)) != NULL)) { - mi_segment_check_free(segment,0,0,tld); // try to free up pages (due to concurrent frees) - if (segment->used == 0) { - // free the segment (by forced reclaim) to make it available to other threads. - // note: we could in principle optimize this by skipping reclaim and directly - // freeing but that would violate some invariants temporarily) - mi_segment_reclaim(segment, heap, 0, NULL, tld); - } - else { - // otherwise, purge if needed and push on the visited list - // note: forced purge can be expensive if many threads are destroyed/created as in mstress. - mi_segment_try_purge(segment, force, tld->stats); - _mi_arena_segment_mark_abandoned(segment); - } - } -} - -/* ----------------------------------------------------------- - Reclaim or allocate ------------------------------------------------------------ */ - -static mi_segment_t* mi_segment_reclaim_or_alloc(mi_heap_t* heap, size_t needed_slices, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) -{ - mi_assert_internal(block_size <= MI_LARGE_OBJ_SIZE_MAX); - - // 1. try to reclaim an abandoned segment - bool reclaimed; - mi_segment_t* segment = mi_segment_try_reclaim(heap, needed_slices, block_size, &reclaimed, tld); - if (reclaimed) { - // reclaimed the right page right into the heap - mi_assert_internal(segment != NULL); - return NULL; // pretend out-of-memory as the page will be in the page queue of the heap with available blocks - } - else if (segment != NULL) { - // reclaimed a segment with a large enough empty span in it - return segment; - } - // 2. otherwise allocate a fresh segment - return mi_segment_alloc(0, 0, heap->arena_id, tld, os_tld, NULL); -} - - -/* ----------------------------------------------------------- - Page allocation ------------------------------------------------------------ */ - -static mi_page_t* mi_segments_page_alloc(mi_heap_t* heap, mi_page_kind_t page_kind, size_t required, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) -{ - mi_assert_internal(required <= MI_LARGE_OBJ_SIZE_MAX && page_kind <= MI_PAGE_LARGE); - - // find a free page - size_t page_size = _mi_align_up(required, (required > MI_MEDIUM_PAGE_SIZE ? MI_MEDIUM_PAGE_SIZE : MI_SEGMENT_SLICE_SIZE)); - size_t slices_needed = page_size / MI_SEGMENT_SLICE_SIZE; - mi_assert_internal(slices_needed * MI_SEGMENT_SLICE_SIZE == page_size); - mi_page_t* page = mi_segments_page_find_and_allocate(slices_needed, heap->arena_id, tld); //(required <= MI_SMALL_SIZE_MAX ? 0 : slices_needed), tld); - if (page==NULL) { - // no free page, allocate a new segment and try again - if (mi_segment_reclaim_or_alloc(heap, slices_needed, block_size, tld, os_tld) == NULL) { - // OOM or reclaimed a good page in the heap - return NULL; - } - else { - // otherwise try again - return mi_segments_page_alloc(heap, page_kind, required, block_size, tld, os_tld); - } - } - mi_assert_internal(page != NULL && page->slice_count*MI_SEGMENT_SLICE_SIZE == page_size); - mi_assert_internal(_mi_ptr_segment(page)->thread_id == _mi_thread_id()); - mi_segment_try_purge(_mi_ptr_segment(page), false, tld->stats); - return page; -} - - - -/* ----------------------------------------------------------- - Huge page allocation ------------------------------------------------------------ */ - -static mi_page_t* mi_segment_huge_page_alloc(size_t size, size_t page_alignment, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) -{ - mi_page_t* page = NULL; - mi_segment_t* segment = mi_segment_alloc(size,page_alignment,req_arena_id,tld,os_tld,&page); - if (segment == NULL || page==NULL) return NULL; - mi_assert_internal(segment->used==1); - mi_assert_internal(mi_page_block_size(page) >= size); - #if MI_HUGE_PAGE_ABANDON - segment->thread_id = 0; // huge segments are immediately abandoned - #endif - - // for huge pages we initialize the block_size as we may - // overallocate to accommodate large alignments. - size_t psize; - uint8_t* start = _mi_segment_page_start(segment, page, &psize); - page->block_size = psize; - mi_assert_internal(page->is_huge); - - // decommit the part of the prefix of a page that will not be used; this can be quite large (close to MI_SEGMENT_SIZE) - if (page_alignment > 0 && segment->allow_decommit) { - uint8_t* aligned_p = (uint8_t*)_mi_align_up((uintptr_t)start, page_alignment); - mi_assert_internal(_mi_is_aligned(aligned_p, page_alignment)); - mi_assert_internal(psize - (aligned_p - start) >= size); - uint8_t* decommit_start = start + sizeof(mi_block_t); // for the free list - ptrdiff_t decommit_size = aligned_p - decommit_start; - _mi_os_reset(decommit_start, decommit_size, &_mi_stats_main); // note: cannot use segment_decommit on huge segments - } - - return page; -} - -#if MI_HUGE_PAGE_ABANDON -// free huge block from another thread -void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) { - // huge page segments are always abandoned and can be freed immediately by any thread - mi_assert_internal(segment->kind==MI_SEGMENT_HUGE); - mi_assert_internal(segment == _mi_page_segment(page)); - mi_assert_internal(mi_atomic_load_relaxed(&segment->thread_id)==0); - - // claim it and free - mi_heap_t* heap = mi_heap_get_default(); // issue #221; don't use the internal get_default_heap as we need to ensure the thread is initialized. - // paranoia: if this it the last reference, the cas should always succeed - size_t expected_tid = 0; - if (mi_atomic_cas_strong_acq_rel(&segment->thread_id, &expected_tid, heap->thread_id)) { - mi_block_set_next(page, block, page->free); - page->free = block; - page->used--; - page->is_zero_init = false; - mi_assert(page->used == 0); - mi_tld_t* tld = heap->tld; - _mi_segment_page_free(page, true, &tld->segments); - } -#if (MI_DEBUG!=0) - else { - mi_assert_internal(false); - } -#endif -} - -#else -// reset memory of a huge block from another thread -void _mi_segment_huge_page_reset(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) { - MI_UNUSED(page); - mi_assert_internal(segment->kind == MI_SEGMENT_HUGE); - mi_assert_internal(segment == _mi_page_segment(page)); - mi_assert_internal(page->used == 1); // this is called just before the free - mi_assert_internal(page->free == NULL); - if (segment->allow_decommit) { - size_t csize = mi_usable_size(block); - if (csize > sizeof(mi_block_t)) { - csize = csize - sizeof(mi_block_t); - uint8_t* p = (uint8_t*)block + sizeof(mi_block_t); - _mi_os_reset(p, csize, &_mi_stats_main); // note: cannot use segment_decommit on huge segments - } - } -} -#endif - -/* ----------------------------------------------------------- - Page allocation and free ------------------------------------------------------------ */ -mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - mi_page_t* page; - if mi_unlikely(page_alignment > MI_BLOCK_ALIGNMENT_MAX) { - mi_assert_internal(_mi_is_power_of_two(page_alignment)); - mi_assert_internal(page_alignment >= MI_SEGMENT_SIZE); - if (page_alignment < MI_SEGMENT_SIZE) { page_alignment = MI_SEGMENT_SIZE; } - page = mi_segment_huge_page_alloc(block_size,page_alignment,heap->arena_id,tld,os_tld); - } - else if (block_size <= MI_SMALL_OBJ_SIZE_MAX) { - page = mi_segments_page_alloc(heap,MI_PAGE_SMALL,block_size,block_size,tld,os_tld); - } - else if (block_size <= MI_MEDIUM_OBJ_SIZE_MAX) { - page = mi_segments_page_alloc(heap,MI_PAGE_MEDIUM,MI_MEDIUM_PAGE_SIZE,block_size,tld, os_tld); - } - else if (block_size <= MI_LARGE_OBJ_SIZE_MAX) { - page = mi_segments_page_alloc(heap,MI_PAGE_LARGE,block_size,block_size,tld, os_tld); - } - else { - page = mi_segment_huge_page_alloc(block_size,page_alignment,heap->arena_id,tld,os_tld); - } - mi_assert_internal(page == NULL || _mi_heap_memid_is_suitable(heap, _mi_page_segment(page)->memid)); - mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld)); - return page; -} - - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/static.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/static.c index bf025eb7..2383f659 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/static.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/static.c @@ -20,10 +20,11 @@ terms of the MIT license. A copy of the license can be found in the file // containing the whole library. If it is linked first // it will override all the standard library allocation // functions (on Unix's). -#include "alloc.c" // includes alloc-override.c +#include "alloc.c" // includes alloc-override.c and free.c #include "alloc-aligned.c" #include "alloc-posix.c" #include "arena.c" +#include "arena-meta.c" #include "bitmap.c" #include "heap.c" #include "init.c" @@ -31,10 +32,11 @@ terms of the MIT license. A copy of the license can be found in the file #include "options.c" #include "os.c" #include "page.c" // includes page-queue.c -#include "random.c" -#include "segment.c" -#include "segment-map.c" +#include "page-map.c" +#include "random.c" #include "stats.c" +#include "theap.c" +#include "threadlocal.c" #include "prim/prim.c" #if MI_OSX_ZONE #include "prim/osx/alloc-override-zone.c" diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/stats.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/stats.c index a9364027..5cc3bbfe 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/stats.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/stats.c @@ -1,10 +1,11 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #include "mimalloc.h" +#include "mimalloc-stats.h" #include "mimalloc/internal.h" #include "mimalloc/atomic.h" #include "mimalloc/prim.h" @@ -19,115 +20,124 @@ terms of the MIT license. A copy of the license can be found in the file Statistics operations ----------------------------------------------------------- */ -static bool mi_is_in_main(void* stat) { - return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main - && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t))); +static void mi_stat_update_mt(mi_stat_count_t* stat, int64_t amount) { + if (amount == 0) return; + // add atomically + int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount); + mi_atomic_maxi64_relaxed(&stat->peak, current + amount); + if (amount > 0) { + mi_atomic_addi64_relaxed(&stat->total, amount); + } } static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) { if (amount == 0) return; - if (mi_is_in_main(stat)) - { - // add atomically (for abandoned pages) - int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount); - mi_atomic_maxi64_relaxed(&stat->peak, current + amount); - if (amount > 0) { - mi_atomic_addi64_relaxed(&stat->allocated,amount); - } - else { - mi_atomic_addi64_relaxed(&stat->freed, -amount); - } - } - else { - // add thread local - stat->current += amount; - if (stat->current > stat->peak) stat->peak = stat->current; - if (amount > 0) { - stat->allocated += amount; - } - else { - stat->freed += -amount; - } - } + // add thread local + stat->current += amount; + if (stat->current > stat->peak) { stat->peak = stat->current; } + if (amount > 0) { stat->total += amount; } } -void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) { - if (mi_is_in_main(stat)) { - mi_atomic_addi64_relaxed( &stat->count, 1 ); - mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount ); - } - else { - stat->count++; - stat->total += amount; - } + +void __mi_stat_counter_increase_mt(mi_stat_counter_t* stat, size_t amount) { + mi_atomic_addi64_relaxed(&stat->total, (int64_t)amount); } -void _mi_stat_increase(mi_stat_count_t* stat, size_t amount) { +void __mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) { + stat->total += amount; +} + +void __mi_stat_increase_mt(mi_stat_count_t* stat, size_t amount) { + mi_stat_update_mt(stat, (int64_t)amount); +} +void __mi_stat_increase(mi_stat_count_t* stat, size_t amount) { mi_stat_update(stat, (int64_t)amount); } -void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) { +void __mi_stat_decrease_mt(mi_stat_count_t* stat, size_t amount) { + mi_stat_update_mt(stat, -((int64_t)amount)); +} +void __mi_stat_decrease(mi_stat_count_t* stat, size_t amount) { mi_stat_update(stat, -((int64_t)amount)); } + +// Adjust stats to compensate; for example before committing a range, +// first adjust downwards with parts that were already committed so +// we avoid double counting. +static void mi_stat_adjust_mt(mi_stat_count_t* stat, int64_t amount) { + if (amount == 0) return; + // adjust atomically + mi_atomic_addi64_relaxed(&stat->current, amount); + mi_atomic_addi64_relaxed(&stat->total, amount); +} + +static void mi_stat_adjust(mi_stat_count_t* stat, int64_t amount) { + if (amount == 0) return; + stat->current += amount; + stat->total += amount; +} + +void __mi_stat_adjust_increase_mt(mi_stat_count_t* stat, size_t amount) { + mi_stat_adjust_mt(stat, (int64_t)amount); +} +void __mi_stat_adjust_increase(mi_stat_count_t* stat, size_t amount) { + mi_stat_adjust(stat, (int64_t)amount); +} +void __mi_stat_adjust_decrease_mt(mi_stat_count_t* stat, size_t amount) { + mi_stat_adjust_mt(stat, -((int64_t)amount)); +} +void __mi_stat_adjust_decrease(mi_stat_count_t* stat, size_t amount) { + mi_stat_adjust(stat, -((int64_t)amount)); +} + + // must be thread safe as it is called from stats_merge -static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) { +static void mi_stat_count_add_mt(mi_stat_count_t* stat, const mi_stat_count_t* src) { if (stat==src) return; - if (src->allocated==0 && src->freed==0) return; - mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit); - mi_atomic_addi64_relaxed( &stat->current, src->current * unit); - mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit); - // peak scores do not work across threads.. - mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit); + mi_atomic_void_addi64_relaxed(&stat->total, &src->total); + const int64_t prev_current = mi_atomic_addi64_relaxed(&stat->current, src->current); + + // Global current plus thread peak approximates new global peak + // note: peak scores do really not work across threads. + // we used to just add them together but that often overestimates in practice. + // similarly, max does not seem to work well. The current approach + // by Artem Kharytoniuk (@artem-lunarg) seems to work better, see PR#1112 + // for a longer description. + mi_atomic_maxi64_relaxed(&stat->peak, prev_current + src->peak); } -static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) { +static void mi_stat_counter_add_mt(mi_stat_counter_t* stat, const mi_stat_counter_t* src) { if (stat==src) return; - mi_atomic_addi64_relaxed( &stat->total, src->total * unit); - mi_atomic_addi64_relaxed( &stat->count, src->count * unit); + mi_atomic_void_addi64_relaxed(&stat->total, &src->total); } +#define MI_STAT_COUNT(stat) mi_stat_count_add_mt(&stats->stat, &src->stat); +#define MI_STAT_COUNTER(stat) mi_stat_counter_add_mt(&stats->stat, &src->stat); + // must be thread safe as it is called from stats_merge static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { - if (stats==src) return; - mi_stat_add(&stats->segments, &src->segments,1); - mi_stat_add(&stats->pages, &src->pages,1); - mi_stat_add(&stats->reserved, &src->reserved, 1); - mi_stat_add(&stats->committed, &src->committed, 1); - mi_stat_add(&stats->reset, &src->reset, 1); - mi_stat_add(&stats->purged, &src->purged, 1); - mi_stat_add(&stats->page_committed, &src->page_committed, 1); - - mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1); - mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1); - mi_stat_add(&stats->threads, &src->threads, 1); - - mi_stat_add(&stats->malloc, &src->malloc, 1); - mi_stat_add(&stats->segments_cache, &src->segments_cache, 1); - mi_stat_add(&stats->normal, &src->normal, 1); - mi_stat_add(&stats->huge, &src->huge, 1); - mi_stat_add(&stats->large, &src->large, 1); - - mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1); - mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1); - mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1); - mi_stat_counter_add(&stats->reset_calls, &src->reset_calls, 1); - mi_stat_counter_add(&stats->purge_calls, &src->purge_calls, 1); - - mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1); - mi_stat_counter_add(&stats->searches, &src->searches, 1); - mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1); - mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1); - mi_stat_counter_add(&stats->large_count, &src->large_count, 1); -#if MI_STAT>1 + if (stats==NULL || src==NULL || stats==src) return; + + // copy all fields + MI_STAT_FIELDS() + + #if MI_STAT>1 for (size_t i = 0; i <= MI_BIN_HUGE; i++) { - if (src->normal_bins[i].allocated > 0 || src->normal_bins[i].freed > 0) { - mi_stat_add(&stats->normal_bins[i], &src->normal_bins[i], 1); - } + mi_stat_count_add_mt(&stats->malloc_bins[i], &src->malloc_bins[i]); + } + #endif + for (size_t i = 0; i <= MI_BIN_HUGE; i++) { + mi_stat_count_add_mt(&stats->page_bins[i], &src->page_bins[i]); + } + for (size_t i = 0; i < MI_CBIN_COUNT; i++) { + mi_stat_count_add_mt(&stats->chunk_bins[i], &src->chunk_bins[i]); } -#endif } +#undef MI_STAT_COUNT +#undef MI_STAT_COUNTER + /* ----------------------------------------------------------- Display statistics ----------------------------------------------------------- */ @@ -136,7 +146,7 @@ static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { // unit == 0: count as decimal // unit < 0 : count in binary static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) { - char buf[32]; buf[0] = 0; + char buf[32]; _mi_memzero_var(buf); int len = 32; const char* suffix = (unit <= 0 ? " " : "B"); const int64_t base = (unit == 0 ? 1000 : 1024); @@ -174,30 +184,30 @@ static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* ar } static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg, const char* notok ) { - _mi_fprintf(out, arg,"%10s:", msg); + _mi_fprintf(out, arg," %-10s:", msg); if (unit != 0) { if (unit > 0) { mi_print_amount(stat->peak, unit, out, arg); - mi_print_amount(stat->allocated, unit, out, arg); - mi_print_amount(stat->freed, unit, out, arg); + mi_print_amount(stat->total, unit, out, arg); + // mi_print_amount(stat->freed, unit, out, arg); mi_print_amount(stat->current, unit, out, arg); mi_print_amount(unit, 1, out, arg); - mi_print_count(stat->allocated, unit, out, arg); + mi_print_count(stat->total, unit, out, arg); } else { mi_print_amount(stat->peak, -1, out, arg); - mi_print_amount(stat->allocated, -1, out, arg); - mi_print_amount(stat->freed, -1, out, arg); + mi_print_amount(stat->total, -1, out, arg); + // mi_print_amount(stat->freed, -1, out, arg); mi_print_amount(stat->current, -1, out, arg); if (unit == -1) { _mi_fprintf(out, arg, "%24s", ""); } else { mi_print_amount(-unit, 1, out, arg); - mi_print_count((stat->allocated / -unit), 0, out, arg); + mi_print_count((stat->total / -unit), 0, out, arg); } } - if (stat->allocated > stat->freed) { + if (stat->current != 0) { _mi_fprintf(out, arg, " "); _mi_fprintf(out, arg, (notok == NULL ? "not all freed" : notok)); _mi_fprintf(out, arg, "\n"); @@ -207,10 +217,9 @@ static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64 } } else { - mi_print_amount(stat->peak, 1, out, arg); - mi_print_amount(stat->allocated, 1, out, arg); - _mi_fprintf(out, arg, "%11s", " "); // no freed - mi_print_amount(stat->current, 1, out, arg); + mi_print_amount(stat->peak, 0, out, arg); + mi_print_amount(stat->total, 0, out, arg); + mi_print_amount(stat->current, 0, out, arg); _mi_fprintf(out, arg, "\n"); } } @@ -219,47 +228,59 @@ static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t mi_stat_print_ex(stat, msg, unit, out, arg, NULL); } -static void mi_stat_peak_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) { - _mi_fprintf(out, arg, "%10s:", msg); - mi_print_amount(stat->peak, unit, out, arg); +#if MI_STAT>1 +static void mi_stat_total_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) { + _mi_fprintf(out, arg, " %-10s:", msg); + _mi_fprintf(out, arg, "%12s", " "); // no peak + mi_print_amount(stat->total, unit, out, arg); _mi_fprintf(out, arg, "\n"); } +#endif static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) { - _mi_fprintf(out, arg, "%10s:", msg); - mi_print_amount(stat->total, -1, out, arg); + _mi_fprintf(out, arg, " %-10s:", msg); + mi_print_amount(stat->total, 0, out, arg); _mi_fprintf(out, arg, "\n"); } +static void mi_stat_counter_print_size(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) { + _mi_fprintf(out, arg, " %-10s:", msg); + mi_print_amount(stat->total, 1, out, arg); + _mi_fprintf(out, arg, "\n"); +} -static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) { - const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count)); - const long avg_whole = (long)(avg_tens/10); - const long avg_frac1 = (long)(avg_tens%10); - _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1); +static void mi_stat_average_print(int64_t count, int64_t total, const char* msg, mi_output_fun* out, void* arg) { + const int64_t avg_tens = (count == 0 ? 0 : (total*10 / count)); + const int64_t avg_whole = avg_tens/10; + const int64_t avg_frac1 = avg_tens%10; + _mi_fprintf(out, arg, " %-10s: %5lld.%lld avg\n", msg, avg_whole, avg_frac1); } -static void mi_print_header(mi_output_fun* out, void* arg ) { - _mi_fprintf(out, arg, "%10s: %11s %11s %11s %11s %11s %11s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count "); +static void mi_print_header(const char* name,mi_output_fun* out, void* arg ) { + _mi_fprintf(out, arg, " %-11s %11s %11s %11s %11s %11s\n", + name, "peak ", "total ", "current ", "block ", "total# "); } #if MI_STAT>1 -static void mi_stats_print_bins(const mi_stat_count_t* bins, size_t max, const char* fmt, mi_output_fun* out, void* arg) { +static bool mi_stats_print_bins(const mi_stat_count_t* bins, size_t max, mi_output_fun* out, void* arg) { bool found = false; char buf[64]; for (size_t i = 0; i <= max; i++) { - if (bins[i].allocated > 0) { + if (bins[i].total > 0) { found = true; - int64_t unit = _mi_bin_size((uint8_t)i); - _mi_snprintf(buf, 64, "%s %3lu", fmt, (long)i); - mi_stat_print(&bins[i], buf, unit, out, arg); + const size_t unit = _mi_bin_size((uint8_t)i); + const char* pagekind = (unit <= MI_SMALL_MAX_OBJ_SIZE ? "S" : + (unit <= MI_MEDIUM_MAX_OBJ_SIZE ? "M" : + (unit <= MI_LARGE_MAX_OBJ_SIZE ? "L" : "H"))); + _mi_snprintf(buf, 64, "bin%2s %3lu", pagekind, (long)i); + mi_stat_print(&bins[i], buf, (int64_t)unit, out, arg); } } if (found) { _mi_fprintf(out, arg, "\n"); - mi_print_header(out, arg); } + return found; } #endif @@ -299,56 +320,8 @@ static void mi_cdecl mi_buffered_out(const char* msg, void* arg) { // Print statistics //------------------------------------------------------------ -static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept { - // wrap the output function to be line buffered - char buf[256]; - buffered_t buffer = { out0, arg0, NULL, 0, 255 }; - buffer.buf = buf; - mi_output_fun* out = &mi_buffered_out; - void* arg = &buffer; - - // and print using that - mi_print_header(out,arg); - #if MI_STAT>1 - mi_stats_print_bins(stats->normal_bins, MI_BIN_HUGE, "normal",out,arg); - #endif - #if MI_STAT - mi_stat_print(&stats->normal, "normal", (stats->normal_count.count == 0 ? 1 : -(stats->normal.allocated / stats->normal_count.count)), out, arg); - mi_stat_print(&stats->large, "large", (stats->large_count.count == 0 ? 1 : -(stats->large.allocated / stats->large_count.count)), out, arg); - mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg); - mi_stat_count_t total = { 0,0,0,0 }; - mi_stat_add(&total, &stats->normal, 1); - mi_stat_add(&total, &stats->large, 1); - mi_stat_add(&total, &stats->huge, 1); - mi_stat_print(&total, "total", 1, out, arg); - #endif - #if MI_STAT>1 - mi_stat_print(&stats->malloc, "malloc req", 1, out, arg); - _mi_fprintf(out, arg, "\n"); - #endif - mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, ""); - mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, ""); - mi_stat_peak_print(&stats->reset, "reset", 1, out, arg ); - mi_stat_peak_print(&stats->purged, "purged", 1, out, arg ); - mi_stat_print(&stats->page_committed, "touched", 1, out, arg); - mi_stat_print(&stats->segments, "segments", -1, out, arg); - mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg); - mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg); - mi_stat_print(&stats->pages, "pages", -1, out, arg); - mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg); - mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg); - mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg); - mi_stat_counter_print(&stats->arena_count, "arenas", out, arg); - mi_stat_counter_print(&stats->arena_crossover_count, "-crossover", out, arg); - mi_stat_counter_print(&stats->arena_rollback_count, "-rollback", out, arg); - mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg); - mi_stat_counter_print(&stats->commit_calls, "commits", out, arg); - mi_stat_counter_print(&stats->reset_calls, "resets", out, arg); - mi_stat_counter_print(&stats->purge_calls, "purges", out, arg); - mi_stat_print(&stats->threads, "threads", -1, out, arg); - mi_stat_counter_print_avg(&stats->searches, "searches", out, arg); - _mi_fprintf(out, arg, "%10s: %5zu\n", "numa nodes", _mi_os_numa_node_count()); - +mi_decl_export void mi_process_info_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept +{ size_t elapsed; size_t user_time; size_t sys_time; @@ -358,58 +331,188 @@ static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) size_t peak_commit; size_t page_faults; mi_process_info(&elapsed, &user_time, &sys_time, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults); - _mi_fprintf(out, arg, "%10s: %5ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000); - _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, rss: ", "process", - user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults ); + _mi_fprintf(out, arg, " %-10s: %5zu.%03zu s\n", "elapsed", elapsed/1000, elapsed%1000); + _mi_fprintf(out, arg, " %-10s: user: %zu.%03zu s, system: %zu.%03zu s, faults: %zu, peak rss: ", "process", + user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, page_faults); mi_printf_amount((int64_t)peak_rss, 1, out, arg, "%s"); if (peak_commit > 0) { - _mi_fprintf(out, arg, ", commit: "); + _mi_fprintf(out, arg, ", peak commit: "); mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s"); } _mi_fprintf(out, arg, "\n"); } +void _mi_stats_print(const char* name, size_t id, const mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept { + // wrap the output function to be line buffered + char buf[256]; _mi_memzero_var(buf); + buffered_t buffer = { out0, arg0, NULL, 0, 255 }; + buffer.buf = buf; + mi_output_fun* out = &mi_buffered_out; + void* arg = &buffer; + + // and print using that + _mi_fprintf(out, arg, "%s %zu\n", name, id); + + if (stats->malloc_normal.total + stats->malloc_huge.total != 0) { + #if MI_STAT>1 + mi_print_header("blocks", out, arg); + mi_stats_print_bins(stats->malloc_bins, MI_BIN_HUGE, out, arg); + #endif + #if MI_STAT + mi_stat_print(&stats->malloc_normal, "binned", (stats->malloc_normal_count.total == 0 ? -1 : 1), out, arg); + mi_stat_print(&stats->malloc_huge, "huge", (stats->malloc_huge_count.total == 0 ? -1 : 1), out, arg); + mi_stat_count_t total = { 0,0,0 }; + mi_stat_count_add_mt(&total, &stats->malloc_normal); + mi_stat_count_add_mt(&total, &stats->malloc_huge); + mi_stat_print_ex(&total, "total", 1, out, arg, ""); + #if MI_STAT>1 + mi_stat_total_print(&stats->malloc_requested, "malloc req", 1, out, arg); + #endif + _mi_fprintf(out, arg, "\n"); + #endif + } + + if (stats->pages.total != 0) { + mi_print_header("pages", out, arg); + mi_stat_print_ex(&stats->page_committed, "touched", 1, out, arg, ""); + // mi_stat_print(&stats->segments, "segments", -1, out, arg); + // mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg); + // mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg); + mi_stat_print(&stats->pages, "pages", 0, out, arg); + mi_stat_print(&stats->pages_abandoned, "abandoned", 0, out, arg); + mi_stat_counter_print(&stats->pages_reclaim_on_alloc, "reclaima", out, arg); + mi_stat_counter_print(&stats->pages_reclaim_on_free, "reclaimf", out, arg); + mi_stat_counter_print(&stats->pages_reabandon_full, "reabandon", out, arg); + mi_stat_counter_print(&stats->pages_unabandon_busy_wait, "waits", out, arg); + mi_stat_counter_print(&stats->pages_extended, "extended", out, arg); + mi_stat_counter_print(&stats->pages_retire, "retire", out, arg); + mi_stat_average_print(stats->page_searches_count.total, stats->page_searches.total, "searches", out, arg); + _mi_fprintf(out, arg, "\n"); + } + + if (stats->arena_count.total > 0) { + mi_print_header("arenas", out, arg); + mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, ""); + mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, ""); + mi_stat_counter_print_size(&stats->reset, "reset", out, arg); + mi_stat_counter_print_size(&stats->purged, "purged", out, arg); + + mi_stat_counter_print(&stats->arena_count, "arenas", out, arg); + mi_stat_counter_print(&stats->arena_rollback_count, "rollback", out, arg); + mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg); + mi_stat_counter_print(&stats->commit_calls, "commits", out, arg); + mi_stat_counter_print(&stats->reset_calls, "resets", out, arg); + mi_stat_counter_print(&stats->purge_calls, "purges", out, arg); + mi_stat_counter_print(&stats->malloc_guarded_count, "guarded", out, arg); + mi_stat_print_ex(&stats->theaps, "theaps", 0, out, arg, ""); + mi_stat_print_ex(&stats->heaps, "heaps", 0, out, arg, ""); + mi_stat_counter_print(&stats->heaps_delete_wait, "heap waits", out, arg); + _mi_fprintf(out, arg, "\n"); + + mi_print_header("process", out, arg); + mi_stat_print_ex(&stats->threads, "threads", 0, out, arg, ""); + _mi_fprintf(out, arg, " %-10s: %5i\n", "numa nodes", _mi_os_numa_node_count()); + mi_process_info_print_out(out, arg); + } + _mi_fprintf(out, arg, "\n"); +} + + static mi_msecs_t mi_process_start; // = 0 -static mi_stats_t* mi_stats_get_default(void) { - mi_heap_t* heap = mi_heap_get_default(); - return &heap->tld->stats; +// called on process init +void _mi_stats_init(void) { + if (mi_process_start == 0) { mi_process_start = _mi_clock_start(); }; } -static void mi_stats_merge_from(mi_stats_t* stats) { - if (stats != &_mi_stats_main) { - mi_stats_add(&_mi_stats_main, stats); - memset(stats, 0, sizeof(mi_stats_t)); - } +static void mi_stats_add_into(mi_stats_t* to, const mi_stats_t* from) { + mi_assert_internal(to != NULL && from != NULL); + if (to == from) return; + mi_stats_add(to, from); +} + +void _mi_stats_merge_into(mi_stats_t* to, mi_stats_t* from) { + mi_assert_internal(to != NULL && from != NULL); + if (to == from) return; + mi_stats_add(to, from); + _mi_memzero(from, sizeof(mi_stats_t)); +} + +static const mi_stats_t* mi_stats_merge_theap_to_heap(mi_theap_t* theap) mi_attr_noexcept { + mi_stats_t* stats = &theap->stats; + mi_stats_t* heap_stats = &_mi_theap_heap(theap)->stats; + _mi_stats_merge_into( heap_stats, stats ); + return heap_stats; +} + +static const mi_stats_t* mi_heap_get_stats(mi_heap_t* heap) { + if (heap==NULL) { heap = mi_heap_main(); } + mi_theap_t* theap = _mi_heap_theap_peek(heap); + if (theap==NULL) return &heap->stats; + else return mi_stats_merge_theap_to_heap(theap); } +// deprecated void mi_stats_reset(void) mi_attr_noexcept { - mi_stats_t* stats = mi_stats_get_default(); - if (stats != &_mi_stats_main) { memset(stats, 0, sizeof(mi_stats_t)); } - memset(&_mi_stats_main, 0, sizeof(mi_stats_t)); - if (mi_process_start == 0) { mi_process_start = _mi_clock_start(); }; + if (!mi_theap_is_initialized(_mi_theap_default())) return; + mi_heap_get_stats(mi_heap_main()); + mi_heap_stats_merge_to_subproc(mi_heap_main()); +} + + +void mi_heap_stats_print_out(mi_heap_t* heap, mi_output_fun* out, void* arg) mi_attr_noexcept { + if (heap==NULL) { heap = mi_heap_main(); } + _mi_stats_print("heap", heap->heap_seq, mi_heap_get_stats(heap), out, arg); } -void mi_stats_merge(void) mi_attr_noexcept { - mi_stats_merge_from( mi_stats_get_default() ); +typedef struct mi_heap_print_visit_info_s { + mi_output_fun* out; + void* out_arg; +} mi_heap_print_visit_info_t; + +static bool mi_cdecl mi_heap_print_visitor(mi_heap_t* heap, void* arg) { + mi_heap_print_visit_info_t* vinfo = (mi_heap_print_visit_info_t*)(arg); + mi_heap_stats_print_out(heap, vinfo->out, vinfo->out_arg); + return true; } -void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done` - mi_stats_merge_from(stats); + +// show each heap and then the subproc +void mi_subproc_heap_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg) mi_attr_noexcept { + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc==NULL) return; + mi_heap_print_visit_info_t vinfo = { out, arg }; + mi_subproc_visit_heaps(subproc_id, &mi_heap_print_visitor, &vinfo); + _mi_stats_print("subproc", subproc->subproc_seq, &subproc->stats, out, arg); +} + + +// aggregate all stats from the heaps and subproc and print those +void mi_subproc_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg) mi_attr_noexcept { + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc==NULL) return; + mi_stats_t_decl(stats); + if (mi_subproc_stats_get(subproc_id, &stats)) { + _mi_stats_print("subproc", subproc->subproc_seq, &stats, out, arg); + } } void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept { - mi_stats_merge_from(mi_stats_get_default()); - _mi_stats_print(&_mi_stats_main, out, arg); + mi_subproc_stats_print_out(mi_subproc_current(),out, arg); } +// deprecated void mi_stats_print(void* out) mi_attr_noexcept { // for compatibility there is an `out` parameter (which can be `stdout` or `stderr`) mi_stats_print_out((mi_output_fun*)out, NULL); } +// deprecated void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept { - _mi_stats_print(mi_stats_get_default(), out, arg); + mi_theap_t* theap = _mi_theap_default(); + if (theap==NULL || !mi_theap_is_initialized(theap)) return; + _mi_stats_print("heap", _mi_theap_heap(theap)->heap_seq, &theap->stats, out, arg); + mi_stats_merge_theap_to_heap(_mi_theap_default()); } @@ -446,8 +549,12 @@ mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, s mi_process_info_t pinfo; _mi_memzero_var(pinfo); pinfo.elapsed = _mi_clock_end(mi_process_start); - pinfo.current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current)); - pinfo.peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak)); + { const mi_subproc_t* subproc = _mi_subproc_main(); + if (subproc!=NULL) { + pinfo.current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&subproc->stats.committed.current))); + pinfo.peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&subproc->stats.committed.peak))); + } + } pinfo.current_rss = pinfo.current_commit; pinfo.peak_rss = pinfo.peak_commit; pinfo.utime = 0; @@ -465,3 +572,245 @@ mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, s if (peak_commit!=NULL) *peak_commit = pinfo.peak_commit; if (page_faults!=NULL) *page_faults = pinfo.page_faults; } + +mi_decl_export void mi_process_info_print(void) mi_attr_noexcept { + mi_process_info_print_out(NULL, NULL); +} + + +// -------------------------------------------------------- +// Return statistics +// -------------------------------------------------------- + +size_t mi_stats_get_bin_size(size_t bin) mi_attr_noexcept { + if (bin > MI_BIN_HUGE) return 0; + return _mi_bin_size(bin); +} + +static bool mi_stats_copy(mi_stats_t* stats_to, const mi_stats_t* stats_from) mi_attr_noexcept { + if (stats_to == NULL || stats_to->size != sizeof(mi_stats_t) || stats_to->version != MI_STAT_VERSION) return false; + if (stats_from == NULL || stats_from->size != stats_to->size) return false; + _mi_memcpy(stats_to, stats_from, stats_to->size); + return true; +} + +bool mi_subproc_stats_get_exclusive(mi_subproc_id_t subproc_id, mi_stats_t* stats) mi_attr_noexcept { + const mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc==NULL) return false; + return mi_stats_copy(stats,&subproc->stats); +} + +bool mi_heap_stats_get(mi_heap_t* heap, mi_stats_t* stats) mi_attr_noexcept { + return mi_stats_copy(stats, mi_heap_get_stats(heap)); +} + + +static bool mi_cdecl mi_heap_aggregate_visitor(mi_heap_t* heap, void* arg) { + mi_stats_t* stats = (mi_stats_t*)arg; + mi_stats_add_into(stats, mi_heap_get_stats(heap)); + return true; +} + +bool mi_subproc_stats_get(mi_subproc_id_t subproc_id, mi_stats_t* stats) mi_attr_noexcept { + if (stats==NULL) return false; + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc == NULL) return false; + if (!mi_stats_copy(stats, &subproc->stats)) return false; + mi_subproc_visit_heaps(subproc_id, &mi_heap_aggregate_visitor, stats); + return true; +} + +bool mi_stats_get(mi_stats_t* stats) mi_attr_noexcept { + return mi_subproc_stats_get(mi_subproc_current(), stats); +} + + +// -------------------------------------------------------- +// Statics in json format +// -------------------------------------------------------- + +typedef struct mi_json_buf_s { + char* buf; + size_t size; + size_t used; + bool can_realloc; +} mi_json_buf_t; + +static bool mi_json_buf_expand(mi_json_buf_t* hbuf) { + if (hbuf==NULL) return false; + if (hbuf->buf != NULL && hbuf->size>0) { + hbuf->buf[hbuf->size-1] = 0; + } + if (hbuf->size > SIZE_MAX/2 || !hbuf->can_realloc) return false; + const size_t newsize = (hbuf->size == 0 ? mi_good_size(12*MI_KiB) : 2*hbuf->size); + char* const newbuf = (char*)mi_rezalloc(hbuf->buf, newsize); + if (newbuf == NULL) return false; + hbuf->buf = newbuf; + hbuf->size = newsize; + return true; +} + +static void mi_json_buf_print(mi_json_buf_t* hbuf, const char* msg) { + if (msg==NULL || hbuf==NULL) return; + if (hbuf->used + 1 >= hbuf->size && !hbuf->can_realloc) return; + for (const char* src = msg; *src != 0; src++) { + char c = *src; + if (hbuf->used + 1 >= hbuf->size) { + if (!mi_json_buf_expand(hbuf)) return; + } + mi_assert_internal(hbuf->used < hbuf->size); + hbuf->buf[hbuf->used++] = c; + } + mi_assert_internal(hbuf->used < hbuf->size); + hbuf->buf[hbuf->used] = 0; +} + +static void mi_json_buf_print_count_bin(mi_json_buf_t* hbuf, const char* prefix, const mi_stat_count_t* stat, size_t bin, bool add_comma) { + const size_t binsize = mi_stats_get_bin_size(bin); + const size_t pagesize = (binsize <= MI_SMALL_MAX_OBJ_SIZE ? MI_SMALL_PAGE_SIZE : + (binsize <= MI_MEDIUM_MAX_OBJ_SIZE ? MI_MEDIUM_PAGE_SIZE : + (binsize <= MI_LARGE_MAX_OBJ_SIZE ? MI_LARGE_PAGE_SIZE : 0))); + char buf[128]; + _mi_snprintf(buf, 128, "%s{ \"total\": %lld, \"peak\": %lld, \"current\": %lld, \"block_size\": %zu, \"page_size\": %zu }%s\n", prefix, stat->total, stat->peak, stat->current, binsize, pagesize, (add_comma ? "," : "")); + buf[127] = 0; + mi_json_buf_print(hbuf, buf); +} + +static void mi_json_buf_print_count_cbin(mi_json_buf_t* hbuf, const char* prefix, const mi_stat_count_t* stat, mi_chunkbin_t bin, bool add_comma) { + const char* cbin = " "; + switch(bin) { + case MI_CBIN_SMALL: cbin = "S"; break; + case MI_CBIN_MEDIUM: cbin = "M"; break; + case MI_CBIN_LARGE: cbin = "L"; break; + case MI_CBIN_HUGE: cbin = "H"; break; + case MI_CBIN_OTHER: cbin = "X"; break; + default: cbin = " "; break; + } + char buf[128]; + _mi_snprintf(buf, 128, "%s{ \"total\": %lld, \"peak\": %lld, \"current\": %lld, \"bin\": \"%s\" }%s\n", prefix, stat->total, stat->peak, stat->current, cbin, (add_comma ? "," : "")); + buf[127] = 0; + mi_json_buf_print(hbuf, buf); +} + +static void mi_json_buf_print_count(mi_json_buf_t* hbuf, const char* prefix, const mi_stat_count_t* stat, bool add_comma) { + char buf[128]; + _mi_snprintf(buf, 128, "%s{ \"total\": %lld, \"peak\": %lld, \"current\": %lld }%s\n", prefix, stat->total, stat->peak, stat->current, (add_comma ? "," : "")); + buf[127] = 0; + mi_json_buf_print(hbuf, buf); +} + +static void mi_json_buf_print_count_value(mi_json_buf_t* hbuf, const char* name, const mi_stat_count_t* stat) { + char buf[128]; + _mi_snprintf(buf, 128, " \"%s\": ", name); + buf[127] = 0; + mi_json_buf_print(hbuf, buf); + mi_json_buf_print_count(hbuf, "", stat, true); +} + +static void mi_json_buf_print_value(mi_json_buf_t* hbuf, const char* name, int64_t val) { + char buf[128]; + _mi_snprintf(buf, 128, " \"%s\": %lld,\n", name, val); + buf[127] = 0; + mi_json_buf_print(hbuf, buf); +} + +static void mi_json_buf_print_size(mi_json_buf_t* hbuf, const char* name, size_t val, bool add_comma) { + char buf[128]; + _mi_snprintf(buf, 128, " \"%s\": %zu%s\n", name, val, (add_comma ? "," : "")); + buf[127] = 0; + mi_json_buf_print(hbuf, buf); +} + +static void mi_json_buf_print_counter_value(mi_json_buf_t* hbuf, const char* name, const mi_stat_counter_t* stat) { + mi_json_buf_print_value(hbuf, name, stat->total); +} + +#define MI_STAT_COUNT(stat) mi_json_buf_print_count_value(&hbuf, #stat, &stats->stat); +#define MI_STAT_COUNTER(stat) mi_json_buf_print_counter_value(&hbuf, #stat, &stats->stat); + +static char* mi_stats_get_json_from(const mi_stats_t* stats, size_t output_size, char* output_buf) mi_attr_noexcept { + if (stats==NULL || stats->size!=sizeof(mi_stats_t) || stats->version!=MI_STAT_VERSION) return NULL; + mi_json_buf_t hbuf = { NULL, 0, 0, true }; + if (output_size > 0 && output_buf != NULL) { + _mi_memzero(output_buf, output_size); + hbuf.buf = output_buf; + hbuf.size = output_size; + hbuf.can_realloc = false; + } + else { + if (!mi_json_buf_expand(&hbuf)) return NULL; + } + mi_json_buf_print(&hbuf, "{\n"); + mi_json_buf_print_value(&hbuf, "stat_version", MI_STAT_VERSION); + mi_json_buf_print_value(&hbuf, "mimalloc_version", MI_MALLOC_VERSION); + + // process info + mi_json_buf_print(&hbuf, " \"process\": {\n"); + size_t elapsed; + size_t user_time; + size_t sys_time; + size_t current_rss; + size_t peak_rss; + size_t current_commit; + size_t peak_commit; + size_t page_faults; + mi_process_info(&elapsed, &user_time, &sys_time, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults); + mi_json_buf_print_size(&hbuf, "elapsed_msecs", elapsed, true); + mi_json_buf_print_size(&hbuf, "user_msecs", user_time, true); + mi_json_buf_print_size(&hbuf, "system_msecs", sys_time, true); + mi_json_buf_print_size(&hbuf, "page_faults", page_faults, true); + mi_json_buf_print_size(&hbuf, "rss_current", current_rss, true); + mi_json_buf_print_size(&hbuf, "rss_peak", peak_rss, true); + mi_json_buf_print_size(&hbuf, "commit_current", current_commit, true); + mi_json_buf_print_size(&hbuf, "commit_peak", peak_commit, false); + mi_json_buf_print(&hbuf, " },\n"); + + // statistics + MI_STAT_FIELDS() + + // size bins + mi_json_buf_print(&hbuf, " \"malloc_bins\": [\n"); + for (size_t i = 0; i <= MI_BIN_HUGE; i++) { + mi_json_buf_print_count_bin(&hbuf, " ", &stats->malloc_bins[i], i, i!=MI_BIN_HUGE); + } + mi_json_buf_print(&hbuf, " ],\n"); + mi_json_buf_print(&hbuf, " \"page_bins\": [\n"); + for (size_t i = 0; i <= MI_BIN_HUGE; i++) { + mi_json_buf_print_count_bin(&hbuf, " ", &stats->page_bins[i], i, i!=MI_BIN_HUGE); + } + mi_json_buf_print(&hbuf, " ],\n"); + mi_json_buf_print(&hbuf, " \"chunk_bins\": [\n"); + for (size_t i = 0; i < MI_CBIN_COUNT; i++) { + mi_json_buf_print_count_cbin(&hbuf, " ", &stats->chunk_bins[i], (mi_chunkbin_t)i, i!=MI_CBIN_COUNT-1); + } + mi_json_buf_print(&hbuf, " ]\n"); + mi_json_buf_print(&hbuf, "}\n"); + if (hbuf.used >= hbuf.size) { + // failed + if (hbuf.can_realloc) { mi_free(hbuf.buf); } + return NULL; + } + else { + return hbuf.buf; + } +} + +char* mi_subproc_stats_get_json(mi_subproc_id_t subproc_id, size_t buf_size, char* buf) mi_attr_noexcept { + mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id); + if (subproc==NULL) return NULL; + mi_stats_t_decl(stats); + if (!mi_subproc_stats_get(subproc_id,&stats)) return NULL; + return mi_stats_get_json_from(&stats, buf_size, buf); +} + +char* mi_heap_stats_get_json(mi_heap_t* heap, size_t buf_size, char* buf) mi_attr_noexcept { + return mi_stats_get_json_from(mi_heap_get_stats(heap), buf_size, buf); +} + +char* mi_stats_get_json(size_t buf_size, char* buf) mi_attr_noexcept { + return mi_subproc_stats_get_json(mi_subproc_current(), buf_size, buf); +} + +char* mi_stats_as_json(mi_stats_t* stats, size_t buf_size, char* buf) mi_attr_noexcept { + return mi_stats_get_json_from(stats, buf_size, buf); +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/theap.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/theap.c new file mode 100644 index 00000000..8d5f0fb0 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/theap.c @@ -0,0 +1,743 @@ +/*---------------------------------------------------------------------------- +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "mimalloc/prim.h" // _mi_theap_default + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#pragma warning(disable:4204) // non-constant aggregate initializer +#endif + +/* ----------------------------------------------------------- + Helpers +----------------------------------------------------------- */ + +// return `true` if ok, `false` to break +typedef bool (theap_page_visitor_fun)(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2); + +// Visit all pages in a theap; returns `false` if break was called. +static bool mi_theap_visit_pages(mi_theap_t* theap, theap_page_visitor_fun* fn, bool include_full, void* arg1, void* arg2) +{ + if (theap==NULL || theap->page_count==0) return 0; + + // visit all pages + #if MI_DEBUG>1 + size_t total = theap->page_count; + size_t count = 0; + #endif + + const size_t max_bin = (include_full ? MI_BIN_FULL : MI_BIN_FULL - 1); + for (size_t i = 0; i <= max_bin; i++) { + mi_page_queue_t* pq = &theap->pages[i]; + mi_page_t* page = pq->first; + while(page != NULL) { + mi_page_t* next = page->next; // save next in case the page gets removed from the queue + mi_assert_internal(mi_page_theap(page) == theap); + #if MI_DEBUG>1 + count++; + #endif + if (!fn(theap, pq, page, arg1, arg2)) return false; + page = next; // and continue + } + } + mi_assert_internal(!include_full || count == total); + return true; +} + + +#if MI_DEBUG>=2 +static bool mi_theap_page_is_valid(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { + MI_UNUSED(arg1); + MI_UNUSED(arg2); + MI_UNUSED(pq); + mi_assert_internal(mi_page_theap(page) == theap); + mi_assert_expensive(_mi_page_is_valid(page)); + return true; +} +#endif +#if MI_DEBUG>=3 +static bool mi_theap_is_valid(mi_theap_t* theap) { + mi_assert_internal(theap!=NULL); + mi_theap_visit_pages(theap, &mi_theap_page_is_valid, true, NULL, NULL); + for (size_t bin = 0; bin < MI_BIN_COUNT; bin++) { + mi_assert_internal(_mi_page_queue_is_valid(theap, &theap->pages[bin])); + } + return true; +} +#endif + + + + +/* ----------------------------------------------------------- + "Collect" pages by migrating `local_free` and `thread_free` + lists and freeing empty pages. This is done when a thread + stops (and in that case abandons pages if there are still + blocks alive) +----------------------------------------------------------- */ + +typedef enum mi_collect_e { + MI_NORMAL, + MI_FORCE, + MI_ABANDON +} mi_collect_t; + + +static bool mi_theap_page_collect(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_t* page, void* arg_collect, void* arg2 ) { + MI_UNUSED(arg2); + MI_UNUSED(theap); + mi_assert_internal(mi_theap_page_is_valid(theap, pq, page, NULL, NULL)); + mi_collect_t collect = *((mi_collect_t*)arg_collect); + _mi_page_free_collect(page, collect >= MI_FORCE); + if (mi_page_all_free(page)) { + // no more used blocks, possibly free the page. + if (collect >= MI_FORCE || page->retire_expire == 0) { // either forced/abandon, or not already retired + // note: this will potentially free retired pages as well. + _mi_page_free(page, pq); + } + } + else if (collect == MI_ABANDON) { + // still used blocks but the thread is done; abandon the page + _mi_page_abandon(page, pq); + } + return true; // don't break +} + +static void mi_theap_merge_stats(mi_theap_t* theap) { + mi_assert_internal(mi_theap_is_initialized(theap)); + mi_heap_t* const heap = _mi_theap_heap(theap); + _mi_stats_merge_into(&heap->stats, &theap->stats); +} + +static void mi_theap_collect_ex(mi_theap_t* theap, mi_collect_t collect) +{ + if (theap==NULL || !mi_theap_is_initialized(theap)) return; + mi_assert_expensive(mi_theap_is_valid(theap)); + + const bool force = (collect >= MI_FORCE); + _mi_deferred_free(theap, force); + + // python/cpython#112532: we may be called from a thread that is not the owner of the theap + // const bool is_main_thread = (_mi_is_main_thread() && theap->thread_id == _mi_thread_id()); + + // collect retired pages + _mi_theap_collect_retired(theap, force); + + // collect all pages owned by this thread + mi_theap_visit_pages(theap, &mi_theap_page_collect, (collect!=MI_NORMAL), &collect, NULL); // dont normally visit full pages, see issue #1220 + + // collect arenas (this is program wide so don't force purges on abandonment of threads) + //mi_atomic_storei64_release(&theap->tld->subproc->purge_expire, 1); + _mi_arenas_collect(collect == MI_FORCE /* force purge? */, collect >= MI_FORCE /* visit all? */, theap->tld); + + // merge statistics + mi_theap_merge_stats(theap); +} + +void _mi_theap_collect_abandon(mi_theap_t* theap) { + mi_theap_collect_ex(theap, MI_ABANDON); +} + +void mi_theap_collect(mi_theap_t* theap, bool force) mi_attr_noexcept { + mi_theap_collect_ex(theap, (force ? MI_FORCE : MI_NORMAL)); +} + +void mi_collect(bool force) mi_attr_noexcept { + // cannot really collect process wide, just a theap.. + mi_theap_collect(_mi_theap_default(), force); +} + +void mi_heap_collect(mi_heap_t* heap, bool force) { + // cannot really collect a heap, just a theap.. + mi_theap_collect(mi_heap_theap(heap), force); +} + +/* ----------------------------------------------------------- + Heap new +----------------------------------------------------------- */ + +mi_theap_t* mi_theap_get_default(void) { + mi_theap_t* theap = _mi_theap_default(); + if mi_unlikely(!mi_theap_is_initialized(theap)) { + mi_thread_init(); + theap = _mi_theap_default(); + mi_assert_internal(mi_theap_is_initialized(theap)); + } + return theap; +} + +mi_theap_t* mi_theap_set_default(mi_theap_t* theap) { + mi_theap_t* const previous = mi_theap_get_default(); + if (mi_theap_is_initialized(theap)) { + _mi_theap_default_set(theap); + } + return previous; +} + +// todo: make order of parameters consistent (but would that break compat with CPython?) +void _mi_theap_init(mi_theap_t* theap, mi_heap_t* heap, mi_tld_t* tld) +{ + mi_assert_internal(theap!=NULL); + mi_assert_internal(heap!=NULL); + mi_assert_internal(tld!=NULL); + mi_memid_t memid = theap->memid; + _mi_memcpy_aligned(theap, &_mi_theap_empty, sizeof(mi_theap_t)); + theap->memid = memid; + theap->tld = tld; // avoid reading the thread-local tld during initialization + mi_atomic_store_release(&theap->refcount,1); + mi_atomic_store_release(&theap->freed,0); + mi_atomic_store_ptr_relaxed(mi_heap_t,&theap->heap,heap); + mi_assert_internal(theap->stats.size == sizeof(mi_stats_t)); + + _mi_theap_options_init(theap); + if (theap->tld->is_in_threadpool) { + // if we run as part of a thread pool it is better to not arbitrarily reclaim abandoned pages into our theap. + // this is checked in `free.c:mi_free_try_collect_mt` + // .. but abandoning is good in this case: halve the full page retain (possibly to 0) + // (so blocked threads do not hold on to too much memory) + if (theap->page_full_retain > 0) { + theap->page_full_retain = theap->page_full_retain / 4; + } + } + + // push on the thread local theaps list + mi_theap_t* head = NULL; + mi_random_ctx_t head_random; + mi_lock(&theap->tld->theaps_lock) { + head = theap->tld->theaps; + theap->tprev = NULL; + theap->tnext = head; + theap->tld->theaps = theap; + if (head!=NULL) { + head->tprev = theap; + head_random = head->random; + } + } + + // initialize random if heap==NULL + if (head == NULL) { // first theap in this thread? + #if defined(_WIN32) && !defined(MI_SHARED_LIB) + _mi_random_init_weak(&theap->random); // prevent allocation failure during bcrypt dll initialization with static linking (issue #1185) + #else + _mi_random_init(&theap->random); + #endif + } + else { + _mi_random_split(&head_random, &theap->random); // &theap->random is used as nonce so it is ok if threads capture the same head->random + } + theap->cookie = _mi_theap_random_next(theap) | 1; + _mi_theap_guarded_init(theap); + mi_subproc_stat_increase(_mi_subproc(),theaps,1); + + // push on the heap's theap list + mi_lock(&heap->theaps_lock) { + head = heap->theaps; + theap->hprev = NULL; + theap->hnext = head; + if (head!=NULL) { head->hprev = theap; } + heap->theaps = theap; + } +} + +mi_theap_t* _mi_theap_create(mi_heap_t* heap, mi_tld_t* tld) { + mi_assert_internal(tld!=NULL); + mi_assert_internal(heap!=NULL); + // allocate and initialize a theap + mi_memid_t memid; + mi_theap_t* theap; + //if (!_mi_is_heap_main(heap)) { + // theap = (mi_theap_t*)mi_heap_zalloc(mi_heap_main(),sizeof(mi_theap_t)); + // memid = _mi_memid_create(MI_MEM_HEAP_MAIN); + // memid.initially_zero = memid.initially_committed = true; + //} + //else + if (heap->exclusive_arena == NULL) { + theap = (mi_theap_t*)_mi_meta_zalloc(sizeof(mi_theap_t), &memid); + } + else { + // theaps associated with a specific arena are allocated in that arena + // note: takes up at least one slice which is quite wasteful... + const size_t size = _mi_align_up(sizeof(mi_theap_t),MI_ARENA_MIN_OBJ_SIZE); + theap = (mi_theap_t*)_mi_arenas_alloc(heap, size, true, true, heap->exclusive_arena, tld->thread_seq, tld->numa_node, &memid); + } + if (theap==NULL) { + _mi_error_message(ENOMEM, "unable to allocate theap meta-data\n"); + return NULL; + } + theap->memid = memid; + _mi_theap_init(theap, heap, tld); + return theap; +} + +uintptr_t _mi_theap_random_next(mi_theap_t* theap) { + return _mi_random_next(&theap->random); +} + +static void mi_theap_free_mem(mi_theap_t* theap) { + if (theap!=NULL) { + mi_subproc_stat_decrease(_mi_subproc(),theaps,1); + // free the used memory + if (theap->memid.memkind == MI_MEM_HEAP_MAIN) { // note: for now unused as it would access theap_default stats in mi_free of the current theap + mi_assert_internal(_mi_is_heap_main(mi_heap_of(theap))); + mi_free(theap); + } + else if (theap->memid.memkind == MI_MEM_META) { + _mi_meta_free(theap, sizeof(*theap), theap->memid); + } + else { + _mi_arenas_free(theap, _mi_align_up(sizeof(*theap),MI_ARENA_MIN_OBJ_SIZE), theap->memid ); // issue #1168, avoid assertion failure + } + } +} + +// we need to reference count theaps due to the _mi_theap_cached thread locals +void _mi_theap_incref(mi_theap_t* theap) { + if (theap!=NULL && theap->memid.memkind > MI_MEM_STATIC) { + mi_atomic_increment_acq_rel(&theap->refcount); + } +} + +void _mi_theap_decref(mi_theap_t* theap) { + if (theap!=NULL && !mi_memid_needs_no_free(theap->memid)) { + if (mi_atomic_decrement_acq_rel(&theap->refcount) == 1) { + mi_theap_free_mem(theap); + } + } +} + + +// called from `mi_theap_delete` to free the internal theap resources. +bool _mi_theap_free(mi_theap_t* theap, bool acquire_heap_theaps_lock, bool acquire_tld_theaps_lock) { + mi_assert(theap != NULL); + if (theap==NULL) return true; + + // ensure only one thread actually frees the theap + const size_t freed = mi_atomic_exchange_acq_rel( &theap->freed, 1 ); + if (freed!=0) { + // concurrent interaction, retry in an outer loop (as the other thread may be blocked on our lock) + return false; + } + else { + // merge stats to the owning heap + mi_heap_t* const heap = _mi_theap_heap(theap); + _mi_stats_merge_into(&heap->stats, &theap->stats); + + // remove ourselves from the heap theaps list + mi_lock_maybe(&heap->theaps_lock, acquire_heap_theaps_lock) { + if (theap->hnext != NULL) { theap->hnext->hprev = theap->hprev; } + if (theap->hprev != NULL) { theap->hprev->hnext = theap->hnext; } + else { mi_assert_internal(heap->theaps == theap); heap->theaps = theap->hnext; } + theap->hnext = theap->hprev = NULL; + } + + // remove ourselves from the thread local theaps list + mi_lock_maybe(&theap->tld->theaps_lock, acquire_tld_theaps_lock) { + if (theap->tnext != NULL) { theap->tnext->tprev = theap->tprev; } + if (theap->tprev != NULL) { theap->tprev->tnext = theap->tnext; } + else { mi_assert_internal(theap->tld->theaps == theap); theap->tld->theaps = theap->tnext; } + theap->tnext = theap->tprev = NULL; + } + + // Set heap to NULL only after we are removed from the thread local theaps list since + // we may concurrently traverse it to collect (in `init.c:mi_thread_theaps_done`) + // (We need to set it to NULL to avoid an ABA problem where the _mi_theap_cached + // has a heap address that is reused for a newly allocated heap.) + mi_atomic_store_ptr_release(mi_heap_t, &theap->heap, NULL); + theap->tld = NULL; + _mi_theap_decref(theap); + return true; + } +} + + +/* ----------------------------------------------------------- + Heap destroy +----------------------------------------------------------- */ +/* + +// zero out the page queues +static void mi_theap_reset_pages(mi_theap_t* theap) { + mi_assert_internal(theap != NULL); + mi_assert_internal(mi_theap_is_initialized(theap)); + // TODO: copy full empty theap instead? + _mi_memset(&theap->pages_free_direct, 0, sizeof(theap->pages_free_direct)); + _mi_memcpy_aligned(&theap->pages, &_mi_theap_empty.pages, sizeof(theap->pages)); + // theap->thread_delayed_free = NULL; + theap->page_count = 0; +} + +static bool _mi_theap_page_destroy(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { + MI_UNUSED(arg1); + MI_UNUSED(arg2); + MI_UNUSED(pq); + + // ensure no more thread_delayed_free will be added + //_mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false); + + // stats + const size_t bsize = mi_page_block_size(page); + if (bsize > MI_LARGE_MAX_OBJ_SIZE) { + mi_theap_stat_decrease(theap, malloc_huge, bsize); + } + #if (MI_STAT>0) + _mi_page_free_collect(page, false); // update used count + const size_t inuse = page->used; + if (bsize <= MI_LARGE_MAX_OBJ_SIZE) { + mi_theap_stat_decrease(theap, malloc_normal, bsize * inuse); + #if (MI_STAT>1) + mi_theap_stat_decrease(theap, malloc_bins[_mi_bin(bsize)], inuse); + #endif + } + // mi_theap_stat_decrease(theap, malloc_requested, bsize * inuse); // todo: off for aligned blocks... + #endif + + /// pretend it is all free now + mi_assert_internal(mi_page_thread_free(page) == NULL); + page->used = 0; + + // and free the page + // mi_page_free(page,false); + page->next = NULL; + page->prev = NULL; + mi_page_set_theap(page, NULL); + _mi_arenas_page_free(page, theap); + + return true; // keep going +} + +void _mi_theap_destroy_pages(mi_theap_t* theap) { + mi_theap_visit_pages(theap, &_mi_theap_page_destroy, NULL, NULL); + mi_theap_reset_pages(theap); +} + +#if MI_TRACK_HEAP_DESTROY +static bool mi_cdecl mi_theap_track_block_free(const mi_theap_t* theap, const mi_theap_area_t* area, void* block, size_t block_size, void* arg) { + MI_UNUSED(theap); MI_UNUSED(area); MI_UNUSED(arg); MI_UNUSED(block_size); + mi_track_free_size(block,mi_usable_size(block)); + return true; +} +#endif + +void mi_theap_destroy(mi_theap_t* theap) { + mi_assert(theap != NULL); + mi_assert(mi_theap_is_initialized(theap)); + mi_assert(!theap->allow_page_reclaim); + mi_assert(!theap->allow_page_abandon); + mi_assert_expensive(mi_theap_is_valid(theap)); + if (theap==NULL || !mi_theap_is_initialized(theap)) return; + #if MI_GUARDED + // _mi_warning_message("'mi_theap_destroy' called but MI_GUARDED is enabled -- using `mi_theap_delete` instead (theap at %p)\n", theap); + mi_theap_delete(theap); + return; + #else + if (theap->allow_page_reclaim) { + _mi_warning_message("'mi_theap_destroy' called but ignored as the theap was not created with 'allow_destroy' (theap at %p)\n", theap); + // don't free in case it may contain reclaimed pages, + mi_theap_delete(theap); + } + else { + // track all blocks as freed + #if MI_TRACK_HEAP_DESTROY + mi_theap_visit_blocks(theap, true, mi_theap_track_block_free, NULL); + #endif + // free all pages + _mi_theap_destroy_pages(theap); + mi_theap_free(theap,true); + } + #endif +} + +// forcefully destroy all theaps in the current thread +void _mi_theap_unsafe_destroy_all(mi_theap_t* theap) { + mi_assert_internal(theap != NULL); + if (theap == NULL) return; + mi_theap_t* curr = theap->tld->theaps; + while (curr != NULL) { + mi_theap_t* next = curr->next; + if (!curr->allow_page_reclaim) { + mi_theap_destroy(curr); + } + else { + _mi_theap_destroy_pages(curr); + } + curr = next; + } +} +*/ + +/* ----------------------------------------------------------- + Safe Heap delete +----------------------------------------------------------- */ + +// Safe delete a theap without freeing any still allocated blocks in that theap. +void _mi_theap_delete(mi_theap_t* theap, bool acquire_tld_theaps_lock) +{ + mi_assert(theap != NULL); + mi_assert(mi_theap_is_initialized(theap)); + mi_assert_expensive(mi_theap_is_valid(theap)); + if (theap==NULL || !mi_theap_is_initialized(theap)) return; + + // abandon all pages + _mi_theap_collect_abandon(theap); + + mi_assert_internal(theap->page_count==0); + _mi_theap_free(theap, true /* acquire heap->theaps_lock */, acquire_tld_theaps_lock); +} + + + +/* ----------------------------------------------------------- + Load/unload theaps +----------------------------------------------------------- */ +/* +void mi_theap_unload(mi_theap_t* theap) { + mi_assert(mi_theap_is_initialized(theap)); + mi_assert_expensive(mi_theap_is_valid(theap)); + if (theap==NULL || !mi_theap_is_initialized(theap)) return; + if (_mi_theap_heap(theap)->exclusive_arena == NULL) { + _mi_warning_message("cannot unload theaps that are not associated with an exclusive arena\n"); + return; + } + + // abandon all pages so all thread'id in the pages are cleared + _mi_theap_collect_abandon(theap); + mi_assert_internal(theap->page_count==0); + + // remove from theap list + mi_theap_free(theap, false); // but don't actually free the memory + + // disassociate from the current thread-local and static state + theap->tld = NULL; + return; +} + +bool mi_theap_reload(mi_theap_t* theap, mi_arena_id_t arena_id) { + mi_assert(mi_theap_is_initialized(theap)); + if (theap==NULL || !mi_theap_is_initialized(theap)) return false; + if (_mi_theap_heap(theap)->exclusive_arena == NULL) { + _mi_warning_message("cannot reload theaps that were not associated with an exclusive arena\n"); + return false; + } + if (theap->tld != NULL) { + _mi_warning_message("cannot reload theaps that were not unloaded first\n"); + return false; + } + mi_arena_t* arena = _mi_arena_from_id(arena_id); + if (_mi_theap_heap(theap)->exclusive_arena != arena) { + _mi_warning_message("trying to reload a theap at a different arena address: %p vs %p\n", _mi_theap_heap(theap)->exclusive_arena, arena); + return false; + } + + mi_assert_internal(theap->page_count==0); + + // re-associate with the current thread-local and static state + theap->tld = mi_theap_get_default()->tld; + + // reinit direct pages (as we may be in a different process) + mi_assert_internal(theap->page_count == 0); + for (size_t i = 0; i < MI_PAGES_DIRECT; i++) { + theap->pages_free_direct[i] = (mi_page_t*)&_mi_page_empty; + } + + // push on the thread local theaps list + theap->tnext = theap->tld->theaps; + theap->tld->theaps = theap; + return true; +} +*/ + + +/* ----------------------------------------------------------- + Visit all theap blocks and areas + Todo: enable visiting abandoned pages, and + enable visiting all blocks of all theaps across threads +----------------------------------------------------------- */ + +void _mi_heap_area_init(mi_heap_area_t* area, mi_page_t* page) { + const size_t bsize = mi_page_block_size(page); + const size_t ubsize = mi_page_usable_block_size(page); + area->reserved = page->reserved * bsize; + area->committed = page->capacity * bsize; + area->blocks = mi_page_start(page); + area->used = page->used; // number of blocks in use (#553) + area->block_size = ubsize; + area->full_block_size = bsize; + area->reserved1 = page; +} + +static void mi_get_fast_divisor(size_t divisor, uint64_t* magic, size_t* shift) { + mi_assert_internal(divisor > 0 && divisor <= UINT32_MAX); + *shift = MI_SIZE_BITS - mi_clz(divisor - 1); + *magic = ((((uint64_t)1 << 32) * (((uint64_t)1 << *shift) - divisor)) / divisor + 1); +} + +static size_t mi_fast_divide(size_t n, uint64_t magic, size_t shift) { + mi_assert_internal(n <= UINT32_MAX); + const uint64_t hi = ((uint64_t)n * magic) >> 32; + return (size_t)((hi + n) >> shift); +} + +bool _mi_theap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t* page, mi_block_visit_fun* visitor, void* arg) { + mi_assert(area != NULL); + if (area==NULL) return true; + mi_assert(page != NULL); + if (page == NULL) return true; + + _mi_page_free_collect(page,true); // collect both thread_delayed and local_free + mi_assert_internal(page->local_free == NULL); + if (page->used == 0) return true; + + size_t psize; + uint8_t* const pstart = mi_page_area(page, &psize); + mi_heap_t* const heap = mi_page_heap(page); + const size_t bsize = mi_page_block_size(page); + const size_t ubsize = mi_page_usable_block_size(page); // without padding + + // optimize page with one block + if (page->capacity == 1) { + mi_assert_internal(page->used == 1 && page->free == NULL); + return visitor(heap, area, pstart, ubsize, arg); + } + mi_assert(bsize <= UINT32_MAX); + + // optimize full pages + if (page->used == page->capacity) { + uint8_t* block = pstart; + for (size_t i = 0; i < page->capacity; i++) { + if (!visitor(heap, area, block, ubsize, arg)) return false; + block += bsize; + } + return true; + } + + // create a bitmap of free blocks. + #define MI_MAX_BLOCKS (MI_SMALL_PAGE_SIZE / sizeof(void*)) + uintptr_t free_map[MI_MAX_BLOCKS / MI_INTPTR_BITS]; + const uintptr_t bmapsize = _mi_divide_up(page->capacity, MI_INTPTR_BITS); + memset(free_map, 0, bmapsize * sizeof(intptr_t)); + if (page->capacity % MI_INTPTR_BITS != 0) { + // mark left-over bits at the end as free + size_t shift = (page->capacity % MI_INTPTR_BITS); + uintptr_t mask = (UINTPTR_MAX << shift); + free_map[bmapsize - 1] = mask; + } + + // fast repeated division by the block size + uint64_t magic; + size_t shift; + mi_get_fast_divisor(bsize, &magic, &shift); + + #if MI_DEBUG>1 + size_t free_count = 0; + #endif + for (mi_block_t* block = page->free; block != NULL; block = mi_block_next(page, block)) { + #if MI_DEBUG>1 + free_count++; + #endif + mi_assert_internal((uint8_t*)block >= pstart && (uint8_t*)block < (pstart + psize)); + size_t offset = (uint8_t*)block - pstart; + mi_assert_internal(offset % bsize == 0); + mi_assert_internal(offset <= UINT32_MAX); + size_t blockidx = mi_fast_divide(offset, magic, shift); + mi_assert_internal(blockidx == offset / bsize); + mi_assert_internal(blockidx < MI_MAX_BLOCKS); + size_t bitidx = (blockidx / MI_INTPTR_BITS); + size_t bit = blockidx - (bitidx * MI_INTPTR_BITS); + free_map[bitidx] |= ((uintptr_t)1 << bit); + } + mi_assert_internal(page->capacity == (free_count + page->used)); + + // walk through all blocks skipping the free ones + #if MI_DEBUG>1 + size_t used_count = 0; + #endif + uint8_t* block = pstart; + for (size_t i = 0; i < bmapsize; i++) { + if (free_map[i] == 0) { + // every block is in use + for (size_t j = 0; j < MI_INTPTR_BITS; j++) { + #if MI_DEBUG>1 + used_count++; + #endif + if (!visitor(heap, area, block, ubsize, arg)) return false; + block += bsize; + } + } + else { + // visit the used blocks in the mask + uintptr_t m = ~free_map[i]; + while (m != 0) { + #if MI_DEBUG>1 + used_count++; + #endif + size_t bitidx = mi_ctz(m); + if (!visitor(heap, area, block + (bitidx * bsize), ubsize, arg)) return false; + m &= m - 1; // clear least significant bit + } + block += bsize * MI_INTPTR_BITS; + } + } + mi_assert_internal(page->used == used_count); + return true; +} + +bool _mi_page_visit_blocks( mi_page_t* page, mi_block_visit_fun* visitor, void* arg ) { + mi_heap_area_t area; + _mi_heap_area_init(&area, page); + return _mi_theap_area_visit_blocks(&area, page, visitor, arg); +} + + +// Separate struct to keep `mi_page_t` out of the public interface +typedef struct mi_theap_area_ex_s { + mi_heap_area_t area; + mi_page_t* page; +} mi_theap_area_ex_t; + +typedef bool (mi_theap_area_visit_fun)(const mi_theap_t* theap, const mi_theap_area_ex_t* area, void* arg); + +static bool mi_theap_visit_areas_page(mi_theap_t* theap, mi_page_queue_t* pq, mi_page_t* page, void* vfun, void* arg) { + MI_UNUSED(theap); + MI_UNUSED(pq); + mi_theap_area_visit_fun* fun = (mi_theap_area_visit_fun*)vfun; + mi_theap_area_ex_t xarea; + xarea.page = page; + _mi_heap_area_init(&xarea.area, page); + return fun(theap, &xarea, arg); +} + +// Visit all theap pages as areas +static bool mi_theap_visit_areas(const mi_theap_t* theap, mi_theap_area_visit_fun* visitor, void* arg) { + if (visitor == NULL) return false; + return mi_theap_visit_pages((mi_theap_t*)theap, &mi_theap_visit_areas_page, true, (void*)(visitor), arg); // note: function pointer to void* :-{ +} + +// Just to pass arguments +typedef struct mi_visit_blocks_args_s { + bool visit_blocks; + mi_block_visit_fun* visitor; + void* arg; +} mi_visit_blocks_args_t; + +static bool mi_theap_area_visitor(const mi_theap_t* theap, const mi_theap_area_ex_t* xarea, void* arg) { + mi_visit_blocks_args_t* args = (mi_visit_blocks_args_t*)arg; + if (!args->visitor(_mi_theap_heap(theap), &xarea->area, NULL, xarea->area.block_size, args->arg)) return false; + if (args->visit_blocks) { + return _mi_theap_area_visit_blocks(&xarea->area, xarea->page, args->visitor, args->arg); + } + else { + return true; + } +} + +// Visit all blocks in a theap +bool mi_theap_visit_blocks(const mi_theap_t* theap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) { + mi_visit_blocks_args_t args = { visit_blocks, visitor, arg }; + return mi_theap_visit_areas(theap, &mi_theap_area_visitor, &args); +} + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/threadlocal.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/threadlocal.c new file mode 100644 index 00000000..eea3329b --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/src/threadlocal.c @@ -0,0 +1,236 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2019-2026, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +/* ---------------------------------------------------------------------------- +Implement dynamic thread local variables (for heap's). +Unlike most OS native implementations there is no limit on the number +that can be allocated. +-----------------------------------------------------------------------------*/ + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "mimalloc/prim.h" + +/* ----------------------------------------------------------- + Each thread can have (a dynamically expanding) array of + thread-local values. Each slot has a value and a version. + The version is used to safely reuse slots. +----------------------------------------------------------- */ +typedef struct mi_tls_slot_s { + size_t version; + void* value; +} mi_tls_slot_t; + +typedef struct mi_thread_locals_s { + size_t count; + mi_tls_slot_t slots[1]; +} mi_thread_locals_t; + +static mi_thread_locals_t mi_thread_locals_empty = { 0, {{0,NULL}} }; + +mi_decl_thread mi_thread_locals_t* mi_thread_locals = &mi_thread_locals_empty; // always point to a valid `mi_thread_locals_t` + + +/* ----------------------------------------------------------- + Each key consists of the slot index in the lower bits, + and its version it the top bits. When we get a value + the version must match or we return NULL. When we set + a value, we also set the version of the key. +----------------------------------------------------------- */ + +#if MI_SIZE_BITS < 64 +#define MI_TLS_IDX_BITS (MI_SIZE_BITS/2) // half for the index, half for the version +#else +#define MI_TLS_IDX_BITS (MI_SIZE_BITS/4) // 16 bits for the index, 48 bits for the version +#endif +#define MI_TLS_IDX_MASK ((MI_ZU(1)<> MI_TLS_IDX_BITS); +} + +static mi_thread_local_t mi_key_create( size_t index, size_t version ) { + mi_assert_internal(version != 0 && version <= MI_TLS_VERSION_MAX); + mi_assert_internal(index <= MI_TLS_IDX_MAX); + const mi_thread_local_t key = ((version << MI_TLS_IDX_BITS) | index); + mi_assert_internal(key != 0); + return key; +} + + +// dynamically reallocate the thread local slots when needed +static mi_thread_locals_t* mi_thread_locals_expand(size_t least_idx) { + mi_thread_locals_t* tls_old = mi_thread_locals; + const size_t count_old = tls_old->count; + size_t count; + if (count_old==0) { + tls_old = NULL; // so we allocate fresh from mi_thread_locals_empty + count = 16; // start with 16 slots + } + else if (count_old >= 1024) { + count = count_old + 1024; // at some point increase linearly + } + else { + count = 2*count_old; // and double initially + } + if (count <= least_idx) { + count = least_idx + 1; + } + if (count > MI_TLS_IDX_MAX) { return NULL; } // too large + mi_thread_locals_t* tls = (mi_thread_locals_t*)mi_rezalloc(tls_old, sizeof(mi_thread_locals_t) + count*sizeof(mi_tls_slot_t)); + if mi_unlikely(tls==NULL) return NULL; + tls->count = count; + mi_thread_locals = tls; + return tls; +} + +static mi_decl_noinline bool mi_thread_local_set_expand( mi_thread_local_t key, void* val ) { + if (val==NULL) return true; + const size_t idx = mi_key_index(key); + mi_thread_locals_t* tls = mi_thread_locals_expand(idx); + if (tls==NULL) return false; + mi_assert_internal(tls == mi_thread_locals); + mi_assert_internal(idx < tls->count); + tls->slots[idx].value = val; + tls->slots[idx].version = mi_key_version(key); + return true; +} + +// set a tls slot; returns `true` if successful. +// Can return `false` if we could not reallocate the slots array. +bool _mi_thread_local_set( mi_thread_local_t key, void* val ) { + mi_thread_locals_t* tls = mi_thread_locals; + mi_assert_internal(tls!=NULL); + mi_assert_internal(key!=0); + const size_t idx = mi_key_index(key); + if mi_likely(idx < tls->count) { + tls->slots[idx].value = val; + tls->slots[idx].version = mi_key_version(key); + return true; + } + else { + return mi_thread_local_set_expand( key, val ); // tailcall + } +} + +// get a tls slot value +void* _mi_thread_local_get( mi_thread_local_t key ) { + const mi_thread_locals_t* const tls = mi_thread_locals; + mi_assert_internal(tls!=NULL); + mi_assert_internal(key!=0); + const size_t idx = mi_key_index(key); + if mi_likely(idx < tls->count && mi_key_version(key) == tls->slots[idx].version) { + return tls->slots[idx].value; + } + else { + return NULL; + } +} + +void _mi_thread_locals_thread_done(void) { + mi_thread_locals_t* const tls = mi_thread_locals; + if (tls!=NULL && tls->count > 0) { + mi_free(tls); + mi_thread_locals = &mi_thread_locals_empty; + } +} + +/* ----------------------------------------------------------- +Create and free fresh TLS key's +----------------------------------------------------------- */ +#include "bitmap.h" + +static mi_lock_t mi_thread_locals_lock; // we need a lock in order to re-allocate the slot bits +static mi_bitmap_t* mi_thread_locals_free; // reuse an arena bitmap to track which slots were assigned (1=free, 0=in-use) +static size_t mi_thread_locals_version; // version to be able to reuse slots safely + +void _mi_thread_locals_init(void) { + mi_lock_init(&mi_thread_locals_lock); +} + +void _mi_thread_locals_done(void) { + mi_lock(&mi_thread_locals_lock) { + mi_bitmap_t* const slots = mi_thread_locals_free; + mi_free(slots); + } + mi_lock_done(&mi_thread_locals_lock); +} + +// strange signature but allows us to reuse the arena code for claiming free pages +static bool mi_thread_local_claim_fun(size_t _slice_index, mi_arena_t* _arena, bool* keep_set) { + MI_UNUSED(_slice_index); MI_UNUSED(_arena); + *keep_set = false; + return true; +} + +// When we claim a free slot, we increase the global version counter +// (so if we reuse a slot it will be returning NULL initially when a thread tries to get it) +static mi_thread_local_t mi_thread_local_claim(void) { + size_t idx = 0; + if (mi_thread_locals_free != NULL && mi_bitmap_try_find_and_claim(mi_thread_locals_free,0,&idx,&mi_thread_local_claim_fun,NULL)) { + mi_thread_locals_version++; + if (mi_thread_locals_version >= MI_TLS_VERSION_MAX) { mi_thread_locals_version = 1; } /* wrap around the version */ + return mi_key_create( idx, mi_thread_locals_version); + } + else { + return 0; + } +} + +static bool mi_thread_local_create_expand(void) { + mi_bitmap_t* const slots = mi_thread_locals_free; + // 1024 bits at a time + const size_t oldcount = (slots==NULL ? 0 : mi_bitmap_max_bits(slots)); + const size_t newcount = 1024 + oldcount; + if (newcount > MI_TLS_IDX_MAX) { return false; } + const size_t newsize = mi_bitmap_size( newcount, NULL ); + mi_bitmap_t* newslots = (mi_bitmap_t*)mi_zalloc_aligned(newsize, MI_BCHUNK_SIZE); + if (newslots==NULL) { return false; } + if (slots!=NULL) { + // copy over the previous bitmap + _mi_memcpy_aligned(newslots, slots, mi_bitmap_size(oldcount, NULL)); + mi_free(slots); + } + mi_bitmap_init(newslots, newcount, true /* pretend already zero'd so we do not zero out the copied old entries */); + mi_bitmap_unsafe_setN(newslots, oldcount, newcount - oldcount); /* set the new expanded slots as available */ + mi_thread_locals_free = newslots; + return true; +} + + +// create a fresh key +mi_thread_local_t _mi_thread_local_create(void) { + mi_thread_local_t key = 0; + mi_lock(&mi_thread_locals_lock) { + key = mi_thread_local_claim(); + if (key==0) { + if (mi_thread_local_create_expand()) { + key = mi_thread_local_claim(); + } + } + } + return key; +} + +// free a key +void _mi_thread_local_free(mi_thread_local_t key) { + if (key==0) return; + const size_t idx = mi_key_index(key); + mi_lock(&mi_thread_locals_lock) { + mi_bitmap_t* const slots = mi_thread_locals_free; + if (slots!=NULL && idx < mi_bitmap_max_bits(slots)) { + mi_bitmap_set(slots,idx); + } + } +} + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/CMakeLists.txt b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/CMakeLists.txt index e76ffa64..199f4809 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/CMakeLists.txt +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.18) project(mimalloc-test C CXX) set(CMAKE_C_STANDARD 11) @@ -16,10 +16,12 @@ if (NOT CMAKE_BUILD_TYPE) endif() # Import mimalloc (if installed) -find_package(mimalloc 2.0 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH) +find_package(mimalloc CONFIG REQUIRED) message(STATUS "Found mimalloc installed at: ${MIMALLOC_LIBRARY_DIR} (${MIMALLOC_VERSION_DIR})") -# overriding with a dynamic library + +# link with a dynamic shared library +# use `LD_PRELOAD` to actually override malloc/free at runtime with mimalloc add_executable(dynamic-override main-override.c) target_link_libraries(dynamic-override PUBLIC mimalloc) @@ -29,9 +31,9 @@ target_link_libraries(dynamic-override-cxx PUBLIC mimalloc) # overriding with a static object file works reliable as the symbols in the # object file have priority over those in library files -add_executable(static-override-obj main-override.c ${MIMALLOC_OBJECT_DIR}/mimalloc.o) +add_executable(static-override-obj main-override.c ${MIMALLOC_OBJECT_DIR}/mimalloc${CMAKE_C_OUTPUT_EXTENSION}) target_include_directories(static-override-obj PUBLIC ${MIMALLOC_INCLUDE_DIR}) -target_link_libraries(static-override-obj PUBLIC pthread) +target_link_libraries(static-override-obj PUBLIC mimalloc-static) # overriding with a static library works too if using the `mimalloc-override.h` diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-dep.cpp b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-dep.cpp new file mode 100644 index 00000000..772e3702 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-dep.cpp @@ -0,0 +1,60 @@ +// Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc. +// This is imported by the `mimalloc-test-override` project. +#include +#include +#include "main-override-dep.h" + +std::string TestAllocInDll::GetString() +{ + char* test = new char[128]; + memset(test, 0, 128); + const char* t = "test"; + memcpy(test, t, 4); + std::string r = test; + std::cout << "override-dep: GetString: " << r << "\n"; + delete[] test; + return r; +} + +#include + +void TestAllocInDll::TestHeapAlloc() +{ + HANDLE theap = GetProcessHeap(); + int* p = (int*)HeapAlloc(theap, 0, sizeof(int)); + *p = 42; + HeapFree(theap, 0, p); +} + +class Static { +private: + void* p; +public: + Static() { + printf("override-dep: static constructor\n"); + p = malloc(64); + return; + } + ~Static() { + free(p); + printf("override-dep: static destructor\n"); + return; + } +}; + +static Static s = Static(); + + +#include + +BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) { + (void)(reserved); + (void)(module); + if (reason==DLL_PROCESS_ATTACH) { + printf("override-dep: dll attach\n"); + } + else if (reason==DLL_PROCESS_DETACH) { + printf("override-dep: dll detach\n"); + } + return TRUE; +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-dep.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-dep.h new file mode 100644 index 00000000..9d4aabfd --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-dep.h @@ -0,0 +1,12 @@ +#pragma once +// Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc. +// This is imported by the `mimalloc-test-override` project. + +#include + +class TestAllocInDll +{ +public: + __declspec(dllexport) std::string GetString(); + __declspec(dllexport) void TestHeapAlloc(); +}; diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-static.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-static.c index e71be29e..c3230e1a 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-static.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override-static.c @@ -1,3 +1,6 @@ +#if _WIN32 +#include +#endif #include #include #include @@ -7,11 +10,13 @@ #include #include // redefines malloc etc. +static void mi_bins(void); static void double_free1(); static void double_free2(); static void corrupt_free(); static void block_overflow1(); +static void block_overflow2(); static void invalid_free(); static void test_aslr(void); static void test_process_info(void); @@ -19,27 +24,43 @@ static void test_reserved(void); static void negative_stat(void); static void alloc_huge(void); static void test_heap_walk(void); -static void test_heap_arena(void); -static void test_align(void); +static void test_canary_leak(void); +static void test_manage_os_memory(void); +// static void test_large_pages(void); + +#if _WIN32 +#include "main-static-dep.h" +static void test_dep(); // test static mimalloc in a separate DLL +#else +static void test_dep() {}; +#endif + int main() { mi_version(); mi_stats_reset(); - // detect double frees and heap corruption + + test_dep(); + + // mi_bins(); + + // test_manage_os_memory(); + // test_large_pages(); + // detect double frees and theap corruption // double_free1(); // double_free2(); // corrupt_free(); // block_overflow1(); + // block_overflow2(); + test_canary_leak(); // test_aslr(); // invalid_free(); // test_reserved(); // negative_stat(); - // test_heap_walk(); + // test_theap_walk(); // alloc_huge(); - // test_heap_walk(); - // test_heap_arena(); - // test_align(); - + + void* p1 = malloc(78); void* p2 = malloc(24); free(p1); @@ -47,15 +68,15 @@ int main() { char* s = strdup("hello\n"); free(p2); - mi_heap_t* h = mi_heap_new(); - mi_heap_set_default(h); + // mi_theap_t* h = mi_theap_new(); + // mi_theap_set_default(h); p2 = malloc(16); p1 = realloc(p1, 32); free(p1); free(p2); free(s); - + /* now test if override worked by allocating/freeing across the api's*/ //p1 = mi_malloc(32); //free(p1); @@ -66,20 +87,13 @@ int main() { //mi_stats_print(NULL); // test_process_info(); - - return 0; -} -static void test_align() { - void* p = mi_malloc_aligned(256, 256); - if (((uintptr_t)p % 256) != 0) { - fprintf(stderr, "%p is not 256 alignend!\n", p); - } + return 0; } static void invalid_free() { free((void*)0xBADBEEF); - realloc((void*)0xBADBEEF,10); + realloc((void*)0xBADBEEF, 10); } static void block_overflow1() { @@ -88,6 +102,12 @@ static void block_overflow1() { free(p); } +static void block_overflow2() { + uint8_t* p = (uint8_t*)mi_malloc(16); + p[17] = 0; + free(p); +} + // The double free samples come ArcHeap [1] by Insu Yun (issue #161) // [1]: https://arxiv.org/pdf/1903.00503.pdf @@ -126,7 +146,7 @@ static void double_free2() { } -// Try to corrupt the heap through buffer overflow +// Try to corrupt the theap through buffer overflow #define N 256 #define SZ 64 @@ -171,7 +191,7 @@ static void test_process_info(void) { size_t peak_commit = 0; size_t page_faults = 0; for (int i = 0; i < 100000; i++) { - void* p = calloc(100,10); + void* p = calloc(100, 10); free(p); } mi_process_info(&elapsed, &user_msecs, &system_msecs, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults); @@ -179,10 +199,10 @@ static void test_process_info(void) { } static void test_reserved(void) { -#define KiB 1024ULL +#define KiB 1024UL #define MiB (KiB*KiB) #define GiB (MiB*KiB) - mi_reserve_os_memory(4*GiB, false, true); + mi_reserve_os_memory(3500*MiB, false, true); void* p1 = malloc(100); void* p2 = malloc(100000); void* p3 = malloc(2*GiB); @@ -228,20 +248,98 @@ static void test_heap_walk(void) { mi_heap_visit_blocks(heap, true, &test_visit, NULL); } -static void test_heap_arena(void) { +static void test_canary_leak(void) { + char* p = mi_mallocn_tp(char, 22); + for (int i = 0; i < 22; i++) { + p[i] = '0'+i; + } + puts(p); + free(p); +} + +#if _WIN32 +static void test_manage_os_memory(void) { + size_t size = 256 * 1024 * 1024; + void* ptr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); mi_arena_id_t arena_id; - int err = mi_reserve_os_memory_ex(100 * 1024 * 1024, false /* commit */, false /* allow large */, true /* exclusive */, &arena_id); - if (err) abort(); - mi_heap_t* heap = mi_heap_new_in_arena(arena_id); - for (int i = 0; i < 500000; i++) { - void* p = mi_heap_malloc(heap, 1024); - if (p == NULL) { - printf("out of memory after %d kb (expecting about 100_000kb)\n", i); - break; + mi_manage_os_memory_ex(ptr, size, true /* committed */, true /* pinned */, false /* is zero */, -1 /* numa node */, true /* exclusive */, &arena_id); + mi_heap_t* cuda_theap = mi_heap_new_in_arena(arena_id); // you can do this in any thread + + // now allocate only in the cuda arena + void* p1 = mi_heap_malloc(cuda_theap, 8); + int* p2 = mi_heap_malloc_tp(int,cuda_theap); + *p2 = 42; + + // and maybe set the cuda theap as the default theap? (but careful as now `malloc` will allocate in the cuda theap as well) + { + mi_theap_t* prev_default_theap = mi_theap_set_default(mi_heap_theap(cuda_theap)); + void* p3 = mi_malloc(8); // allocate in the cuda theap + mi_free(p3); + } + mi_free(p1); + mi_free(p2); +} +#else +static void test_manage_os_memory(void) { + // empty +} +#endif + +#if _WIN32 + +static void call_library(void) { + HMODULE dll = LoadLibraryA("mimalloc-test-static-dep.dll"); + if (dll != NULL) { + TestFun fun = (TestFun)GetProcAddress(dll, "Test"); + if (fun != NULL) { + fun(); } + bool ok = FreeLibrary(dll); + if (!ok) printf("unable to free library: %i\n", ok); } } +static void test_dep(void) { + call_library(); + call_library(); +} +#endif + +// Experiment with huge OS pages +#if 0 + +#include +#include +#include +#include + +static void test_large_pages(void) { + mi_memid_t memid; + +#if 0 + size_t pages_reserved; + size_t page_size; + uint8_t* p = (uint8_t*)_mi_os_alloc_huge_os_pages(1, -1, 30000, &pages_reserved, &page_size, &memid); + const size_t req_size = pages_reserved * page_size; +#else + const size_t req_size = 64*MI_MiB; + uint8_t* p = (uint8_t*)_mi_os_alloc(req_size, &memid, NULL); +#endif + + p[0] = 1; + + //_mi_os_protect(p, _mi_os_page_size()); + //_mi_os_unprotect(p, _mi_os_page_size()); + //_mi_os_decommit(p, _mi_os_page_size(), NULL); + if (madvise(p, req_size, MADV_HUGEPAGE) == 0) { + printf("advised huge pages\n"); + _mi_os_decommit(p, _mi_os_page_size(), NULL); + }; + _mi_os_free(p, req_size, memid, NULL); +} + +#endif + // ---------------------------- // bin size experiments // ------------------------------ @@ -249,8 +347,8 @@ static void test_heap_arena(void) { #if 0 #include #include +#include -#define MI_INTPTR_SIZE 8 #define MI_LARGE_WSIZE_MAX (4*1024*1024 / MI_INTPTR_SIZE) #define MI_BIN_HUGE 100 @@ -260,11 +358,11 @@ static void test_heap_arena(void) { static inline uint8_t mi_bsr32(uint32_t x); #if defined(_MSC_VER) -#include +//#include #include static inline uint8_t mi_bsr32(uint32_t x) { uint32_t idx; - _BitScanReverse((DWORD*)&idx, x); + _BitScanReverse(&idx, x); return idx; } #elif defined(__GNUC__) || defined(__clang__) @@ -288,7 +386,7 @@ static inline uint8_t mi_bsr32(uint32_t x) { } #endif -/* + // Bit scan reverse: return the index of the highest bit. uint8_t _mi_bsr(uintptr_t x) { if (x == 0) return 0; @@ -301,38 +399,39 @@ uint8_t _mi_bsr(uintptr_t x) { # error "define bsr for non-32 or 64-bit platforms" #endif } -*/ - static inline size_t _mi_wsize_from_size(size_t size) { return (size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); } +// #define MI_ALIGN2W + // Return the bin for a given field size. // Returns MI_BIN_HUGE if the size is too large. // We use `wsize` for the size in "machine word sizes", // i.e. byte size == `wsize*sizeof(void*)`. -extern inline uint8_t _mi_bin8(size_t size) { - size_t wsize = _mi_wsize_from_size(size); - uint8_t bin; - if (wsize <= 1) { +static inline size_t mi_bin(size_t wsize) { + // size_t wsize = _mi_wsize_from_size(size); + // size_t bin; + /*if (wsize <= 1) { bin = 1; } + */ #if defined(MI_ALIGN4W) - else if (wsize <= 4) { - bin = (uint8_t)((wsize+1)&~1); // round to double word sizes + if (wsize <= 4) { + return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes } #elif defined(MI_ALIGN2W) - else if (wsize <= 8) { - bin = (uint8_t)((wsize+1)&~1); // round to double word sizes + if (wsize <= 8) { + return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes } #else - else if (wsize <= 8) { - bin = (uint8_t)wsize; + if (wsize <= 8) { + return (wsize == 0 ? 1 : wsize); } #endif else if (wsize > MI_LARGE_WSIZE_MAX) { - bin = MI_BIN_HUGE; + return MI_BIN_HUGE; } else { #if defined(MI_ALIGN4W) @@ -340,15 +439,19 @@ extern inline uint8_t _mi_bin8(size_t size) { #endif wsize--; // find the highest bit - uint8_t b = mi_bsr32((uint32_t)wsize); + size_t idx; + mi_bsr(wsize, &idx); + uint8_t b = (uint8_t)idx; // and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation). // - adjust with 3 because we use do not round the first 8 sizes // which each get an exact bin - bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3; + const size_t bin = ((b << 2) + ((wsize >> (b - 2)) & 0x03)) - 3; + assert(bin > 0 && bin < MI_BIN_HUGE); + return bin; } - return bin; } + static inline uint8_t _mi_bin4(size_t size) { size_t wsize = _mi_wsize_from_size(size); uint8_t bin; @@ -372,44 +475,79 @@ static inline uint8_t _mi_bin4(size_t size) { bin = MI_BIN_HUGE; } else { - uint8_t b = mi_bsr32((uint32_t)wsize); + size_t idx; + mi_bsr(wsize, &idx); + uint8_t b = (uint8_t)idx; bin = ((b << 1) + (uint8_t)((wsize >> (b - 1)) & 0x01)) + 3; } return bin; } -static size_t _mi_binx4(size_t bsize) { - if (bsize==0) return 0; - uint8_t b = mi_bsr32((uint32_t)bsize); - if (b <= 1) return bsize; - size_t bin = ((b << 1) | (bsize >> (b - 1))&0x01); +static size_t _mi_binx4(size_t wsize) { + size_t bin; + if (wsize <= 1) { + bin = 1; + } + else if (wsize <= 8) { + // bin = (wsize+1)&~1; // round to double word sizes + bin = (uint8_t)wsize; + } + else { + size_t idx; + mi_bsr(wsize, &idx); + uint8_t b = (uint8_t)idx; + if (b <= 1) return wsize; + bin = ((b << 1) | (wsize >> (b - 1))&0x01) + 3; + } return bin; } static size_t _mi_binx8(size_t bsize) { if (bsize<=1) return bsize; - uint8_t b = mi_bsr32((uint32_t)bsize); + size_t idx; + mi_bsr(bsize, &idx); + uint8_t b = (uint8_t)idx; if (b <= 2) return bsize; size_t bin = ((b << 2) | (bsize >> (b - 2))&0x03) - 5; return bin; } + +static inline size_t mi_binx(size_t wsize) { + uint8_t bin; + if (wsize <= 1) { + bin = 1; + } + else if (wsize <= 8) { + // bin = (wsize+1)&~1; // round to double word sizes + bin = (uint8_t)wsize; + } + else { + wsize--; + assert(wsize>0); + // find the highest bit + uint8_t b = (uint8_t)(MI_SIZE_BITS - 1 - mi_clz(wsize)); + + // and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation). + // - adjust with 3 because we use do not round the first 8 sizes + // which each get an exact bin + bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3; + } + return bin; +} + + static void mi_bins(void) { //printf(" QNULL(1), /* 0 */ \\\n "); size_t last_bin = 0; - size_t min_bsize = 0; - size_t last_bsize = 0; - for (size_t bsize = 1; bsize < 2*1024; bsize++) { - size_t size = bsize * 64 * 1024; - size_t bin = _mi_binx8(bsize); + for (size_t wsize = 1; wsize <= (4*1024*1024) / 8 + 1024; wsize++) { + size_t bin = mi_bin(wsize); if (bin != last_bin) { - printf("min bsize: %6zd, max bsize: %6zd, bin: %6zd\n", min_bsize, last_bsize, last_bin); - //printf("QNULL(%6zd), ", wsize); - //if (last_bin%8 == 0) printf("/* %i */ \\\n ", last_bin); + //printf("min bsize: %6zd, max bsize: %6zd, bin: %6zd\n", min_wsize, last_wsize, last_bin); + printf("QNULL(%6zd), ", wsize-1); + if (last_bin%8 == 0) printf("/* %zu */ \\\n ", last_bin); last_bin = bin; - min_bsize = bsize; } - last_bsize = bsize; } } #endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.c index 284fdd20..837fdf42 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.c @@ -2,35 +2,48 @@ #include #include #include - -#include +#include +// #include int main() { mi_version(); // ensure mimalloc library is linked void* p1 = malloc(78); + _expand(p1, 100); + if (!mi_is_in_heap_region(p1)) { + printf("p1: malloc failed to allocate in heap region\n"); + return 1; + } + + void* p2 = malloc(24); + if (!mi_is_in_heap_region(p2)) { + printf("p2: malloc failed to allocate in heap region\n"); + return 1; + } free(p1); p1 = malloc(8); - //char* s = strdup("hello\n"); + char* s = strdup("hello\n"); free(p2); p2 = malloc(16); - p1 = realloc(p1, 32); + void* p3 = realloc(p1, 32); if (p3!=NULL) { p1 = p3; } free(p1); free(p2); - //free(s); + free(s); //mi_collect(true); /* now test if override worked by allocating/freeing across the api's*/ - //p1 = mi_malloc(32); - //free(p1); - //p2 = malloc(32); - //mi_free(p2); - p1 = malloc(24); - p2 = reallocarray(p1, 16, 16); - free(p2); - p1 = malloc(24); - assert(reallocarr(&p1, 16, 16) == 0); + p1 = mi_malloc(32); free(p1); + p2 = malloc(32); + mi_free(p2); + + //p1 = malloc(24); + //p2 = reallocarray(p1, 16, 16); + //free(p2); + //p1 = malloc(24); + //assert(reallocarr(&p1, 16, 16) == 0); + //free(p1); + mi_stats_print(NULL); return 0; } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.cpp b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.cpp index 582f24ee..c8b59ea3 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.cpp +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-override.cpp @@ -9,64 +9,83 @@ #include #include #include - #include -#include +#include +#include #include #ifdef _WIN32 #include -#endif - -#ifdef _WIN32 -#include +#include static void msleep(unsigned long msecs) { Sleep(msecs); } #else #include static void msleep(unsigned long msecs) { usleep(msecs * 1000UL); } #endif -static void heap_thread_free_large(); // issue #221 -static void heap_no_delete(); // issue #202 -static void heap_late_free(); // issue #204 +static void theap_thread_free_large(); // issue #221 +static void theap_no_delete(); // issue #202 +static void theap_late_free(); // issue #204 static void padding_shrink(); // issue #209 static void various_tests(); static void test_mt_shutdown(); -static void large_alloc(void); // issue #363 static void fail_aslr(); // issue #372 static void tsan_numa_test(); // issue #414 -static void strdup_test(); // issue #445 -static void bench_alloc_large(void); // issue #xxx -//static void test_large_migrate(void); // issue #691 -static void heap_thread_free_huge(); +static void strdup_test(); // issue #445 +static void theap_thread_free_huge(); static void test_std_string(); // issue #697 - +static void test_thread_local(); // issue #944 +// static void test_mixed0(); // issue #942 +static void test_mixed1(); // issue #942 static void test_stl_allocators(); - +static void test_join(); // issue #1177 +static void test_thread_leak(void); // issue #1104 +static void test_perf(void); // issue #1104 +static void test_perf2(void); // issue #1104 +static void test_perf3(void); // issue #1104 +static void test_perf4(void); // issue #1104 +static void test_perf5(void); // issue #1104 + +#if _WIN32 +#include "main-override-dep.h" +static void test_dep(); // issue #981: test overriding in another DLL +#else +static void test_dep() { }; +#endif int main() { - // mi_stats_reset(); // ignore earlier allocations - - // test_std_string(); - // heap_thread_free_huge(); + mi_stats_reset(); // ignore earlier allocations + //various_tests(); + //test_mixed1(); + + // test_dep(); + // test_join(); + + // test_thread_leak(); + // test_perf(); + // test_perf2(); + // test_perf3(); + // test_perf4(); + test_perf5(); + + //test_std_string(); + //test_thread_local(); + // theap_thread_free_huge(); /* - heap_thread_free_huge(); - heap_thread_free_large(); - heap_no_delete(); - heap_late_free(); - padding_shrink(); - various_tests(); - large_alloc(); - tsan_numa_test(); - strdup_test(); + theap_thread_free_large(); + theap_no_delete(); + theap_late_free(); + padding_shrink(); + + tsan_numa_test(); + */ + /* + strdup_test(); + test_stl_allocators(); + test_mt_shutdown(); */ - // test_stl_allocators(); - // test_mt_shutdown(); - // test_large_migrate(); - //fail_aslr(); - // bench_alloc_large(); - // mi_stats_print(NULL); + mi_stats_print(NULL); return 0; } @@ -109,6 +128,11 @@ static void various_tests() { t = new (tbuf) Test(42); t->~Test(); delete[] tbuf; + + #if _WIN32 + const char* ptr = ::_Getdays(); // test _base overrid + free((void*)ptr); + #endif } class Static { @@ -137,6 +161,18 @@ static bool test_stl_allocator1() { struct some_struct { int i; int j; double z; }; + +#if _WIN32 +static void test_dep() +{ + TestAllocInDll t; + std::string s = t.GetString(); + std::cout << "test_dep GetString: " << s << "\n"; + t.TestHeapAlloc(); +} +#endif + + static bool test_stl_allocator2() { std::vector > vec; vec.push_back(some_struct()); @@ -185,6 +221,53 @@ static void test_stl_allocators() { #endif } +#if 0 +#include +#include +#include +#include +#include +#include + +static void test_mixed0() { + std::vector> numbers(1024 * 1024 * 100); + std::vector threads(1); + + std::atomic index{}; + + auto start = std::chrono::system_clock::now(); + + for (auto& thread : threads) { + thread = std::thread{[&index, &numbers]() { + while (true) { + auto i = index.fetch_add(1, std::memory_order_relaxed); + if (i >= numbers.size()) return; + + numbers[i] = std::make_unique(i); + } + }}; + } + + for (auto& thread : threads) thread.join(); + + auto end = std::chrono::system_clock::now(); + + auto duration = + std::chrono::duration_cast(end - start); + std::cout << "Running on " << threads.size() << " threads took " << duration + << std::endl; +} +#endif + +void asd() { + void* p = malloc(128); + free(p); +} +static void test_mixed1() { + std::thread thread(asd); + thread.join(); +} + #if 0 // issue #691 static char* cptr; @@ -262,7 +345,7 @@ static void t1main() { mi_heap_delete(heap); } -static void heap_late_free() { +static void theap_late_free() { auto t1 = std::thread(t1main); msleep(2000); @@ -288,30 +371,56 @@ static void padding_shrink(void) // Issue #221 -static void heap_thread_free_large_worker() { +static void theap_thread_free_large_worker() { mi_free(shared_p); } -static void heap_thread_free_large() { +static void theap_thread_free_large() { for (int i = 0; i < 100; i++) { - shared_p = mi_malloc_aligned(2 * 1024 * 1024 + 1, 8); - auto t1 = std::thread(heap_thread_free_large_worker); + shared_p = mi_malloc_aligned(2*1024*1024 + 1, 8); + auto t1 = std::thread(theap_thread_free_large_worker); t1.join(); } } -static void heap_thread_free_huge_worker() { +static void theap_thread_free_huge_worker() { mi_free(shared_p); } -static void heap_thread_free_huge() { - for (int i = 0; i < 100; i++) { +static void theap_thread_free_huge() { + for (int i = 0; i < 10; i++) { shared_p = mi_malloc(1024 * 1024 * 1024); - auto t1 = std::thread(heap_thread_free_huge_worker); + auto t1 = std::thread(theap_thread_free_huge_worker); t1.join(); } } +static std::atomic xgsum; + +static void local_alloc() { + long sum = 0; + for(int i = 0; i < 1000000; i++) { + const int n = 1 + std::rand() % 1000; + uint8_t* p = (uint8_t*)calloc(n, 1); + p[0] = 1; + sum += p[std::rand() % n]; + if ((std::rand() % 100) > 24) { + free(p); + } + } + xgsum += sum; +} + +static void test_thread_leak() { + std::vector threads; + for (int i=1; i<=100; ++i) { + threads.emplace_back(std::thread(&local_alloc)); + } + for (auto& th : threads) { + th.join(); + } +} + static void test_mt_shutdown() { const int threads = 5; @@ -336,21 +445,9 @@ static void test_mt_shutdown() std::cout << "done" << std::endl; } -// issue #363 -using namespace std; - -void large_alloc(void) -{ - char* a = new char[1ull << 25]; - thread th([&] { - delete[] a; - }); - th.join(); -} - // issue #372 static void fail_aslr() { - size_t sz = (4ULL << 40); // 4TiB + size_t sz = (size_t)(4ULL << 40); // 4TiB void* p = malloc(sz); printf("pointer p: %p: area up to %p\n", p, (uint8_t*)p + sz); *(int*)0x5FFFFFFF000 = 0; // should segfault @@ -368,33 +465,171 @@ static void tsan_numa_test() { t1.join(); } -// issue #? -#include -#include -#include -static void bench_alloc_large(void) { - static constexpr int kNumBuffers = 20; - static constexpr size_t kMinBufferSize = 5 * 1024 * 1024; - static constexpr size_t kMaxBufferSize = 25 * 1024 * 1024; - std::unique_ptr buffers[kNumBuffers]; - - std::random_device rd; (void)rd; - std::mt19937 gen(42); //rd()); - std::uniform_int_distribution<> size_distribution(kMinBufferSize, kMaxBufferSize); - std::uniform_int_distribution<> buf_number_distribution(0, kNumBuffers - 1); - - static constexpr int kNumIterations = 2000; - const auto start = std::chrono::steady_clock::now(); - for (int i = 0; i < kNumIterations; ++i) { - int buffer_idx = buf_number_distribution(gen); - size_t new_size = size_distribution(gen); - buffers[buffer_idx] = std::make_unique(new_size); +class MTest +{ + char *data; +public: + MTest() { data = (char*)malloc(1024); } + ~MTest() { free(data); }; +}; + +thread_local MTest tlVariable; + +void threadFun( int i ) +{ + printf( "Thread %d\n", i ); + std::this_thread::sleep_for( std::chrono::milliseconds(100) ); +} + +void test_thread_local() +{ + for( int i=1; i < 100; ++i ) + { + std::thread t( threadFun, i ); + t.join(); + mi_stats_print(NULL); + } + return; +} + +// issue #1177 +thread_local void* s_ptr = mi_malloc(1); + +void test_join() { + std::thread thread([]() { mi_free(s_ptr); }); + thread.join(); + mi_free(s_ptr); +} + + +static std::atomic gsum; + +const int LEN[] = { 1000, 5000, 10000, 50000 }; + +// adapted from example in +// https://github.com/microsoft/mimalloc/issues/1104 + +static void test_perf_local_alloc() +{ + // thread-local random number generator + std::minstd_rand rng(std::random_device{}()); + + long sum = 0; + for (int i = 0; i < 1000000; i++) + { + int len = LEN[rng() % 4]; + int* p = (int*)mi_zalloc_aligned(len * sizeof(int), alignof(int)); + p[0] = 1; + sum += p[rng() % len]; + free(p); } - const auto end = std::chrono::steady_clock::now(); - const auto num_ms = std::chrono::duration_cast(end - start).count(); - const auto us_per_allocation = std::chrono::duration_cast(end - start).count() / kNumIterations; - std::cout << kNumIterations << " allocations Done in " << num_ms << "ms." << std::endl; - std::cout << "Avg " << us_per_allocation << " us per allocation" << std::endl; + std::cout << "."; + gsum += sum; } +static void test_perf_run() +{ + std::vector threads; + for (int i = 0; i < 24; ++i) + { + threads.emplace_back(std::thread(&test_perf_local_alloc)); + } + for (auto& th : threads) + { + th.join(); + } + std::cout << "\n"; +} + +void test_perf(void) +{ + test_perf_run(); + std::cout << "gsum: " << gsum.load() << "\n"; +} + + +static int sum2; + +static void escape(uint8_t* p, size_t n) { + if (n==0) return; + p[std::rand() % n] = 42; + sum2 += p[std::rand() % n]; +} + +void test_perf2(void) { + for (size_t i = 0; i < 100000000; i++) { + const size_t n = 1000; + uint8_t* p = (uint8_t*)calloc(1, n); + escape(p,n); + free(p); + } +} + +void test_perf3(void) { + for (size_t i = 0; i < 5; i++) { + const size_t n = (size_t)1*1024*1024*1024; + uint8_t* p = (uint8_t*)calloc(1, n); + escape(p, n); + free(p); + } +} + + +static void local_alloc4() { + for (int i = 0; i < 1000000; i++) { + const size_t n = i%1000; + uint8_t* p = (uint8_t*)calloc(1,n); + escape(p,n); + if (i % 4 > 0) { + free(p); + } + } +} + +static void test_perf4(void) { + std::vector threads; + for (int i = 1; i <= 100; ++i) { + threads.emplace_back(std::thread(&local_alloc4)); + } + for (auto& th : threads) { + th.join(); + } +} + + +void escape5(uint8_t* p, size_t n) { + if (n==0) return; + for (size_t i = 0; i < n; i++) { + p[i] = (uint8_t)(i & 0xFF); + } + p[rand() % n] = (uint8_t)(n&0xFF); + // asm volatile("" : : "g"(p) : "memory"); +} + +static long gsum5; + +static void local_alloc5() { + long sum = 0; + for (int i = 0; i < 500000; i++) { + const size_t n = i % 1000; + uint8_t* p = (uint8_t*)mi_malloc(n); + escape5(p, n); + if (i % 4 > 0) { + if (n>0) { sum += p[n-1]; } + mi_free(p); + } + } + gsum5 += sum; +} + +static void test_perf5(void) { + std::vector threads; + for (int i = 1; i <= 100; ++i) { + threads.emplace_back(std::thread(&local_alloc5)); + } + for (auto& th : threads) { + th.join(); + } + printf("gsum5: %li\n", gsum5); +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static-dep.cpp b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static-dep.cpp new file mode 100644 index 00000000..98ad2a2a --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static-dep.cpp @@ -0,0 +1,51 @@ +// test allocation in a DLL that is statically linked to mimalloc +#include +#include +#include "main-static-dep.h" +#include + +class Static { +private: + void* p; +public: + Static() { + printf("static-dep: static constructor\n"); + p = mi_malloc(64); + return; + } + ~Static() { + mi_free(p); + printf("static-dep: static destructor\n"); + return; + } +}; + +static Static s = Static(); + +void Test(void) { + char* s = mi_mallocn_tp(char, 128); + #ifdef _WIN32 + strcpy_s(s, 128, "hello world!"); + #else + strlcpy(s, "hello world!", 128); + #endif + printf("message from static dll: %s\n", s); + mi_free(s); +} + +#ifdef WIN32 +#include + +BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) { + (void)(reserved); + (void)(module); + if (reason==DLL_PROCESS_ATTACH) { + printf("static-dep: dll attach\n"); + } + else if (reason==DLL_PROCESS_DETACH) { + mi_option_enable(mi_option_destroy_on_exit); + printf("static-dep: dll detach\n"); + } + return TRUE; +} +#endif \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static-dep.h b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static-dep.h new file mode 100644 index 00000000..d652d5e4 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static-dep.h @@ -0,0 +1,17 @@ +#pragma once + +#if __cplusplus +extern "C" { +#endif + +#ifdef WIN32 +typedef void (__cdecl *TestFun)(void); +__declspec(dllexport) void __cdecl Test(void); +#else +typedef void (*TestFun)(void); +void Test(void); +#endif + +#if __cplusplus +} +#endif \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static.cpp b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static.cpp new file mode 100644 index 00000000..3310bd07 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main-static.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main-static-dep.h" + +TestFun fun; +void* so; + +void testso() { + fun(); +} + +void loadso() { + so = dlopen("./libstatic.so", RTLD_LAZY); + fun = (TestFun)dlsym(so,"Test"); + testso(); +} + +static void test_static(void) { + auto t1 = std::thread(&loadso); + t1.join(); + auto t2 = std::thread(&testso); + t2.join(); +} + +int main(int argc, char** argv) { + test_static(); + return 0; +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main.c index b148f712..6d94c8ec 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/main.c @@ -8,7 +8,7 @@ void test_heap(void* p_out) { void* p2 = mi_heap_malloc(heap,48); mi_free(p_out); mi_heap_destroy(heap); - //mi_heap_delete(heap); mi_free(p1); mi_free(p2); + //mi_theap_delete(theap); mi_free(p1); mi_free(p2); } void test_large() { diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api-fill.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api-fill.c index 3fca3b9d..466d0f55 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api-fill.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api-fill.c @@ -271,7 +271,7 @@ int main(void) { mi_free(p); }; - #if !(MI_TRACK_VALGRIND || MI_TRACK_ASAN) + #if !(MI_TRACK_VALGRIND || MI_TRACK_ASAN || MI_GUARDED) CHECK_BODY("fill-freed-small") { size_t malloc_size = MI_SMALL_SIZE_MAX / 2; uint8_t* p = (uint8_t*)mi_malloc(malloc_size); @@ -310,7 +310,7 @@ bool check_zero_init(uint8_t* p, size_t size) { #if MI_DEBUG >= 2 bool check_debug_fill_uninit(uint8_t* p, size_t size) { -#if MI_TRACK_VALGRIND || MI_TRACK_ASAN +#if MI_TRACK_VALGRIND || MI_TRACK_ASAN || MI_GUARDED (void)p; (void)size; return true; // when compiled with valgrind we don't init on purpose #else @@ -326,7 +326,7 @@ bool check_debug_fill_uninit(uint8_t* p, size_t size) { } bool check_debug_fill_freed(uint8_t* p, size_t size) { -#if MI_TRACK_VALGRIND +#if MI_TRACK_VALGRIND || MI_GUARDED (void)p; (void)size; return true; // when compiled with valgrind we don't fill on purpose #else diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api.c index 76101980..25d5e77a 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-api.c @@ -34,22 +34,24 @@ we therefore test the API over various inputs. Please add more tests :-) #include "mimalloc.h" // #include "mimalloc/internal.h" -#include "mimalloc/types.h" // for MI_DEBUG and MI_BLOCK_ALIGNMENT_MAX +#include "mimalloc/types.h" // for MI_DEBUG and MI_PAGE_MAX_OVERALLOC_ALIGN #include "testhelper.h" // --------------------------------------------------------------------------- // Test functions // --------------------------------------------------------------------------- -bool test_heap1(void); -bool test_heap2(void); +bool test_theap1(void); +bool test_theap2(void); +bool test_theap_arena_destroy(void); +bool test_theap_arena_delete(void); bool test_stl_allocator1(void); bool test_stl_allocator2(void); -bool test_stl_heap_allocator1(void); -bool test_stl_heap_allocator2(void); -bool test_stl_heap_allocator3(void); -bool test_stl_heap_allocator4(void); +bool test_stl_theap_allocator1(void); +bool test_stl_theap_allocator2(void); +bool test_stl_theap_allocator3(void); +bool test_stl_theap_allocator4(void); bool mem_is_zero(uint8_t* p, size_t size) { if (p==NULL) return false; @@ -65,6 +67,15 @@ bool mem_is_zero(uint8_t* p, size_t size) { int main(void) { mi_option_disable(mi_option_verbose); + CHECK_BODY("malloc-aligned9a") { // test large alignments + void* p = mi_zalloc_aligned(1024 * 1024, 2); + mi_free(p); + p = mi_zalloc_aligned(1024 * 1024, 2); + mi_free(p); + result = true; + }; + + // --------------------------------------------------- // Malloc // --------------------------------------------------- @@ -77,9 +88,14 @@ int main(void) { CHECK_BODY("malloc-nomem1") { result = (mi_malloc((size_t)PTRDIFF_MAX + (size_t)1) == NULL); }; - CHECK_BODY("malloc-null") { + CHECK_BODY("malloc-free-null") { mi_free(NULL); }; + #if MI_INTPTR_BITS > 32 + CHECK_BODY("malloc-free-invalid-low") { + mi_free((void*)(MI_ZU(0x0000000003990080))); // issue #1087 + }; + #endif CHECK_BODY("calloc-overflow") { // use (size_t)&mi_calloc to get some number without triggering compiler warnings result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL); @@ -154,12 +170,13 @@ int main(void) { void* p = mi_malloc_aligned(4097,4096); size_t usable = mi_usable_size(p); result = (usable >= 4097 && usable < 16000); - printf("malloc_aligned5: usable size: %zi\n", usable); + fprintf(stderr, "malloc_aligned5: usable size: %zi. ", usable); mi_free(p); }; + /* CHECK_BODY("malloc-aligned6") { bool ok = true; - for (size_t align = 1; align <= MI_BLOCK_ALIGNMENT_MAX && ok; align *= 2) { + for (size_t align = 1; align <= MI_PAGE_MAX_OVERALLOC_ALIGN && ok; align *= 2) { void* ps[8]; for (int i = 0; i < 8 && ok; i++) { ps[i] = mi_malloc_aligned(align*13 // size @@ -174,26 +191,34 @@ int main(void) { } result = ok; }; + */ CHECK_BODY("malloc-aligned7") { - void* p = mi_malloc_aligned(1024,MI_BLOCK_ALIGNMENT_MAX); + void* p = mi_malloc_aligned(1024,MI_PAGE_MAX_OVERALLOC_ALIGN); mi_free(p); - result = ((uintptr_t)p % MI_BLOCK_ALIGNMENT_MAX) == 0; + result = ((uintptr_t)p % MI_PAGE_MAX_OVERALLOC_ALIGN) == 0; }; CHECK_BODY("malloc-aligned8") { bool ok = true; for (int i = 0; i < 5 && ok; i++) { int n = (1 << i); - void* p = mi_malloc_aligned(1024, n * MI_BLOCK_ALIGNMENT_MAX); - ok = ((uintptr_t)p % (n*MI_BLOCK_ALIGNMENT_MAX)) == 0; + void* p = mi_malloc_aligned(1024, n * MI_PAGE_MAX_OVERALLOC_ALIGN); + ok = ((uintptr_t)p % (n*MI_PAGE_MAX_OVERALLOC_ALIGN)) == 0; mi_free(p); } result = ok; }; - CHECK_BODY("malloc-aligned9") { + CHECK_BODY("malloc-aligned9") { // test large alignments bool ok = true; void* p[8]; - size_t sizes[8] = { 8, 512, 1024 * 1024, MI_BLOCK_ALIGNMENT_MAX, MI_BLOCK_ALIGNMENT_MAX + 1, 2 * MI_BLOCK_ALIGNMENT_MAX, 8 * MI_BLOCK_ALIGNMENT_MAX, 0 }; - for (int i = 0; i < 28 && ok; i++) { + const int max_align_shift = + #if SIZE_MAX > UINT32_MAX + 28 + #else + 20 + #endif + ; + size_t sizes[8] = { 8, 512, 1024 * 1024, MI_PAGE_MAX_OVERALLOC_ALIGN, MI_PAGE_MAX_OVERALLOC_ALIGN + 1, 2 * MI_PAGE_MAX_OVERALLOC_ALIGN, 8 * MI_PAGE_MAX_OVERALLOC_ALIGN, 0 }; + for (int i = 0; i < max_align_shift && ok; i++) { int align = (1 << i); for (int j = 0; j < 8 && ok; j++) { p[j] = mi_zalloc_aligned(sizes[j], align); @@ -219,12 +244,12 @@ int main(void) { } result = ok; } - CHECK_BODY("malloc_aligned11") { - mi_heap_t* heap = mi_heap_new(); - void* p = mi_heap_malloc_aligned(heap, 33554426, 8); - result = mi_heap_contains_block(heap, p); - mi_heap_destroy(heap); - } + //CHECK_BODY("malloc_aligned11") { + // mi_theap_t* theap = mi_theap_new(); + // void* p = mi_theap_malloc_aligned(theap, 33554426, 8); + // result = mi_theap_contains_block(theap, p); + // mi_theap_destroy(theap); + //} CHECK_BODY("mimalloc-aligned12") { void* p = mi_malloc_aligned(0x100, 0x100); result = (((uintptr_t)p % 0x100) == 0); // #602 @@ -241,7 +266,7 @@ int main(void) { } for(int i = 0; i < 10 && ok; i++) { mi_free(p[i]); - } + } /* if (ok && align <= size && ((size + MI_PADDING_SIZE) & (align-1)) == 0) { size_t bsize = mi_good_size(size); @@ -311,11 +336,47 @@ int main(void) { mi_free(p); }; + CHECK_BODY("realloc-guarded") { // issue #1304 + void* shared_ptr = NULL; + for (int iterations = 0; iterations < 64; ++iterations) { + for (int i = 0; i < 1024; ++i) { + shared_ptr = mi_realloc(shared_ptr, i * 64); + } + } + } + + // --------------------------------------------------- + // Returned block sizes + // --------------------------------------------------- + CHECK_BODY("umalloc1") { + for(size_t size = 1; size <= 32*MI_MiB; size *= 2 ) { + size_t bsize; + void* p = mi_umalloc(size,&bsize); + assert(bsize >= size); + size_t pre_size; + size_t post_size; + p = mi_urealloc(p, size + 1024, &pre_size, &post_size); + assert(pre_size == bsize); + assert(post_size >= size + 1024); + size_t fsize; + mi_ufree(p,&fsize); + assert(fsize == post_size); + } + } + + #if (MI_INTPTR_SIZE > 4) + CHECK_BODY("arena_reserve") { + result = (0==mi_reserve_os_memory(16*MI_GiB,false,true)); + } + #endif + // --------------------------------------------------- // Heaps // --------------------------------------------------- - CHECK("heap_destroy", test_heap1()); - CHECK("heap_delete", test_heap2()); + //CHECK("theap_destroy", test_theap1()); + //CHECK("theap_delete", test_theap2()); + //CHECK("theap_arena_destroy", test_theap_arena_destroy()); + //CHECK("theap_arena_delete", test_theap_arena_delete()); //mi_stats_print(NULL); @@ -333,10 +394,10 @@ int main(void) { CHECK("stl_allocator1", test_stl_allocator1()); CHECK("stl_allocator2", test_stl_allocator2()); - CHECK("stl_heap_allocator1", test_stl_heap_allocator1()); - CHECK("stl_heap_allocator2", test_stl_heap_allocator2()); - CHECK("stl_heap_allocator3", test_stl_heap_allocator3()); - CHECK("stl_heap_allocator4", test_stl_heap_allocator4()); + //CHECK("stl_theap_allocator1", test_stl_theap_allocator1()); + //CHECK("stl_theap_allocator2", test_stl_theap_allocator2()); + //CHECK("stl_theap_allocator3", test_stl_theap_allocator3()); + //CHECK("stl_theap_allocator4", test_stl_theap_allocator4()); // --------------------------------------------------- // Done @@ -348,26 +409,53 @@ int main(void) { // Larger test functions // --------------------------------------------------- -bool test_heap1(void) { - mi_heap_t* heap = mi_heap_new(); - int* p1 = mi_heap_malloc_tp(heap,int); - int* p2 = mi_heap_malloc_tp(heap,int); +/* +bool test_theap1(void) { + mi_theap_t* theap = mi_theap_new(); + int* p1 = mi_theap_malloc_tp(theap,int); + int* p2 = mi_theap_malloc_tp(theap,int); *p1 = *p2 = 43; - mi_heap_destroy(heap); + mi_theap_destroy(theap); return true; } -bool test_heap2(void) { - mi_heap_t* heap = mi_heap_new(); - int* p1 = mi_heap_malloc_tp(heap,int); - int* p2 = mi_heap_malloc_tp(heap,int); - mi_heap_delete(heap); +bool test_theap2(void) { + mi_theap_t* theap = mi_theap_new(); + int* p1 = mi_theap_malloc_tp(theap,int); + int* p2 = mi_theap_malloc_tp(theap,int); + mi_theap_delete(theap); *p1 = 42; mi_free(p1); mi_free(p2); return true; } +bool test_theap_arena_destroy(void) { + mi_arena_id_t arena_id = NULL; + if (mi_reserve_os_memory_ex(64 * 1024 * 1024, true, false, true, &arena_id) != 0) { + return false; + } + mi_theap_t* theap = mi_theap_new_ex(0, true, arena_id); + if (theap == NULL) { + return false; + } + mi_theap_destroy(theap); + return true; +} + +bool test_theap_arena_delete(void) { + mi_arena_id_t arena_id = NULL; + if (mi_reserve_os_memory_ex(64 * 1024 * 1024, true, false, true, &arena_id) != 0) { + return false; + } + mi_theap_t* theap = mi_theap_new_ex(0, true, arena_id); + if (theap == NULL) { + return false; + } + mi_theap_delete(theap); + return true; +} +*/ bool test_stl_allocator1(void) { #ifdef __cplusplus std::vector > vec; @@ -392,9 +480,10 @@ bool test_stl_allocator2(void) { #endif } -bool test_stl_heap_allocator1(void) { +/* +bool test_stl_theap_allocator1(void) { #ifdef __cplusplus - std::vector > vec; + std::vector > vec; vec.push_back(some_struct()); vec.pop_back(); return vec.size() == 0; @@ -403,9 +492,9 @@ bool test_stl_heap_allocator1(void) { #endif } -bool test_stl_heap_allocator2(void) { +bool test_stl_theap_allocator2(void) { #ifdef __cplusplus - std::vector > vec; + std::vector > vec; vec.push_back(some_struct()); vec.pop_back(); return vec.size() == 0; @@ -414,38 +503,39 @@ bool test_stl_heap_allocator2(void) { #endif } -bool test_stl_heap_allocator3(void) { +bool test_stl_theap_allocator3(void) { #ifdef __cplusplus - mi_heap_t* heap = mi_heap_new(); + mi_theap_t* theap = mi_theap_new(); bool good = false; { - mi_heap_stl_allocator myAlloc(heap); - std::vector > vec(myAlloc); + mi_theap_stl_allocator myAlloc(theap); + std::vector > vec(myAlloc); vec.push_back(some_struct()); vec.pop_back(); good = vec.size() == 0; } - mi_heap_delete(heap); + mi_theap_delete(theap); return good; #else return true; #endif } -bool test_stl_heap_allocator4(void) { +bool test_stl_theap_allocator4(void) { #ifdef __cplusplus - mi_heap_t* heap = mi_heap_new(); + mi_theap_t* theap = mi_theap_new(); bool good = false; { - mi_heap_destroy_stl_allocator myAlloc(heap); - std::vector > vec(myAlloc); + mi_theap_destroy_stl_allocator myAlloc(theap); + std::vector > vec(myAlloc); vec.push_back(some_struct()); vec.pop_back(); good = vec.size() == 0; } - mi_heap_destroy(heap); + mi_theap_destroy(theap); return good; #else return true; #endif } +*/ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-stress.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-stress.c index 15d0e25b..52da81e8 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-stress.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-stress.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2020 Microsoft Research, Daan Leijen +Copyright (c) 2018-2025 Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. -----------------------------------------------------------------------------*/ @@ -22,40 +22,89 @@ terms of the MIT license. #include #include +// #define MI_GUARDED 1 +// #define USE_STD_MALLOC 1 + +#ifndef USE_STD_MALLOC +#define MI_USE_HEAPS 4 +#endif + // > mimalloc-test-stress [THREADS] [SCALE] [ITER] // // argument defaults -static int THREADS = 32; // more repeatable if THREADS <= #processors -static int SCALE = 25; // scaling factor - -#if defined(MI_TSAN) -static int ITER = 10; // N full iterations destructing and re-creating all threads (on tsan reduce for azure pipeline limits) +#if defined(MI_TSAN) // with thread-sanitizer reduce the threads to test within the azure pipeline limits +static int THREADS = 8; +static int SCALE = 25; +static int ITER = 400; +#elif defined(MI_UBSAN) // with undefined behavious sanitizer reduce parameters to stay within the azure pipeline limits +static int THREADS = 8; +static int SCALE = 25; +static int ITER = 20; +#elif defined(MI_GUARDED) // with debug guard pages reduce parameters to stay within the azure pipeline limits +static int THREADS = 8; +static int SCALE = 10; +static int ITER = 10; +#elif 0 +static int THREADS = 4; +static int SCALE = 10; +static int ITER = 20; +#elif 0 +static int THREADS = 32; +static int SCALE = 50; +static int ITER = 50; +#elif 0 +static int THREADS = 32; +static int SCALE = 25; +static int ITER = 50; +#define ALLOW_LARGE true #else +static int THREADS = 32; // more repeatable if THREADS <= #processors +static int SCALE = 50; // scaling factor static int ITER = 50; // N full iterations destructing and re-creating all threads #endif -// static int THREADS = 8; // more repeatable if THREADS <= #processors -// static int SCALE = 100; // scaling factor + #define STRESS // undefine for leak test -static bool allow_large_objects = true; // allow very large objects? (set to `true` if SCALE>100) +#ifndef ALLOW_LARGE +#define ALLOW_LARGE false +#endif + +static bool allow_large_objects = ALLOW_LARGE; // allow very large objects? (set to `true` if SCALE>100) + static size_t use_one_size = 0; // use single object size of `N * sizeof(uintptr_t)`? static bool main_participates = false; // main thread participates as a worker too -// #define USE_STD_MALLOC #ifdef USE_STD_MALLOC + #define custom_calloc(n,s) calloc(n,s) #define custom_realloc(p,s) realloc(p,s) #define custom_free(p) free(p) + #else + #include +#include + +#ifdef MI_USE_HEAPS +static mi_heap_t* current_heap; +#define custom_calloc(n,s) mi_heap_calloc(current_heap,n,s) +#define custom_realloc(p,s) mi_heap_realloc(current_heap,p,s) +#define custom_free(p) mi_free(p) +#else #define custom_calloc(n,s) mi_calloc(n,s) #define custom_realloc(p,s) mi_realloc(p,s) #define custom_free(p) mi_free(p) #endif +#ifndef NDEBUG +#define xMI_HEAP_WALK // walk the theap objects? +#endif + +#endif + // transfer pointer between threads #define TRANSFERS (1000) static volatile void* transfer[TRANSFERS]; @@ -102,9 +151,9 @@ static void* alloc_items(size_t items, random_t r) { else if (chance(10, r) && allow_large_objects) items *= 1000; // 0.1% huge else items *= 100; // 1% large objects; } - if (items == 40) items++; // pthreads uses that size for stack increases + if (items>=32 && items<=40) items*=2; // pthreads uses 320b allocations (this shows that more clearly in the stats) if (use_one_size > 0) items = (use_one_size / sizeof(uintptr_t)); - if (items==0) items = 1; + if (items==0) items = 1; uintptr_t* p = (uintptr_t*)custom_calloc(items,sizeof(uintptr_t)); if (p != NULL) { for (uintptr_t i = 0; i < items; i++) { @@ -129,6 +178,16 @@ static void free_items(void* p) { custom_free(p); } +#ifdef MI_HEAP_WALK +static bool visit_blocks(const mi_theap_t* theap, const mi_theap_area_t* area, void* block, size_t block_size, void* arg) { + (void)(theap); (void)(area); + size_t* total = (size_t*)arg; + if (block != NULL) { + *total += block_size; + } + return true; +} +#endif static void stress(intptr_t tid) { //bench_start_thread(); @@ -173,6 +232,13 @@ static void stress(intptr_t tid) { data[data_idx] = q; } } + + #ifdef MI_HEAP_WALK + // walk the theap + size_t total = 0; + mi_theap_visit_blocks(mi_theap_get_default(), true, visit_blocks, &total); + #endif + // free everything that is left for (size_t i = 0; i < retain_top; i++) { free_items(retained[i]); @@ -188,23 +254,73 @@ static void stress(intptr_t tid) { static void run_os_threads(size_t nthreads, void (*entry)(intptr_t tid)); static void test_stress(void) { + #ifdef MI_USE_HEAPS + mi_heap_t* prev_heaps[MI_USE_HEAPS] = { NULL }; + #endif uintptr_t r = rand(); for (int n = 0; n < ITER; n++) { - run_os_threads(THREADS, &stress); + + #ifdef MI_USE_HEAPS + // new heap for each iteration + if (prev_heaps[MI_USE_HEAPS-1] != NULL) { + mi_heap_delete(prev_heaps[MI_USE_HEAPS-1]); // delete from N iterations ago + } + for(int i = MI_USE_HEAPS-1; i > 0; i--) { + prev_heaps[i] = prev_heaps[i-1]; + } + prev_heaps[0] = current_heap; + current_heap = mi_heap_new(); + #endif + + run_os_threads(THREADS, &stress); + + #if !defined(NDEBUG) && !defined(USE_STD_MALLOC) + // switch between arena and OS allocation for testing + // mi_option_set_enabled(mi_option_disallow_arena_alloc, (n%2)==1); + #endif + #if defined(MI_HEAP_WALK) && defined(MI_USE_HEAPS) + size_t total = 0; + // mi_abandoned_visit_blocks(mi_subproc_main(), -1, true, visit_blocks, &total); + mi_heap_visit_blocks(heap, true, visit_blocks, &total); + #endif + for (int i = 0; i < TRANSFERS; i++) { if (chance(50, &r) || n + 1 == ITER) { // free all on last run, otherwise free half of the transfers void* p = atomic_exchange_ptr(&transfer[i], NULL); free_items(p); } } - #ifndef NDEBUG - //mi_collect(false); - //mi_debug_show_arenas(); - #endif + #if !defined(NDEBUG) || defined(MI_TSAN) - if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); } + if ((n + 1) % 10 == 0) { + printf("- iterations left: %3d\n", ITER - (n + 1)); + #ifndef USE_STD_MALLOC + mi_debug_show_arenas(); + #endif + //mi_collect(true); + //mi_debug_show_arenas(); + } #endif } + + #ifndef USE_STD_MALLOC + mi_stats_print(NULL); + #endif + + // clean up (a bit too early to test the final free_items still works correctly) + #ifdef MI_USE_HEAPS + for (int i = 0; i < MI_USE_HEAPS; i++) { + mi_heap_delete(prev_heaps[i]); prev_heaps[i] = NULL; + } + mi_heap_delete(current_heap); current_heap = NULL; + #endif + + for (int i = 0; i < TRANSFERS; i++) { + void* p = atomic_exchange_ptr(&transfer[i], NULL); + if (p != NULL) { + free_items(p); + } + } } #ifndef STRESS @@ -229,10 +345,28 @@ static void test_leak(void) { } #endif +#if defined(USE_STD_MALLOC) && defined(MI_LINK_VERSION) +#ifdef __cplusplus +extern "C" +#endif +int mi_version(void); +#endif + int main(int argc, char** argv) { - #ifndef USE_STD_MALLOC - mi_stats_reset(); - #endif + #ifdef MI_LINK_VERSION + mi_version(); + #endif + #ifdef MI_HEAP_WALK + mi_option_enable(mi_option_visit_abandoned); + #endif + #if !defined(NDEBUG) && !defined(USE_STD_MALLOC) + mi_option_set(mi_option_arena_reserve, mi_arena_min_size()/1024 /* in KiB ! */); + mi_option_set(mi_option_purge_delay,1); + #endif + #if defined(NDEBUG) && !defined(USE_STD_MALLOC) + // mi_option_set(mi_option_purge_delay,-1); + mi_option_set(mi_option_page_reclaim_on_free, 0); + #endif // > mimalloc-test-stress [THREADS] [SCALE] [ITER] if (argc >= 2) { @@ -253,7 +387,16 @@ int main(int argc, char** argv) { if (SCALE > 100) { allow_large_objects = true; } - printf("Using %d threads with a %d%% load-per-thread and %d iterations %s\n", THREADS, SCALE, ITER, (allow_large_objects ? "(allow large objects)" : "")); + printf("Using %d threads with a %d%% load-per-thread and %d iterations%s", THREADS, SCALE, ITER, (allow_large_objects ? " (allow large objects)" : "")); + #if MI_USE_HEAPS + printf(" (using %d rolling heaps)", MI_USE_HEAPS); + #endif + printf("\n"); + + #if !defined(NDEBUG) && !defined(USE_STD_MALLOC) + mi_stats_reset(); + #endif + //mi_reserve_os_memory(1024*1024*1024ULL, false, true); //int res = mi_reserve_huge_os_pages(4,1); //printf("(reserve huge: %i\n)", res); @@ -262,24 +405,25 @@ int main(int argc, char** argv) { // Run ITER full iterations where half the objects in the transfer buffer survive to the next round. srand(0x7feb352d); - - //mi_reserve_os_memory(512ULL << 20, true, true); - -#if !defined(NDEBUG) && !defined(USE_STD_MALLOC) - mi_stats_reset(); -#endif - + // mi_stats_reset(); #ifdef STRESS - test_stress(); + test_stress(); #else - test_leak(); + test_leak(); #endif #ifndef USE_STD_MALLOC #ifndef NDEBUG - // mi_collect(true); - mi_debug_show_arenas(true,true,true); - #endif + mi_collect(true); + mi_debug_show_arenas(); + //mi_collect(true); + //char* json = mi_stats_get_json(0, NULL); + //if (json != NULL) { + // fputs(json,stderr); + // mi_free(json); + //} + #endif + mi_collect(true); mi_stats_print(NULL); #endif //bench_end_program(); @@ -291,7 +435,7 @@ static void (*thread_entry_fun)(intptr_t) = &stress; #ifdef _WIN32 -#include +#include static DWORD WINAPI thread_entry(LPVOID param) { thread_entry_fun((intptr_t)param); @@ -302,9 +446,10 @@ static void run_os_threads(size_t nthreads, void (*fun)(intptr_t)) { thread_entry_fun = fun; DWORD* tids = (DWORD*)custom_calloc(nthreads,sizeof(DWORD)); HANDLE* thandles = (HANDLE*)custom_calloc(nthreads,sizeof(HANDLE)); + thandles[0] = GetCurrentThread(); // avoid lint warning const size_t start = (main_participates ? 1 : 0); for (size_t i = start; i < nthreads; i++) { - thandles[i] = CreateThread(0, 8*1024, &thread_entry, (void*)(i), 0, &tids[i]); + thandles[i] = CreateThread(0, 8*1024L, &thread_entry, (void*)(i), 0, &tids[i]); } if (main_participates) fun(0); // run the main thread as well for (size_t i = start; i < nthreads; i++) { diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-wrong.c b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-wrong.c index 56a2339a..666d99f5 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-wrong.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/mimalloc-dev/test/test-wrong.c @@ -53,8 +53,11 @@ terms of the MIT license. A copy of the license can be found in the file #endif int main(int argc, char** argv) { + (void)(argc); + (void)(argv); int* p = (int*)mi(malloc)(3*sizeof(int)); - + p[0] = 1; + int* r = (int*)mi_malloc_aligned(8,16); mi_free(r); @@ -64,19 +67,20 @@ int main(int argc, char** argv) { mi(free)(c); // undefined access - int* q = (int*)mi(malloc)(sizeof(int)); - printf("undefined: %d\n", *q); + long* q = (long*)mi(malloc)(sizeof(long)); + printf("undefined: %ld\n", *q); // illegal int read - printf("invalid: over: %d, under: %d\n", q[1], q[-1]); + printf("invalid: over: %ld, under: %ld\n", q[1], q[-1]); *q = 42; // buffer overflow q[1] = 43; + q[2] = 44; // buffer underflow - q[-1] = 44; + q[-1] = 41; mi(free)(q); @@ -84,9 +88,9 @@ int main(int argc, char** argv) { mi(free)(q); // use after free - printf("use-after-free: %d\n", *q); + printf("use-after-free: %ld\n", *q); // leak p // mi_free(p) return 0; -} \ No newline at end of file +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c b/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c index 627c3731..cf499af3 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c @@ -220,14 +220,13 @@ typedef struct ExternalInfo { napi_finalize finalizeCallback; // size_t } ExternalInfo; +#ifdef USE_HOST_OBJECT typedef struct NapiHostObjectInfo { void *data; - napi_ref ref; napi_finalize finalize_cb; - bool is_array; - napi_ref getter; - napi_ref setter; + napi_host_object_methods methods; } NapiHostObjectInfo; +#endif typedef struct JsAtoms { @@ -266,7 +265,6 @@ typedef struct napi_env__ { JsAtoms atoms; ExternalInfo *gcBefore; ExternalInfo *gcAfter; - int js_enter_state; int64_t usedMemory; } napi_env__; @@ -297,21 +295,14 @@ typedef struct ExternalBufferInfo { } ExternalBufferInfo; /** - * ------------------------------------- - * MICROTASK HANDLING - * ------------------------------------- + * MICROTASK HANDLING + * + * The microtask queue is drained by NapiScope (see quickjs/jsr.h) once the + * native call stack fully unwinds back out of JS — the same scope-depth model + * used by the Hermes and PrimJS engines. qjs_execute_pending_jobs below is the + * pump it calls; there is no longer any per-napi-call js_enter/js_exit here. */ -static inline void js_enter(napi_env env) { - env->js_enter_state++; -} - -static inline void js_exit(napi_env env) { - if (--env->js_enter_state <= 0) { - qjs_execute_pending_jobs(env); - } -} - /** * -------------------------------------- * NAPI DATA FINALIZERS @@ -428,7 +419,7 @@ static inline napi_status CreateJSValueHandle(napi_env env, JSValue value, struc CHECK_ARG(env) CHECK_ARG(result) - RETURN_STATUS_IF_FALSE(!LIST_EMPTY(&env->handleScopeList), napi_handle_scope_empty) + RETURN_STATUS_IF_FALSE(!LIST_EMPTY(&env->handleScopeList), napi_handle_scope_mismatch) napi_handle_scope handleScope = LIST_FIRST(&env->handleScopeList); @@ -484,7 +475,7 @@ napi_status napi_open_handle_scope(napi_env env, napi_handle_scope *result) { napi_handle_scope__ *handleScope = (napi_handle_scope__ *) mi_malloc( sizeof(napi_handle_scope__)); - RETURN_STATUS_IF_FALSE(handleScope, napi_memory_error) + RETURN_STATUS_IF_FALSE(handleScope, napi_handle_scope_mismatch) handleScope->type = HANDLE_HEAP_ALLOCATED; handleScope->handleCount = 0; handleScope->escapeCalled = false; @@ -530,12 +521,12 @@ napi_status napi_open_escapable_handle_scope(napi_env env, napi_escapable_handle sizeof(napi_handle_scope__)); handleScope->type = HANDLE_HEAP_ALLOCATED; handleScope->handleCount = 0; - RETURN_STATUS_IF_FALSE(handleScope, napi_memory_error) + RETURN_STATUS_IF_FALSE(handleScope, napi_handle_scope_mismatch) SLIST_INIT(&handleScope->handleList); handleScope->escapeCalled = false; LIST_INSERT_HEAD(&env->handleScopeList, handleScope, node); - *result = handleScope; + *result = (napi_escapable_handle_scope) handleScope; return napi_clear_last_error(env); } @@ -545,23 +536,27 @@ napi_close_escapable_handle_scope(napi_env env, napi_escapable_handle_scope esca CHECK_ARG(env) CHECK_ARG(escapableScope) - assert(LIST_FIRST(&env->handleScopeList) == escapableScope && + // Escapable scopes share the napi_handle_scope__ representation; the public + // type is opaque and distinct, so cast to operate on the members. + napi_handle_scope__ *scope = (napi_handle_scope__ *) escapableScope; + + assert(LIST_FIRST(&env->handleScopeList) == scope && "napi_close_handle_scope() or napi_close_escapable_handle_scope() should follow FILO rule."); Handle *handle, *tempHandle; - SLIST_FOREACH_SAFE(handle, &escapableScope->handleList, node, tempHandle) { + SLIST_FOREACH_SAFE(handle, &scope->handleList, node, tempHandle) { JS_FreeValue(env->context, handle->value); handle->value = JSUndefined; // Instead of freeing, return the handle to the pool for reuse - SLIST_REMOVE(&escapableScope->handleList, handle, Handle, node); + SLIST_REMOVE(&scope->handleList, handle, Handle, node); if (handle->type == HANDLE_HEAP_ALLOCATED) { mi_free(handle); } } - LIST_REMOVE(escapableScope, node); - mi_free(escapableScope); + LIST_REMOVE(scope, node); + mi_free(scope); return napi_clear_last_error(env); } @@ -574,16 +569,20 @@ napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, CHECK_ARG(scope) CHECK_ARG(escapee) - RETURN_STATUS_IF_FALSE(!scope->escapeCalled, napi_escape_called_twice) + // Escapable scopes share the napi_handle_scope__ representation; the public + // type is opaque and distinct, so cast to operate on the members. + napi_handle_scope__ *hs = (napi_handle_scope__ *) scope; + + RETURN_STATUS_IF_FALSE(!hs->escapeCalled, napi_escape_called_twice) // Get the outer handle scope - napi_handle_scope handleScope = LIST_NEXT(scope, node); - RETURN_STATUS_IF_FALSE(handleScope, napi_handle_scope_empty) + napi_handle_scope handleScope = LIST_NEXT(hs, node); + RETURN_STATUS_IF_FALSE(handleScope, napi_handle_scope_mismatch) Handle *handle = (Handle *) mi_malloc(sizeof(Handle)); - RETURN_STATUS_IF_FALSE(handle, napi_memory_error) + RETURN_STATUS_IF_FALSE(handle, napi_handle_scope_mismatch) - scope->escapeCalled = true; + hs->escapeCalled = true; handle->value = JS_DupValue(env->context, *((JSValue *) escapee)); SLIST_INSERT_HEAD(&handleScope->handleList, handle, node); @@ -796,7 +795,7 @@ napi_create_reference(napi_env env, napi_value value, uint32_t initialRefCount, CHECK_ARG(result) *result = (napi_ref__ *) mi_malloc(sizeof(napi_ref__)); - RETURN_STATUS_IF_FALSE(*result, napi_memory_error) + RETURN_STATUS_IF_FALSE(*result, napi_generic_failure) JSValue jsValue = *((JSValue *) value); @@ -882,7 +881,7 @@ napi_status napi_get_reference_value(napi_env env, napi_ref ref, napi_value *res CHECK_ARG(result) if (!ref->referenceCount && JS_IsUndefined(ref->value)) { - CreateScopedResult(env, JS_UNDEFINED, result); + return CreateScopedResult(env, JS_UNDEFINED, result); } JSValue value; @@ -1263,7 +1262,9 @@ napi_create_external(napi_env env, void *data, napi_finalize finalize_cb, void * JS_SetOpaque(object, externalInfo); - napi_status status = CreateScopedResult(env, object, result); + // On failure CreateScopedResult frees `object`, which runs the external + // finalizer and frees `externalInfo`; do not touch it afterwards. + CHECK_NAPI(CreateScopedResult(env, object, result)); externalInfo->finalizeCallback = finalize_cb; @@ -1777,7 +1778,7 @@ napi_status napi_get_typedarray_info(napi_env env, } if (arraybuffer) { - CreateScopedResult(env, jsArrayBuffer, arraybuffer); + CHECK_NAPI(CreateScopedResult(env, jsArrayBuffer, arraybuffer)); } else { JS_FreeValue(env->context, jsArrayBuffer); } @@ -1823,7 +1824,7 @@ napi_status napi_get_dataview_info(napi_env env, } if (arraybuffer) { - CreateScopedResult(env, jsArrayBuffer, arraybuffer); + CHECK_NAPI(CreateScopedResult(env, jsArrayBuffer, arraybuffer)); } else { JS_FreeValue(env->context, jsArrayBuffer); } @@ -2043,8 +2044,15 @@ napi_status napi_get_value_string_latin1(napi_env env, napi_value value, char *s CHECK_ARG(result) *result = cstr_len; } else if (length != 0) { - strcpy(str, cstr); - str[cstr_len] = '\0'; + // Respect the destination buffer size (length includes the null + // terminator) and report the number of bytes written, excluding the + // null — matching the Node-API contract. + size_t to_copy = cstr_len < length - 1 ? cstr_len : length - 1; + memcpy(str, cstr, to_copy); + str[to_copy] = '\0'; + if (result != NULL) { + *result = to_copy; + } } else if (result != NULL) { *result = 0; } @@ -2069,8 +2077,15 @@ napi_status napi_get_value_string_utf8(napi_env env, napi_value value, char *str CHECK_ARG(result) *result = cstr_len; } else if (length != 0) { - strcpy(str, cstr); - str[cstr_len] = '\0'; + // Respect the destination buffer size (length includes the null + // terminator) and report the number of bytes written, excluding the + // null — matching the Node-API contract. + size_t to_copy = cstr_len < length - 1 ? cstr_len : length - 1; + memcpy(str, cstr, to_copy); + str[to_copy] = '\0'; + if (result != NULL) { + *result = to_copy; + } } else if (result != NULL) { *result = 0; } @@ -2325,19 +2340,16 @@ napi_status napi_coerce_to_string(napi_env env, napi_value value, napi_value *re CHECK_ARG(result) JSValue jsValue = *((JSValue *) value); - JSValue jsResult; - if (JS_IsSymbol(jsValue)) { - jsResult = JS_GetPropertyStr(env->context, jsValue, "description"); - } else { - jsResult = JS_ToString(env->context, jsValue); - } + // ToString abstract operation. Per spec (and matching the V8 N-API) this + // throws a TypeError for a Symbol; callers that want a symbol's textual form + // must handle it before coercing (see Console::buildStringFromArg). JS_ToString + // returns a value with an owned reference, which CreateScopedResult takes over. + JSValue jsResult = JS_ToString(env->context, jsValue); if (JS_IsException(jsResult)) { return napi_set_last_error(env, napi_pending_exception, NULL, 0, NULL); } - JS_DupValue(env->context, jsResult); - return CreateScopedResult(env, jsResult, result); } @@ -2960,7 +2972,7 @@ napi_status napi_delete_element(napi_env env, napi_value object, uint32_t index, return napi_clear_last_error(env); } -static inline void +static inline napi_status napi_set_property_descriptor(napi_env env, napi_value object, napi_property_descriptor descriptor) { JSAtom key; @@ -2995,7 +3007,12 @@ napi_set_property_descriptor(napi_env env, napi_value object, napi_property_desc } else if (descriptor.method) { flags |= JS_PROP_HAS_VALUE; napi_value function = NULL; - napi_create_function(env, descriptor.utf8name, NAPI_AUTO_LENGTH, descriptor.method, descriptor.data, &function); + napi_status status = napi_create_function(env, descriptor.utf8name, NAPI_AUTO_LENGTH, + descriptor.method, descriptor.data, &function); + if (status != napi_ok) { + JS_FreeAtom(env->context, key); + return napi_set_last_error(env, status, NULL, 0, NULL); + } if (function) { value = *((JSValue *) function); } @@ -3003,7 +3020,12 @@ napi_set_property_descriptor(napi_env env, napi_value object, napi_property_desc if (descriptor.getter) { napi_value getter = NULL; flags |= JS_PROP_HAS_GET; - napi_create_function(env, descriptor.utf8name, NAPI_AUTO_LENGTH, descriptor.getter, descriptor.data, &getter); + napi_status status = napi_create_function(env, descriptor.utf8name, NAPI_AUTO_LENGTH, + descriptor.getter, descriptor.data, &getter); + if (status != napi_ok) { + JS_FreeAtom(env->context, key); + return napi_set_last_error(env, status, NULL, 0, NULL); + } if (getter) { getterValue = *((JSValue *) getter); } @@ -3012,7 +3034,12 @@ napi_set_property_descriptor(napi_env env, napi_value object, napi_property_desc if (descriptor.setter) { napi_value setter = NULL; flags |= JS_PROP_HAS_SET; - napi_create_function(env, descriptor.utf8name, NAPI_AUTO_LENGTH, descriptor.setter, descriptor.data, &setter); + napi_status status = napi_create_function(env, descriptor.utf8name, NAPI_AUTO_LENGTH, + descriptor.setter, descriptor.data, &setter); + if (status != napi_ok) { + JS_FreeAtom(env->context, key); + return napi_set_last_error(env, status, NULL, 0, NULL); + } if (setter) { setterValue = *((JSValue *) setter); } @@ -3021,6 +3048,8 @@ napi_set_property_descriptor(napi_env env, napi_value object, napi_property_desc JS_DefineProperty(env->context, jsObject, key, value, getterValue, setterValue, flags); JS_FreeAtom(env->context, key); + + return napi_clear_last_error(env); } napi_status napi_define_properties(napi_env env, napi_value object, size_t property_count, @@ -3036,7 +3065,7 @@ napi_status napi_define_properties(napi_env env, napi_value object, size_t prope } for (size_t i = 0; i < property_count; i++) { - napi_set_property_descriptor(env, object, properties[i]); + CHECK_NAPI(napi_set_property_descriptor(env, object, properties[i])); } return napi_clear_last_error(env); @@ -3093,7 +3122,6 @@ napi_status napi_call_function(napi_env env, napi_value thisValue, napi_value fu jsThis = JS_GetGlobalObject(env->context); } - js_enter(env); JSValue *args = NULL; JSValue returnValue; @@ -3119,8 +3147,6 @@ napi_status napi_call_function(napi_env env, napi_value thisValue, napi_value fu NULL); } - js_exit(env); - if (useGlobal) JS_FreeValue(env->context, jsThis); if (JS_IsException(returnValue)) { @@ -3210,7 +3236,7 @@ napi_create_function(napi_env env, const char *utf8name, size_t length, napi_cal CHECK_ARG(result) FunctionInfo *functionInfo = (FunctionInfo *) mi_malloc(sizeof(FunctionInfo)); - RETURN_STATUS_IF_FALSE(functionInfo, napi_memory_error) + RETURN_STATUS_IF_FALSE(functionInfo, napi_generic_failure) functionInfo->data = data; functionInfo->callback = cb; @@ -3299,7 +3325,6 @@ napi_new_instance(napi_env env, napi_value constructor, size_t argc, const napi_ CHECK_ARG(constructor) CHECK_ARG(result) - js_enter(env); JSValue *args = NULL; JSValue returnValue; @@ -3324,11 +3349,11 @@ napi_new_instance(napi_env env, napi_value constructor, size_t argc, const napi_ args); } - js_exit(env); - if (JS_IsException(returnValue)) { - JS_Throw(env->context, returnValue); + // The constructor's real error is already pending; leave it intact rather + // than re-throwing the JS_EXCEPTION sentinel (which frees the real error + // and replaces it with a non-object sentinel). return napi_set_last_error(env, napi_pending_exception, NULL, 0, NULL); } @@ -3372,10 +3397,11 @@ CallConstructor(JSContext *context, JSValueConst newTarget, int argc, JSValueCon JSValue returnValue = JS_UNDEFINED; if (result) { - returnValue = *((JSValue *) result); - JS_DupValue(env->context, returnValue); - JS_FreeValue(env->context, thisValue); + returnValue = JS_DupValue(env->context, *((JSValue *) result)); } + // Always release the trampoline-created `this`; a null result (callback + // returned undefined or bailed) previously leaked it. + JS_FreeValue(env->context, thisValue); assert(LIST_FIRST(&env->handleScopeList) == &handleScope && "napi_close_handle_scope() or napi_close_escapable_handle_scope() should follow FILO rule."); @@ -3434,10 +3460,17 @@ napi_status napi_define_class(napi_env env, JS_SetConstructor(env->context, cls, prototype); for (size_t i = 0; i < property_count; i++) { + napi_status status; if (properties[i].attributes & napi_static) { - napi_set_property_descriptor(env, (napi_value) &cls, properties[i]); + status = napi_set_property_descriptor(env, (napi_value) &cls, properties[i]); } else { - napi_set_property_descriptor(env, (napi_value) &prototype, properties[i]); + status = napi_set_property_descriptor(env, (napi_value) &prototype, properties[i]); + } + if (status != napi_ok) { + JS_FreeValue(env->context, external); + JS_FreeValue(env->context, prototype); + JS_FreeValue(env->context, cls); + return napi_set_last_error(env, status, NULL, 0, NULL); } } @@ -3470,7 +3503,7 @@ napi_wrap(napi_env env, napi_value jsObject, void *nativeObject, napi_finalize f externalInfo->data = nativeObject; externalInfo->finalizeHint = finalize_hint; - externalInfo->finalizeCallback = NULL; + externalInfo->finalizeCallback = finalize_cb; JSValue external = JS_NewObjectClass(env->context, (int) env->runtime->externalClassId); @@ -3486,7 +3519,7 @@ napi_wrap(napi_env env, napi_value jsObject, void *nativeObject, napi_finalize f if (result) { napi_ref ref; - napi_create_reference(env, jsObject, 0, &ref); + CHECK_NAPI(napi_create_reference(env, jsObject, 0, &ref)); *result = ref; } @@ -3615,7 +3648,7 @@ napi_add_finalizer(napi_env env, napi_value jsObject, void *nativeObject, napi_f if (result) { napi_ref ref; - napi_create_reference(env, jsObject, 0, &ref); + CHECK_NAPI(napi_create_reference(env, jsObject, 0, &ref)); *result = ref; } @@ -3704,10 +3737,8 @@ napi_status napi_resolve_deferred(napi_env env, napi_deferred deferred, napi_val if (resolution != NULL) { value = *((JSValue *) resolution); } - js_enter(env); JSValue jsResult = JS_Call(env->context, *((JSValue *) deferred->resolve), JS_UNDEFINED, 1, &value); - js_exit(env); JS_FreeValue(env->context, jsResult); return napi_clear_last_error(env); @@ -3721,10 +3752,8 @@ napi_status napi_reject_deferred(napi_env env, napi_deferred deferred, napi_valu if (rejection != NULL) { value = *((JSValue *) rejection); } - js_enter(env); JSValue jsResult = JS_Call(env->context, *((JSValue *) deferred->reject), JS_UNDEFINED, 1, &value); - js_exit(env); JS_FreeValue(env->context, jsResult); return napi_clear_last_error(env); @@ -3838,169 +3867,146 @@ napi_status napi_run_script(napi_env env, } +#ifdef USE_HOST_OBJECT + +// Open/close a temporary handle scope around a host-object callback. Mirrors +// the function-callback trampoline (only heap-allocated handles are freed; +// stack handles live in the scope struct). +#define HO_SCOPE_OPEN \ + napi_handle_scope__ _hs; \ + _hs.type = HANDLE_STACK_ALLOCATED; \ + _hs.handleCount = 0; \ + _hs.escapeCalled = false; \ + SLIST_INIT(&_hs.handleList); \ + LIST_INSERT_HEAD(&env->handleScopeList, &_hs, node); +#define HO_SCOPE_CLOSE \ + do { \ + Handle *_h, *_th; \ + SLIST_FOREACH_SAFE(_h, &_hs.handleList, node, _th) { \ + JS_FreeValue(env->context, _h->value); \ + _h->value = JSUndefined; \ + SLIST_REMOVE(&_hs.handleList, _h, Handle, node); \ + if (_h->type == HANDLE_HEAP_ALLOCATED) mi_free(_h); \ + } \ + LIST_REMOVE(&_hs, node); \ + } while (0) + void host_object_finalizer(JSRuntime *rt, JSValue value) { napi_env env = (napi_env) JS_GetRuntimeOpaque(rt); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(value, - env->runtime->napiHostObjectClassId); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque( + value, env->runtime->napiHostObjectClassId); + if (info == NULL) return; if (info->finalize_cb) { info->finalize_cb(env, info->data, NULL); } - if (info->is_array) { - napi_delete_reference(env, info->getter); - napi_delete_reference(env, info->setter); - } - - napi_delete_reference(env, info->ref); mi_free(info); } int host_object_set(JSContext *ctx, JSValue obj, JSAtom atom, JSValue value, JSValue receiver, int flags) { napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - if (info->is_array) { - JSValue atom_val = JS_AtomToValue(ctx, atom); - JSValue argv[4] = { - info->ref->value, - atom_val, - value, - obj - }; - JSValue result = JS_Call(ctx, info->setter->value, JS_UNDEFINED, 4, argv); - - JS_FreeValue(ctx, atom_val); - - if (JS_IsException(result) || JS_HasException(ctx)) return -1; - - return true; - } - return JS_SetProperty(ctx, info->ref->value, atom, JS_DupValue(ctx, value)); - } + NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque( + obj, env->runtime->napiHostObjectClassId); + if (info == NULL || info->methods.set == NULL) return true; + + HO_SCOPE_OPEN + napi_value host, prop, val; + CreateScopedResult(env, JS_DupValue(ctx, obj), &host); + CreateScopedResult(env, JS_AtomToValue(ctx, atom), &prop); + CreateScopedResult(env, JS_DupValue(ctx, value), &val); + info->methods.set(env, host, prop, val, info->data); + HO_SCOPE_CLOSE; + + if (JS_HasException(ctx)) return -1; return true; } JSValue host_object_get(JSContext *ctx, JSValue obj, JSAtom atom, JSValue receiver) { napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - if (info->is_array) { - JSValue atom_val = JS_AtomToValue(ctx, atom); - JSValue argv[3] = { - info->ref->value, - atom_val, - obj - }; - JSValue value = JS_Call(ctx, info->getter->value, JS_UNDEFINED, 3, argv); - JS_FreeValue(ctx, atom_val); - return value; - } - return JS_GetProperty(ctx, info->ref->value, atom); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque( + obj, env->runtime->napiHostObjectClassId); + if (info == NULL || info->methods.get == NULL) return JS_UNDEFINED; + + HO_SCOPE_OPEN + napi_value host, prop; + CreateScopedResult(env, JS_DupValue(ctx, obj), &host); + CreateScopedResult(env, JS_AtomToValue(ctx, atom), &prop); + napi_value result = info->methods.get(env, host, prop, info->data); + JSValue ret = JS_UNDEFINED; + if (result != NULL) { + ret = JS_DupValue(ctx, *((JSValue *) result)); } - return JS_UNDEFINED; + HO_SCOPE_CLOSE; + + if (JS_HasException(ctx)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + return ret; } int host_object_has(JSContext *ctx, JSValue obj, JSAtom atom) { napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - return JS_HasProperty(ctx, info->ref->value, atom); - } - return false; + NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque( + obj, env->runtime->napiHostObjectClassId); + if (info == NULL || info->methods.has == NULL) return false; + + HO_SCOPE_OPEN + napi_value host, prop; + CreateScopedResult(env, JS_DupValue(ctx, obj), &host); + CreateScopedResult(env, JS_AtomToValue(ctx, atom), &prop); + bool present = info->methods.has(env, host, prop, info->data); + HO_SCOPE_CLOSE; + + if (JS_HasException(ctx)) return -1; + return present; } static int host_object_delete(JSContext *ctx, JSValue obj, JSAtom atom) { napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - return JS_DeleteProperty(ctx, info->ref->value, atom, 0); - } - return true; -} + NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque( + obj, env->runtime->napiHostObjectClassId); + if (info == NULL || info->methods.delete_property == NULL) return true; -static int host_object_get_own_property_names(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, - JSValue obj) { - napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - return JS_GetOwnPropertyNames(ctx, ptab, plen, info->ref->value, - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY); - } - return true; -} + HO_SCOPE_OPEN + napi_value host, prop; + CreateScopedResult(env, JS_DupValue(ctx, obj), &host); + CreateScopedResult(env, JS_AtomToValue(ctx, atom), &prop); + bool deleted = info->methods.delete_property(env, host, prop, info->data); + HO_SCOPE_CLOSE; -static int host_object_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc, - JSValue obj, JSAtom prop) { - napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - return JS_GetOwnProperty(ctx, desc, info->ref->value, prop); - } - return true; + if (JS_HasException(ctx)) return -1; + return deleted; } -static int host_object_define_own_property(JSContext *ctx, JSValue obj, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, - int flags) { - napi_env env = (napi_env) JS_GetContextOpaque(ctx); - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(obj, - env->runtime->napiHostObjectClassId); - if (info != NULL) { - return JS_DefineProperty(ctx, info->ref->value, prop, JS_DupValue(ctx, val), getter, setter, - flags); - } - return true; -} +#undef HO_SCOPE_OPEN +#undef HO_SCOPE_CLOSE JSClassExoticMethods NapiHostObjectExoticMethods = { .set_property = host_object_set, .get_property = host_object_get, .has_property = host_object_has, .delete_property = host_object_delete, -// .get_own_property_names = host_object_get_own_property_names, -// .get_own_property = host_object_get_own_property, -// .define_own_property = host_object_define_own_property }; - napi_status -napi_create_host_object(napi_env env, napi_value value, napi_finalize finalize, void *data, - bool is_array, napi_value getter, napi_value setter, napi_value *result) { +napi_create_host_object(napi_env env, napi_finalize finalize, void *data, + const napi_host_object_methods *methods, + napi_value *result) { CHECK_ARG(env); + CHECK_ARG(methods); CHECK_ARG(result); + RETURN_STATUS_IF_FALSE(methods->get != NULL && methods->set != NULL, + napi_invalid_arg); - napi_value constructor; - napi_get_named_property(env, value, "constructor", &constructor); - - napi_value prototype; - napi_get_named_property(env, constructor, "prototype", &prototype); + JSValue jsValue = JS_NewObjectClass(env->context, + env->runtime->napiHostObjectClassId); - JSValue jsValue = JS_NewObjectClass(env->context, env->runtime->napiHostObjectClassId); - JS_SetPrototype(env->context, jsValue, *((JSValue *) prototype)); - - NapiHostObjectInfo *info = (NapiHostObjectInfo *) mi_malloc(sizeof(NapiHostObjectInfo)); + NapiHostObjectInfo *info = + (NapiHostObjectInfo *) mi_malloc(sizeof(NapiHostObjectInfo)); info->data = data; - if (finalize) { - info->finalize_cb = finalize; - } else { - info->finalize_cb = NULL; - } - info->is_array = is_array; - - if (is_array) { - if (getter) napi_create_reference(env, getter, 1, &info->getter); - if (setter) napi_create_reference(env, setter, 1, &info->setter); - } - - napi_create_reference(env, value, 1, &info->ref); + info->finalize_cb = finalize; + info->methods = *methods; JS_SetOpaque(jsValue, info); return CreateScopedResult(env, jsValue, result); @@ -4013,13 +4019,12 @@ napi_status napi_get_host_object_data(napi_env env, napi_value object, void **da JSValue jsValue = *((JSValue *) object); - if (!JS_IsObject(jsValue)) { return napi_set_last_error(env, napi_object_expected, NULL, 0, NULL); } - NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque(jsValue, - env->runtime->napiHostObjectClassId); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) JS_GetOpaque( + jsValue, env->runtime->napiHostObjectClassId); if (info) { *data = info->data; } else { @@ -4039,17 +4044,14 @@ napi_status napi_is_host_object(napi_env env, napi_value object, bool *result) { return napi_set_last_error(env, napi_object_expected, NULL, 0, NULL); } - void *data = JS_GetOpaque(jsValue, - env->runtime->napiHostObjectClassId); - if (data != NULL) { - *result = true; - } else { - *result = false; - } + void *data = JS_GetOpaque(jsValue, env->runtime->napiHostObjectClassId); + *result = data != NULL; return napi_clear_last_error(env); } +#endif // USE_HOST_OBJECT + /** * -------------------------------- * JS RUNTIME @@ -4065,6 +4067,36 @@ napi_status qjs_create_runtime(napi_runtime *runtime) { *runtime = mi_malloc(sizeof(napi_runtime__)); #ifdef USE_MIMALLOC +#ifdef MIMALLOC_PERF + // One-time mimalloc runtime tuning: trade memory for speed. Set before the + // first runtime is created (options are process-global). Guarded so it runs + // once even if multiple runtimes/workers are spawned. + static int mi_perf_configured = 0; + if (!mi_perf_configured) { + mi_perf_configured = 1; + // Never decommit/return freed pages to the OS: eliminates the + // madvise(DONTNEED)/re-commit syscall churn during GC. Biggest lever. + mi_option_set(mi_option_purge_delay, -1); + // Commit arenas up front instead of lazily on first page fault. + mi_option_set(mi_option_arena_eager_commit, 1); + // Transparent huge pages default to OFF on Android (mi_option_allow_thp + // = 0). Enabling madvise(MADV_HUGEPAGE) is the effective way to get 2MB + // pages here (fewer TLB misses); this is a v3 option. + mi_option_set(mi_option_allow_thp, 1); + // Also try explicit large OS pages; silently ignored if the kernel has + // no hugepage pool (typical on Android), so it's a harmless best-effort. + mi_option_set(mi_option_allow_large_os_pages, 1); + // v3: keep more full pages cached per size class (default 2) so freed + // pages are reused instead of released back to the arena. + mi_option_set(mi_option_page_full_retain, 4); + // v3: run mimalloc's internal heap collection less often (default every + // 10000 generic allocs) — fewer housekeeping passes at the cost of RSS. + mi_option_set(mi_option_generic_collect, 100000); + // Reserve a 64MB arena up front to avoid many small mmap calls as the + // JS heap grows. This option is stored internally in KiB. + mi_option_set(mi_option_arena_reserve, 64 * 1024); + } +#endif (*runtime)->runtime = JS_NewRuntime2(&mi_mf, NULL); #else (*runtime)->runtime = JS_NewRuntime(); @@ -4086,8 +4118,10 @@ napi_status qjs_create_runtime(napi_runtime *runtime) { JSClassDef ExternalClassDef = {"ExternalInfo", external_finalizer, NULL, NULL, NULL}; JSClassDef FunctionClassDef = {"FunctionInfo", function_finalizer, NULL, NULL, NULL}; JSClassDef ConstructorClassDef = {"ConstructorInfo", function_finalizer, NULL, NULL, NULL}; +#ifdef USE_HOST_OBJECT JSClassDef NapiHostObjectClassDef = {"NapiHostObject", host_object_finalizer, NULL, NULL, &NapiHostObjectExoticMethods}; +#endif #ifndef __QJS_NG__ JS_NewClassID( &(*runtime)->napiHostObjectClassId); JS_NewClassID( &(*runtime)->constructorClassId); @@ -4100,7 +4134,9 @@ napi_status qjs_create_runtime(napi_runtime *runtime) { JS_NewClassID((*runtime)->runtime, &(*runtime)->externalClassId); #endif +#ifdef USE_HOST_OBJECT JS_NewClass((*runtime)->runtime, (*runtime)->napiHostObjectClassId, &NapiHostObjectClassDef); +#endif JS_NewClass((*runtime)->runtime, (*runtime)->externalClassId, &ExternalClassDef); JS_NewClass((*runtime)->runtime, (*runtime)->functionClassId, &FunctionClassDef); JS_NewClass((*runtime)->runtime, (*runtime)->constructorClassId, &ConstructorClassDef); @@ -4172,8 +4208,6 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { (*env)->context = context; - (*env)->js_enter_state = 0; - JS_SetRuntimeOpaque(runtime->runtime, *env); // Create runtime atoms @@ -4216,6 +4250,10 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { JS_SetHostPromiseRejectionTracker(runtime->runtime, JSR_PromiseRejectionTracker, *env); + // referenceSymbolValue is otherwise unassigned but is JS_FreeValue'd in + // qjs_free_napi_env; without this it holds uninitialized mi_malloc garbage and + // freeing it at env teardown dereferences a bogus refcount pointer (SIGSEGV). + (*env)->referenceSymbolValue = JS_UNDEFINED; (*env)->instanceData = NULL; (*env)->isThrowNull = false; (*env)->gcBefore = NULL; @@ -4365,20 +4403,64 @@ napi_status qjs_execute_script(napi_env env, JSValue eval_result; const char *cScript = JS_ToCString(env->context, *((JSValue *) script)); - js_enter(env); eval_result = JS_Eval(env->context, cScript, strlen(cScript), file, JS_EVAL_TYPE_GLOBAL); JS_FreeCString(env->context, cScript); - js_exit(env); if (JS_IsException(eval_result)) { - const char *exceptionMessage = JS_ToCString(env->context, eval_result); - napi_set_last_error(env, napi_cannot_run_js, exceptionMessage, 0, NULL); - JS_FreeCString(env->context, exceptionMessage); - JS_Throw(env->context, eval_result); + // JS_Eval failed (e.g. a syntax error) and already left the real error + // object as the context's pending exception. Leave it intact so the + // caller can retrieve it via napi_get_and_clear_last_exception. + // + // Do NOT JS_Throw(eval_result) here: eval_result is only the JS_EXCEPTION + // sentinel, and JS_Throw frees the real pending error and replaces it with + // that sentinel. The caller would then receive a non-object sentinel + // instead of the actual Error/SyntaxError (which fails napi_typeof and any + // subsequent property set on it). + napi_set_last_error(env, napi_cannot_run_js, NULL, 0, NULL); return napi_cannot_run_js; } if (result) { - CreateScopedResult(env, eval_result, result); + CHECK_NAPI(CreateScopedResult(env, eval_result, result)); + } else { + JS_FreeValue(env->context, eval_result); + } + + return napi_clear_last_error(env); +} + +/* Deserialize and run pre-compiled bytecode (JS_WriteObject output). Mirrors + * qjs_execute_script but reads bytecode instead of compiling source; the + * completion value (the module wrapper function) is returned via *result. */ +napi_status qjs_run_bytecode(napi_env env, + const uint8_t *buf, + size_t buf_len, + const char *file, + napi_value *result) { + CHECK_ARG(env) + CHECK_ARG(buf) + + /* Errors here return napi_pending_exception (not napi_cannot_run_js): the file + * IS bytecode, so the caller must surface the error, not fall back to source. */ + JSValue fun_obj = JS_ReadObject(env->context, buf, buf_len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(fun_obj)) { + // The real error is already the context's pending exception; leave it + // intact. Re-throwing fun_obj (the JS_EXCEPTION sentinel) via JS_Throw + // frees the real error and replaces it with the non-object sentinel. + napi_set_last_error(env, napi_pending_exception, NULL, 0, NULL); + return napi_pending_exception; + } + + /* JS_EvalFunction consumes fun_obj. */ + JSValue eval_result = JS_EvalFunction(env->context, fun_obj); + if (JS_IsException(eval_result)) { + // See above: leave the real pending exception in place instead of + // clobbering it with the eval_result sentinel. + napi_set_last_error(env, napi_pending_exception, NULL, 0, NULL); + return napi_pending_exception; + } + + if (result) { + CHECK_NAPI(CreateScopedResult(env, eval_result, result)); } else { JS_FreeValue(env->context, eval_result); } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/quicks-runtime.h b/test-app/runtime/src/main/cpp/napi/quickjs/quicks-runtime.h index e1d969ca..94a2f484 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/quicks-runtime.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/quicks-runtime.h @@ -8,6 +8,12 @@ EXTERN_C_START +// QuickJS' own runtime handle. This is the engine-internal runtime (defined in +// quickjs-api.c as napi_runtime__ holding the JSRuntime and class IDs) and is +// distinct from the engine-agnostic jsr_ns_runtime used by the jsr layer. The +// jsr adapter (jsr.cpp) wraps this in a jsr_ns_runtime__. +typedef struct napi_runtime__ *napi_runtime; + NAPI_EXTERN napi_status NAPI_CDECL qjs_create_runtime(napi_runtime *runtime); NAPI_EXTERN napi_status NAPI_CDECL qjs_create_napi_env(napi_env *env, napi_runtime runtime); @@ -21,6 +27,12 @@ NAPI_EXTERN napi_status NAPI_CDECL qjs_execute_script(napi_env env, const char *file, napi_value *result); +NAPI_EXTERN napi_status NAPI_CDECL qjs_run_bytecode(napi_env env, + const uint8_t *buf, + size_t buf_len, + const char *file, + napi_value *result); + NAPI_EXTERN napi_status NAPI_CDECL qjs_runtime_before_gc_callback(napi_env env, napi_finalize cb, void *data); NAPI_EXTERN napi_status NAPI_CDECL qjs_runtime_after_gc_callback(napi_env env, napi_finalize cb, void *data); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source b/test-app/runtime/src/main/cpp/napi/quickjs/source new file mode 160000 index 00000000..04be2460 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source @@ -0,0 +1 @@ +Subproject commit 04be246001599f5995fa2f2d8c91a0f198d3f34c diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/.gitignore b/test-app/runtime/src/main/cpp/napi/quickjs/source/.gitignore deleted file mode 100644 index fbeb98f3..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.a -*.orig -*.so -.obj/ -build/ -unicode/ -test262_*.txt -.idea -cmake-* \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/.gitmodules b/test-app/runtime/src/main/cpp/napi/quickjs/source/.gitmodules deleted file mode 100644 index b10f021f..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "test262"] - path = test262 - url = https://github.com/tc39/test262 - shallow = true diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/Changelog b/test-app/runtime/src/main/cpp/napi/quickjs/source/Changelog deleted file mode 100755 index 070b0a77..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/Changelog +++ /dev/null @@ -1,219 +0,0 @@ -- micro optimizations (30% faster on bench-v8) -- added resizable array buffers -- added ArrayBuffer.prototype.transfer -- added the Iterator object and methods -- added set methods -- added Atomics.pause -- added added Map and WeakMap upsert methods -- added Math.sumPrecise() -- misc bug fixes - -2025-09-13: - -- added JSON modules and import attributes -- added JS_PrintValue() API -- qjs: pretty print objects in print() and console.log() -- qjs: better promise rejection tracker heuristics -- added RegExp v flag -- added RegExp modifiers -- added RegExp.escape -- added Float16Array -- added Promise.try -- improved JSON parser spec conformance -- qjs: improved compatibility of std.parseExtJSON() with JSON5 and - accept JSON5 modules -- added JS_FreePropertyEnum() and JS_AtomToCStringLen() API -- added Error.isError() -- misc bug fixes - -2025-04-26: - -- removed the bignum extensions and qjscalc -- new BigInt implementation optimized for small numbers -- added WeakRef, FinalizationRegistry and symbols as weakrefs -- added builtin float64 printing and parsing functions for more correctness -- faster repeated string concatenation -- qjs: promise unhandled rejections are fatal errors by default -- added column number in debug information -- removed the "use strip" extension -- qjs: added -s and --strip-source options -- qjsc: added -s and --keep-source options -- added JS_GetAnyOpaque() -- added more callbacks for exotic objects in JSClassExoticMethods -- misc bug fixes - -2024-01-13: - -- top-level-await support in modules -- allow 'await' in the REPL -- added Array.prototype.{with,toReversed,toSpliced,toSorted} and -TypedArray.prototype.{with,toReversed,toSorted} -- added String.prototype.isWellFormed and String.prototype.toWellFormed -- added Object.groupBy and Map.groupBy -- added Promise.withResolvers -- class static block -- 'in' operator support for private fields -- optional chaining fixes -- added RegExp 'd' flag -- fixed RegExp zero length match logic -- fixed RegExp case insensitive flag -- added os.sleepAsync(), os.getpid() and os.now() -- added cosmopolitan build -- misc bug fixes - -2023-12-09: - -- added Object.hasOwn, {String|Array|TypedArray}.prototype.at, - {Array|TypedArray}.prototype.findLast{Index} -- BigInt support is enabled even if CONFIG_BIGNUM disabled -- updated to Unicode 15.0.0 -- misc bug fixes - -2021-03-27: - -- faster Array.prototype.push and Array.prototype.unshift -- added JS_UpdateStackTop() -- fixed Windows console -- misc bug fixes - -2020-11-08: - -- improved function parameter initializers -- added std.setenv(), std.unsetenv() and std.getenviron() -- added JS_EvalThis() -- misc bug fixes - -2020-09-06: - -- added logical assignment operators -- added IsHTMLDDA support -- faster for-of loops -- os.Worker now takes a module filename as parameter -- qjsc: added -D option to compile dynamically loaded modules or workers -- misc bug fixes - -2020-07-05: - -- modified JS_GetPrototype() to return a live value -- REPL: support unicode characters larger than 16 bits -- added os.Worker -- improved object serialization -- added std.parseExtJSON -- misc bug fixes - -2020-04-12: - -- added cross realm support -- added AggregateError and Promise.any -- added env, uid and gid options in os.exec() -- misc bug fixes - -2020-03-16: - -- reworked error handling in std and os libraries: suppressed I/O - exceptions in std FILE functions and return a positive errno value - when it is explicit -- output exception messages to stderr -- added std.loadFile(), std.strerror(), std.FILE.prototype.tello() -- added JS_GetRuntimeOpaque(), JS_SetRuntimeOpaque(), JS_NewUint32() -- updated to Unicode 13.0.0 -- misc bug fixes - -2020-01-19: - -- keep CONFIG_BIGNUM in the makefile -- added os.chdir() -- qjs: added -I option -- more memory checks in the bignum operations -- modified operator overloading semantics to be closer to the TC39 - proposal -- suppressed "use bigint" mode. Simplified "use math" mode -- BigDecimal: changed suffix from 'd' to 'm' -- misc bug fixes - -2020-01-05: - -- always compile the bignum code. Added '--bignum' option to qjs. -- added BigDecimal -- added String.prototype.replaceAll -- misc bug fixes - -2019-12-21: - -- added nullish coalescing operator (ES2020) -- added optional chaining (ES2020) -- removed recursions in garbage collector -- test stack overflow in the parser -- improved backtrace logic -- added JS_SetHostPromiseRejectionTracker() -- allow exotic constructors -- improved c++ compatibility -- misc bug fixes - -2019-10-27: - -- added example of C class in a module (examples/test_point.js) -- added JS_GetTypedArrayBuffer() -- misc bug fixes - -2019-09-18: - -- added os.exec and other system calls -- exported JS_ValueToAtom() -- qjsc: added 'qjsc_' prefix to the generated C identifiers -- added cross-compilation support -- misc bug fixes - -2019-09-01: - -- added globalThis -- documented JS_EVAL_FLAG_COMPILE_ONLY -- added import.meta.url and import.meta.main -- added 'debugger' statement -- misc bug fixes - -2019-08-18: - -- added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat, - os.readlink, os.readdir, os.utimes, std.popen -- module autodetection -- added import.meta -- misc bug fixes - -2019-08-10: - -- added public class fields and private class fields, methods and - accessors (TC39 proposal) -- changed JS_ToCStringLen() prototype -- qjsc: handle '-' in module names and modules with the same filename -- added std.urlGet -- exported JS_GetOwnPropertyNames() and JS_GetOwnProperty() -- exported some bigint C functions -- added support for eshost in run-test262 -- misc bug fixes - -2019-07-28: - -- added dynamic import -- added Promise.allSettled -- added String.prototype.matchAll -- added Object.fromEntries -- reduced number of ticks in await -- added BigInt support in Atomics -- exported JS_NewPromiseCapability() -- misc async function and async generator fixes -- enabled hashbang support by default - -2019-07-21: - -- updated test262 tests -- updated to Unicode version 12.1.0 -- fixed missing Date object in qjsc -- fixed multi-context creation -- misc ES2020 related fixes -- simplified power and division operators in bignum extension -- fixed several crash conditions - -2019-07-09: - -- first public release diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/LICENSE b/test-app/runtime/src/main/cpp/napi/quickjs/source/LICENSE deleted file mode 100755 index 2cf449dd..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -QuickJS Javascript Engine - -Copyright (c) 2017-2021 Fabrice Bellard -Copyright (c) 2017-2021 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. diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile b/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile deleted file mode 100755 index ba64923d..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile +++ /dev/null @@ -1,550 +0,0 @@ -# -# QuickJS Javascript Engine -# -# Copyright (c) 2017-2021 Fabrice Bellard -# Copyright (c) 2017-2021 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. - -ifeq ($(shell uname -s),Darwin) -CONFIG_DARWIN=y -endif -ifeq ($(shell uname -s),FreeBSD) -CONFIG_FREEBSD=y -endif -# Windows cross compilation from Linux -# May need to have libwinpthread*.dll alongside the executable -# (On Ubuntu/Debian may be installed with mingw-w64-x86-64-dev -# to /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll) -#CONFIG_WIN32=y -# use link time optimization (smaller and faster executables but slower build) -#CONFIG_LTO=y -# consider warnings as errors (for development) -#CONFIG_WERROR=y -# force 32 bit build on x86_64 -#CONFIG_M32=y -# cosmopolitan build (see https://github.com/jart/cosmopolitan) -#CONFIG_COSMO=y - -# installation directory -PREFIX?=/usr/local - -# use the gprof profiler -#CONFIG_PROFILE=y -# use address sanitizer -#CONFIG_ASAN=y -# use memory sanitizer -#CONFIG_MSAN=y -# use UB sanitizer -#CONFIG_UBSAN=y - -# TEST262 bootstrap config: commit id and shallow "since" parameter -TEST262_COMMIT?=42303c7c2bcf1c1edb9e5375c291c6fbc8a261ab -TEST262_SINCE?=2025-09-01 - -OBJDIR=.obj - -ifdef CONFIG_ASAN -OBJDIR:=$(OBJDIR)/asan -endif -ifdef CONFIG_MSAN -OBJDIR:=$(OBJDIR)/msan -endif -ifdef CONFIG_UBSAN -OBJDIR:=$(OBJDIR)/ubsan -endif - -ifdef CONFIG_DARWIN -# use clang instead of gcc -CONFIG_CLANG=y -CONFIG_DEFAULT_AR=y -endif -ifdef CONFIG_FREEBSD -# use clang instead of gcc -CONFIG_CLANG=y -CONFIG_DEFAULT_AR=y -CONFIG_LTO= -endif - -ifdef CONFIG_WIN32 - ifdef CONFIG_M32 - CROSS_PREFIX?=i686-w64-mingw32- - else - CROSS_PREFIX?=x86_64-w64-mingw32- - endif - EXE=.exe -else ifdef MSYSTEM - CONFIG_WIN32=y - CROSS_PREFIX?= - EXE=.exe -else - CROSS_PREFIX?= - EXE= -endif - -ifdef CONFIG_CLANG - HOST_CC=clang - CC=$(CROSS_PREFIX)clang - CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d - CFLAGS += -Wextra - CFLAGS += -Wno-sign-compare - CFLAGS += -Wno-missing-field-initializers - CFLAGS += -Wundef -Wuninitialized - CFLAGS += -Wunused -Wno-unused-parameter - CFLAGS += -Wwrite-strings - CFLAGS += -Wchar-subscripts -funsigned-char - CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d - ifdef CONFIG_DEFAULT_AR - AR=$(CROSS_PREFIX)ar - else - ifdef CONFIG_LTO - AR=$(CROSS_PREFIX)llvm-ar - else - AR=$(CROSS_PREFIX)ar - endif - endif - LIB_FUZZING_ENGINE ?= "-fsanitize=fuzzer" -else ifdef CONFIG_COSMO - CONFIG_LTO= - HOST_CC=gcc - CC=cosmocc - # cosmocc does not correct support -MF - CFLAGS=-g -Wall #-MMD -MF $(OBJDIR)/$(@F).d - CFLAGS += -Wno-array-bounds -Wno-format-truncation - AR=cosmoar -else - HOST_CC=gcc - CC=$(CROSS_PREFIX)gcc - CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d - CFLAGS += -Wno-array-bounds -Wno-format-truncation -Wno-infinite-recursion - ifdef CONFIG_LTO - AR=$(CROSS_PREFIX)gcc-ar - else - AR=$(CROSS_PREFIX)ar - endif -endif -STRIP?=$(CROSS_PREFIX)strip -ifdef CONFIG_M32 -CFLAGS+=-msse2 -mfpmath=sse # use SSE math for correct FP rounding -ifndef CONFIG_WIN32 -CFLAGS+=-m32 -LDFLAGS+=-m32 -endif -endif -CFLAGS+=-fwrapv # ensure that signed overflows behave as expected -ifdef CONFIG_WERROR -CFLAGS+=-Werror -endif -DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" -ifdef CONFIG_WIN32 -DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior -endif -ifndef CONFIG_WIN32 -ifeq ($(shell $(CC) -o /dev/null compat/test-closefrom.c 2>/dev/null && echo 1),1) -DEFINES+=-DHAVE_CLOSEFROM -endif -endif - -CFLAGS+=$(DEFINES) -CFLAGS_DEBUG=$(CFLAGS) -O0 -CFLAGS_SMALL=$(CFLAGS) -Os -CFLAGS_OPT=$(CFLAGS) -O2 -CFLAGS_NOLTO:=$(CFLAGS_OPT) -ifdef CONFIG_COSMO -LDFLAGS+=-s # better to strip by default -else -LDFLAGS+=-g -endif -ifdef CONFIG_LTO -CFLAGS_SMALL+=-flto -CFLAGS_OPT+=-flto -LDFLAGS+=-flto -endif -ifdef CONFIG_PROFILE -CFLAGS+=-p -LDFLAGS+=-p -endif -ifdef CONFIG_ASAN -CFLAGS+=-fsanitize=address -fno-omit-frame-pointer -LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -endif -ifdef CONFIG_MSAN -CFLAGS+=-fsanitize=memory -fno-omit-frame-pointer -LDFLAGS+=-fsanitize=memory -fno-omit-frame-pointer -endif -ifdef CONFIG_UBSAN -CFLAGS+=-fsanitize=undefined -fno-omit-frame-pointer -LDFLAGS+=-fsanitize=undefined -fno-omit-frame-pointer -endif -ifdef CONFIG_WIN32 -LDEXPORT= -else -LDEXPORT=-rdynamic -endif - -ifndef CONFIG_COSMO -ifndef CONFIG_DARWIN -ifndef CONFIG_WIN32 -CONFIG_SHARED_LIBS=y # building shared libraries is supported -endif -endif -endif - -PROGS=qjs$(EXE) qjsc$(EXE) run-test262$(EXE) - -ifneq ($(CROSS_PREFIX),) -QJSC_CC=gcc -QJSC=./host-qjsc -PROGS+=$(QJSC) -else -QJSC_CC=$(CC) -QJSC=./qjsc$(EXE) -endif -PROGS+=libquickjs.a -ifdef CONFIG_LTO -PROGS+=libquickjs.lto.a -endif - -# examples -ifeq ($(CROSS_PREFIX),) -ifndef CONFIG_ASAN -ifndef CONFIG_MSAN -ifndef CONFIG_UBSAN -PROGS+=examples/hello examples/test_fib -# no -m32 option in qjsc -ifndef CONFIG_M32 -ifndef CONFIG_WIN32 -PROGS+=examples/hello_module -endif -endif -ifdef CONFIG_SHARED_LIBS -PROGS+=examples/fib.so examples/point.so -endif -endif -endif -endif -endif - -all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) - -QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/dtoa.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o - -QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) - -HOST_LIBS=-lm -ldl -lpthread -LIBS=-lm -lpthread -ifndef CONFIG_WIN32 -LIBS+=-ldl -endif -LIBS+=$(EXTRA_LIBS) - -$(OBJDIR): - mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests - -qjs$(EXE): $(QJS_OBJS) - $(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) - -qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS)) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -fuzz_eval: $(OBJDIR)/fuzz_eval.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a - $(CC) $(CFLAGS_OPT) $^ -o fuzz_eval $(LIB_FUZZING_ENGINE) - -fuzz_compile: $(OBJDIR)/fuzz_compile.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a - $(CC) $(CFLAGS_OPT) $^ -o fuzz_compile $(LIB_FUZZING_ENGINE) - -fuzz_regexp: $(OBJDIR)/fuzz_regexp.o $(OBJDIR)/libregexp.fuzz.o $(OBJDIR)/cutils.fuzz.o $(OBJDIR)/libunicode.fuzz.o - $(CC) $(CFLAGS_OPT) $^ -o fuzz_regexp $(LIB_FUZZING_ENGINE) - -libfuzzer: fuzz_eval fuzz_compile fuzz_regexp - -ifneq ($(CROSS_PREFIX),) - -$(QJSC): $(OBJDIR)/qjsc.host.o \ - $(patsubst %.o, %.host.o, $(QJS_LIB_OBJS)) - $(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS) - -endif #CROSS_PREFIX - -QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\" -ifdef CONFIG_LTO -QJSC_DEFINES+=-DCONFIG_LTO -endif -QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\" - -$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES) -$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES) - -ifdef CONFIG_LTO -LTOEXT=.lto -else -LTOEXT= -endif - -libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS) - $(AR) rcs $@ $^ - -ifdef CONFIG_LTO -libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) - $(AR) rcs $@ $^ -endif # CONFIG_LTO - -libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS)) - $(AR) rcs $@ $^ - -repl.c: $(QJSC) repl.js - $(QJSC) -s -c -o $@ -m repl.js - -ifneq ($(wildcard unicode/UnicodeData.txt),) -$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.nolto.o: libunicode-table.h - -libunicode-table.h: unicode_gen - ./unicode_gen unicode $@ -endif - -run-test262$(EXE): $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -# object suffix order: nolto - -$(OBJDIR)/%.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -c -o $@ $< - -$(OBJDIR)/fuzz_%.o: fuzz/fuzz_%.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -c -I. -o $@ $< - -$(OBJDIR)/%.host.o: %.c | $(OBJDIR) - $(HOST_CC) $(CFLAGS_OPT) -c -o $@ $< - -$(OBJDIR)/%.pic.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $< - -$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_NOLTO) -c -o $@ $< - -$(OBJDIR)/%.debug.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_DEBUG) -c -o $@ $< - -$(OBJDIR)/%.fuzz.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -fsanitize=fuzzer-no-link -c -o $@ $< - -$(OBJDIR)/%.check.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $< - -regexp_test: libregexp.c libunicode.c cutils.c - $(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS) - -unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h - $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o - -clean: - rm -f repl.c out.c - rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS) - rm -f hello.c test_fib.c - rm -f examples/*.so tests/*.so - rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug$(EXE) - rm -rf run-test262-debug$(EXE) - rm -f run_octane run_sunspider_like - -install: all - mkdir -p "$(DESTDIR)$(PREFIX)/bin" - $(STRIP) qjs$(EXE) qjsc$(EXE) - install -m755 qjs$(EXE) qjsc$(EXE) "$(DESTDIR)$(PREFIX)/bin" - mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs" - install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs" -ifdef CONFIG_LTO - install -m644 libquickjs.lto.a "$(DESTDIR)$(PREFIX)/lib/quickjs" -endif - mkdir -p "$(DESTDIR)$(PREFIX)/include/quickjs" - install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(PREFIX)/include/quickjs" - -############################################################################### -# examples - -# example of static JS compilation -HELLO_SRCS=examples/hello.js -HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ - -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ - -fno-date -fno-module-loader - -hello.c: $(QJSC) $(HELLO_SRCS) - $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) - -examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -# example of static JS compilation with modules -HELLO_MODULE_SRCS=examples/hello_module.js -HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-typedarray \ - -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ - -fno-date -m -examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) - $(QJSC) $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS) - -# use of an external C module (static compilation) - -test_fib.c: $(QJSC) examples/test_fib.js - $(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js - -examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -examples/fib.so: $(OBJDIR)/examples/fib.pic.o - $(CC) $(LDFLAGS) -shared -o $@ $^ - -examples/point.so: $(OBJDIR)/examples/point.pic.o - $(CC) $(LDFLAGS) -shared -o $@ $^ - -############################################################################### -# documentation - -DOCS=doc/quickjs.pdf doc/quickjs.html - -build_doc: $(DOCS) - -clean_doc: - rm -f $(DOCS) - -doc/version.texi: VERSION - @echo "@set VERSION `cat $<`" > $@ - -doc/%.pdf: doc/%.texi doc/version.texi - texi2pdf --clean -o $@ -q $< - -doc/%.html.pre: doc/%.texi doc/version.texi - makeinfo --html --no-headers --no-split --number-sections -o $@ $< - -doc/%.html: doc/%.html.pre - sed -e 's||\n|' < $< > $@ - -############################################################################### -# tests - -ifdef CONFIG_SHARED_LIBS -test: tests/bjson.so examples/point.so -endif - -test: qjs$(EXE) - $(WINE) ./qjs$(EXE) tests/test_closure.js - $(WINE) ./qjs$(EXE) tests/test_language.js - $(WINE) ./qjs$(EXE) --std tests/test_builtin.js - $(WINE) ./qjs$(EXE) tests/test_loop.js - $(WINE) ./qjs$(EXE) tests/test_bigint.js - $(WINE) ./qjs$(EXE) tests/test_cyclic_import.js - $(WINE) ./qjs$(EXE) tests/test_worker.js -ifndef CONFIG_WIN32 - $(WINE) ./qjs$(EXE) tests/test_std.js -endif -ifdef CONFIG_SHARED_LIBS - $(WINE) ./qjs$(EXE) tests/test_bjson.js - $(WINE) ./qjs$(EXE) examples/test_point.js -endif - -stats: qjs$(EXE) - $(WINE) ./qjs$(EXE) -qd - -microbench: qjs$(EXE) - $(WINE) ./qjs$(EXE) --std tests/microbench.js - -ifeq ($(wildcard test262/features.txt),) -test2-bootstrap: - git clone --single-branch --shallow-since=$(TEST262_SINCE) https://github.com/tc39/test262.git - (cd test262 && git checkout -q $(TEST262_COMMIT) && patch -p1 < ../tests/test262.patch && cd ..) -else -test2-bootstrap: - (cd test262 && git fetch && git reset --hard $(TEST262_COMMIT) && patch -p1 < ../tests/test262.patch && cd ..) -endif - -ifeq ($(wildcard test262o/tests.txt),) -test2o test2o-update: - @echo test262o tests not installed -else -# ES5 tests (obsolete) -test2o: run-test262 - time ./run-test262 -t -m -c test262o.conf - -test2o-update: run-test262 - ./run-test262 -t -u -c test262o.conf -endif - -ifeq ($(wildcard test262/features.txt),) -test2 test2-update test2-default test2-check: - @echo test262 tests not installed -else -# Test262 tests -test2-default: run-test262 - time ./run-test262 -t -m -c test262.conf - -test2: run-test262 - time ./run-test262 -t -m -c test262.conf -a - -test2-update: run-test262 - ./run-test262 -t -u -c test262.conf -a - -test2-check: run-test262 - time ./run-test262 -t -m -c test262.conf -E -a -endif - -testall: all test microbench test2o test2 - -testall-complete: testall - -node-test: - node tests/test_closure.js - node tests/test_language.js - node tests/test_builtin.js - node tests/test_loop.js - node tests/test_bigint.js - -node-microbench: - node tests/microbench.js -s microbench-node.txt - node --jitless tests/microbench.js -s microbench-node-jitless.txt - -bench-v8: qjs - make -C tests/bench-v8 - ./qjs -d tests/bench-v8/combined.js - -node-bench-v8: - make -C tests/bench-v8 - node --jitless tests/bench-v8/combined.js - -tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o - $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) - -BENCHMARKDIR=../quickjs-benchmarks - -run_sunspider_like: $(BENCHMARKDIR)/run_sunspider_like.c - $(CC) $(CFLAGS) $(LDFLAGS) -DNO_INCLUDE_DIR -I. -o $@ $< libquickjs$(LTOEXT).a $(LIBS) - -run_octane: $(BENCHMARKDIR)/run_octane.c - $(CC) $(CFLAGS) $(LDFLAGS) -DNO_INCLUDE_DIR -I. -o $@ $< libquickjs$(LTOEXT).a $(LIBS) - -benchmarks: run_sunspider_like run_octane - ./run_sunspider_like $(BENCHMARKDIR)/kraken-1.0/ - ./run_sunspider_like $(BENCHMARKDIR)/kraken-1.1/ - ./run_sunspider_like $(BENCHMARKDIR)/sunspider-1.0/ - ./run_octane $(BENCHMARKDIR)/ - --include $(wildcard $(OBJDIR)/*.d) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/TODO b/test-app/runtime/src/main/cpp/napi/quickjs/source/TODO deleted file mode 100755 index e52ccd75..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/TODO +++ /dev/null @@ -1,66 +0,0 @@ -Misc ideas: -- use custom printf to avoid compatibility issues with floating point numbers -- consistent naming for preprocessor defines -- unify coding style and naming conventions -- use names from the ECMA spec in library implementation -- use byte code emitters with typed arguments (for clarity) -- use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing - and use the same wrappers in all phases -- use more generic method for line numbers in resolve_variables and resolve_labels -- use custom timezone support to avoid C library compatibility issues - -Memory: -- use memory pools for objects, etc? -- test border cases for max number of atoms, object properties, string length -- add emergency malloc mode for out of memory exceptions. -- test all DynBuf memory errors -- test all js_realloc memory errors -- improve JS_ComputeMemoryUsage() with more info - -Built-in standard library: -- BSD sockets -- modules: use realpath in module name normalizer and put it in quickjs-libc -- modules: if no ".", use a well known module loading path ? -- get rid of __loadScript, use more common name - -REPL: -- debugger -- readline: support MS Windows terminal -- readline: handle dynamic terminal resizing -- readline: handle double width unicode characters -- multiline editing -- runtime object and function inspectors -- interactive object browser -- use more generic approach to display evaluation results -- improve directive handling: dispatch, colorize, completion... -- save history -- close all predefined methods in repl.js and jscalc.js - -Optimization ideas: -- use 64 bit JSValue in 64 bit mode -- use JSValue as atoms and use a specific constant pool in functions to - reference atoms from the bytecode -- reuse stack slots for disjoint scopes, if strip -- add heuristic to avoid some cycles in closures -- small String (1 codepoint) with immediate storage -- perform static string concatenation at compile time -- add implicit numeric strings for Uint32 numbers? -- optimize `s += a + b`, `s += a.b` and similar simple expressions -- ensure string canonical representation and optimise comparisons and hashes? -- property access optimization on the global object, functions, - prototypes and special non extensible objects. -- create object literals with the correct length by backpatching length argument -- remove redundant set_loc_uninitialized/check_uninitialized opcodes -- peephole optim: push_atom_value, to_propkey -> push_atom_value -- peephole optim: put_loc x, get_loc_check x -> set_loc x -- convert slow array to fast array when all properties != length are numeric -- optimize destructuring assignments for global and local variables -- implement some form of tail-call-optimization -- optimize OP_apply -- optimize f(...b) - -Test262o: 0/11262 errors, 463 excluded -Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) - -Test262: -Result: 66/83147 errors, 1646 excluded, 5538 skipped diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/compat/test-closefrom.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/compat/test-closefrom.c deleted file mode 100755 index 1324e97b..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/compat/test-closefrom.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(void) { - closefrom(3); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c deleted file mode 100755 index 52ff1649..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c +++ /dev/null @@ -1,641 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include - -#include "cutils.h" - -void 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. */ -char *pstrcat(char *buf, int buf_size, const char *s) -{ - int len; - len = strlen(buf); - if (len < buf_size) - pstrcpy(buf + len, buf_size - len, s); - return buf; -} - -int 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; -} - -int 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) -{ - return realloc(ptr, size); -} - -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; -} - -void dbuf_init(DynBuf *s) -{ - dbuf_init2(s, NULL, NULL); -} - -/* Try to allocate 'len' more bytes. return < 0 if error */ -int dbuf_claim(DynBuf *s, size_t len) -{ - size_t new_size, size; - uint8_t *new_buf; - new_size = s->size + len; - if (new_size < len) - return -1; /* overflow case */ - if (new_size > s->allocated_size) { - if (s->error) - return -1; - size = s->allocated_size + (s->allocated_size / 2); - if (size < s->allocated_size) - return -1; /* overflow case */ - if (size > new_size) - new_size = size; - new_buf = s->realloc_func(s->opaque, s->buf, new_size); - if (!new_buf) { - s->error = TRUE; - return -1; - } - s->buf = new_buf; - s->allocated_size = new_size; - } - return 0; -} - -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) -{ - if (unlikely((s->allocated_size - s->size) < len)) { - if (dbuf_claim(s, len)) - return -1; - } - memcpy_no_ub(s->buf + s->size, data, len); - s->size += len; - return 0; -} - -int dbuf_put_self(DynBuf *s, size_t offset, size_t len) -{ - if (unlikely((s->allocated_size - s->size) < len)) { - if (dbuf_claim(s, len)) - return -1; - } - memcpy(s->buf + s->size, s->buf + offset, len); - s->size += len; - return 0; -} - -int __dbuf_putc(DynBuf *s, uint8_t c) -{ - return dbuf_put(s, &c, 1); -} - -int __dbuf_put_u16(DynBuf *s, uint16_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 2); -} - -int __dbuf_put_u32(DynBuf *s, uint32_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 4); -} - -int __dbuf_put_u64(DynBuf *s, uint64_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 8); -} - -int dbuf_putstr(DynBuf *s, const char *str) -{ - return dbuf_put(s, (const uint8_t *)str, strlen(str)); -} - -int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, - 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 < 0) - return -1; - if (len < 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; -} - -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)); -} - -/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes - are output. */ -int unicode_to_utf8(uint8_t *buf, unsigned int c) -{ - uint8_t *q = buf; - - if (c < 0x80) { - *q++ = c; - } else { - if (c < 0x800) { - *q++ = (c >> 6) | 0xc0; - } else { - if (c < 0x10000) { - *q++ = (c >> 12) | 0xe0; - } else { - if (c < 0x00200000) { - *q++ = (c >> 18) | 0xf0; - } else { - if (c < 0x04000000) { - *q++ = (c >> 24) | 0xf8; - } else if (c < 0x80000000) { - *q++ = (c >> 30) | 0xfc; - *q++ = ((c >> 24) & 0x3f) | 0x80; - } else { - return 0; - } - *q++ = ((c >> 18) & 0x3f) | 0x80; - } - *q++ = ((c >> 12) & 0x3f) | 0x80; - } - *q++ = ((c >> 6) & 0x3f) | 0x80; - } - *q++ = (c & 0x3f) | 0x80; - } - return q - buf; -} - -static const unsigned int utf8_min_code[5] = { - 0x80, 0x800, 0x10000, 0x00200000, 0x04000000, -}; - -static const unsigned char utf8_first_code_mask[5] = { - 0x1f, 0xf, 0x7, 0x3, 0x1, -}; - -/* return -1 if error. *pp is not updated in this case. max_len must - be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */ -int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp) -{ - int l, c, b, i; - - c = *p++; - if (c < 0x80) { - *pp = p; - return c; - } - switch(c) { - case 0xc0: case 0xc1: 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: - l = 1; - break; - case 0xe0: 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: - l = 2; - break; - case 0xf0: case 0xf1: case 0xf2: case 0xf3: - case 0xf4: case 0xf5: case 0xf6: case 0xf7: - l = 3; - break; - case 0xf8: case 0xf9: case 0xfa: case 0xfb: - l = 4; - break; - case 0xfc: case 0xfd: - l = 5; - break; - default: - return -1; - } - /* check that we have enough characters */ - if (l > (max_len - 1)) - return -1; - c &= utf8_first_code_mask[l - 1]; - for(i = 0; i < l; i++) { - b = *p++; - if (b < 0x80 || b >= 0xc0) - return -1; - c = (c << 6) | (b & 0x3f); - } - if (c < utf8_min_code[l - 1]) - return -1; - *pp = p; - return c; -} - -#if 0 - -#if defined(EMSCRIPTEN) || defined(__ANDROID__) - -static void *rqsort_arg; -static int (*rqsort_cmp)(const void *, const void *, void *); - -static int rqsort_cmp2(const void *p1, const void *p2) -{ - return rqsort_cmp(p1, p2, rqsort_arg); -} - -/* not reentrant, but not needed with emscripten */ -void rqsort(void *base, size_t nmemb, size_t size, - int (*cmp)(const void *, const void *, void *), - void *arg) -{ - rqsort_arg = arg; - rqsort_cmp = cmp; - qsort(base, nmemb, size, rqsort_cmp2); -} - -#endif - -#else - -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 */ -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); - } - } -} - -#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h deleted file mode 100755 index 094a8f12..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h +++ /dev/null @@ -1,457 +0,0 @@ -/* - * 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 - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#define force_inline inline __attribute__((always_inline)) -#define no_inline __attribute__((noinline)) -#define __maybe_unused __attribute__((unused)) - -#define xglue(x, y) x ## y -#define glue(x, y) xglue(x, y) -#define stringify(s) tostring(s) -#define tostring(s) #s - -#ifndef offsetof -#define offsetof(type, field) ((size_t) &((type *)0)->field) -#endif -#ifndef countof -#define countof(x) (sizeof(x) / sizeof((x)[0])) -#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(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#define minimum_length(n) static n -#else -#define minimum_length(n) n -#endif - -typedef int BOOL; - -#ifndef FALSE -enum { - FALSE = 0, - TRUE = 1, -}; -#endif - -void pstrcpy(char *buf, int buf_size, const char *str); -char *pstrcat(char *buf, int buf_size, const char *s); -int strstart(const char *str, const char *val, const char **ptr); -int has_suffix(const char *str, const char *suffix); - -/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */ -static inline void memcpy_no_ub(void *dest, const void *src, size_t n) { - if (n) - memcpy(dest, src, n); -} - -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) -{ - return __builtin_clz(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int clz64(uint64_t a) -{ - return __builtin_clzll(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int ctz32(unsigned int a) -{ - return __builtin_ctz(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int ctz64(uint64_t a) -{ - return __builtin_ctzll(a); -} - -struct __attribute__((packed)) packed_u64 { - uint64_t v; -}; - -struct __attribute__((packed)) packed_u32 { - uint32_t v; -}; - -struct __attribute__((packed)) packed_u16 { - uint16_t v; -}; - -static inline uint64_t get_u64(const uint8_t *tab) -{ - return ((const struct packed_u64 *)tab)->v; -} - -static inline int64_t get_i64(const uint8_t *tab) -{ - return (int64_t)((const struct packed_u64 *)tab)->v; -} - -static inline void put_u64(uint8_t *tab, uint64_t val) -{ - ((struct packed_u64 *)tab)->v = val; -} - -static inline uint32_t get_u32(const uint8_t *tab) -{ - return ((const struct packed_u32 *)tab)->v; -} - -static inline int32_t get_i32(const uint8_t *tab) -{ - return (int32_t)((const struct packed_u32 *)tab)->v; -} - -static inline void put_u32(uint8_t *tab, uint32_t val) -{ - ((struct packed_u32 *)tab)->v = val; -} - -static inline uint32_t get_u16(const uint8_t *tab) -{ - return ((const struct packed_u16 *)tab)->v; -} - -static inline int32_t get_i16(const uint8_t *tab) -{ - return (int16_t)((const struct packed_u16 *)tab)->v; -} - -static inline void put_u16(uint8_t *tab, uint16_t val) -{ - ((struct packed_u16 *)tab)->v = 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 - -/* 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; - -void dbuf_init(DynBuf *s); -void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); -int dbuf_claim(DynBuf *s, size_t len); -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); -int dbuf_put_self(DynBuf *s, size_t offset, size_t len); -int dbuf_putstr(DynBuf *s, const char *str); -int __dbuf_putc(DynBuf *s, uint8_t c); -int __dbuf_put_u16(DynBuf *s, uint16_t val); -int __dbuf_put_u32(DynBuf *s, uint32_t val); -int __dbuf_put_u64(DynBuf *s, uint64_t val); - -static inline int dbuf_putc(DynBuf *s, uint8_t val) -{ - if (unlikely((s->allocated_size - s->size) < 1)) { - return __dbuf_putc(s, val); - } else { - 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); - } else { - 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); - } else { - 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); - } else { - put_u64(s->buf + s->size, val); - s->size += 8; - return 0; - } -} - -int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, - const char *fmt, ...); -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; -} - -#define UTF8_CHAR_LEN_MAX 6 - -int unicode_to_utf8(uint8_t *buf, unsigned int c); -int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); - -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 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00); -} - -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; -} - -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 double fromfp16(uint16_t v) -{ - double d; - uint32_t v1; - v1 = v & 0x7fff; - if (unlikely(v1 >= 0x7c00)) - v1 += 0x1f8000; /* NaN or infinity */ - d = uint64_as_float64(((uint64_t)(v >> 15) << 63) | ((uint64_t)v1 << (52 - 10))); - return d * 0x1p1008; -} - -static inline uint16_t tofp16(double d) -{ - uint64_t a, addend; - uint32_t v, sgn; - int shift; - - a = float64_as_uint64(d); - sgn = a >> 63; - a = a & 0x7fffffffffffffff; - if (unlikely(a > 0x7ff0000000000000)) { - /* nan */ - v = 0x7c01; - } else if (a < 0x3f10000000000000) { /* 0x1p-14 */ - /* subnormal f16 number or zero */ - if (a <= 0x3e60000000000000) { /* 0x1p-25 */ - v = 0x0000; /* zero */ - } else { - shift = 1051 - (a >> 52); - a = ((uint64_t)1 << 52) | (a & (((uint64_t)1 << 52) - 1)); - addend = ((a >> shift) & 1) + (((uint64_t)1 << (shift - 1)) - 1); - v = (a + addend) >> shift; - } - } else { - /* normal number or infinity */ - a -= 0x3f00000000000000; /* adjust the exponent */ - /* round */ - addend = ((a >> (52 - 10)) & 1) + (((uint64_t)1 << (52 - 11)) - 1); - v = (a + addend) >> (52 - 10); - /* overflow ? */ - if (unlikely(v > 0x7c00)) - v = 0x7c00; - } - return v | (sgn << 15); -} - -static inline int isfp16nan(uint16_t v) -{ - return (v & 0x7FFF) > 0x7C00; -} - -static inline int isfp16zero(uint16_t v) -{ - return (v & 0x7FFF) == 0; -} - -#endif /* CUTILS_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/doc/quickjs.texi b/test-app/runtime/src/main/cpp/napi/quickjs/source/doc/quickjs.texi deleted file mode 100755 index 0b8c744e..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/doc/quickjs.texi +++ /dev/null @@ -1,1073 +0,0 @@ -\input texinfo - -@iftex -@afourpaper -@headings double -@end iftex - -@include version.texi - -@titlepage -@afourpaper -@sp 7 -@center @titlefont{QuickJS Javascript Engine} -@sp 3 -@center Version: @value{VERSION} -@end titlepage - -@setfilename spec.info -@settitle QuickJS Javascript Engine - -@contents - -@chapter Introduction - -QuickJS (version @value{VERSION}) is a small and embeddable Javascript -engine. It supports most of the ES2024 specification -@footnote{@url{https://tc39.es/ecma262/2024 }} including modules, -asynchronous generators, proxies and BigInt. - -@section Main Features - -@itemize - -@item Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple ``hello world'' program. - -@item Fast interpreter with very low startup time: runs the 77000 tests of the ECMAScript Test Suite@footnote{@url{https://github.com/tc39/test262}} in less than 2 minutes on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds. - -@item Almost complete ES2024 support including modules, asynchronous -generators and full Annex B support (legacy web compatibility). Some -features from the upcoming ES2024 specification -@footnote{@url{https://tc39.es/ecma262/}} are also supported. - -@item Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2024 features. - -@item Compile Javascript sources to executables with no external dependency. - -@item Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal. - -@item Command line interpreter with contextual colorization and completion implemented in Javascript. - -@item Small built-in standard library with C library wrappers. - -@end itemize - -@chapter Usage - -@section Installation - -A Makefile is provided to compile the engine on Linux or MacOS/X. A -preliminary Windows support is available thru cross compilation on a -Linux host with the MingGW tools. - -Edit the top of the @code{Makefile} if you wish to select specific -options then run @code{make}. - -You can type @code{make install} as root if you wish to install the binaries and support files to -@code{/usr/local} (this is not necessary to use QuickJS). - -Note: On some OSes atomic operations are not available or need a -specific library. If you get related errors, you should either add -@code{-latomics} in the Makefile @code{LIBS} variable or disable -@code{CONFIG_ATOMICS} in @file{quickjs.c}. - -@section Quick start - -@code{qjs} is the command line interpreter (Read-Eval-Print Loop). You can pass -Javascript files and/or expressions as arguments to execute them: - -@example -./qjs examples/hello.js -@end example - -@code{qjsc} is the command line compiler: - -@example -./qjsc -o hello examples/hello.js -./hello -@end example - -generates a @code{hello} executable with no external dependency. - -@section Command line options - -@subsection @code{qjs} interpreter - -@verbatim -usage: qjs [options] [file [args]] -@end verbatim - -Options are: -@table @code -@item -h -@item --help -List options. - -@item -e @code{EXPR} -@item --eval @code{EXPR} -Evaluate EXPR. - -@item -i -@item --interactive -Go to interactive mode (it is not the default when files are provided on the command line). - -@item -m -@item --module -Load as ES6 module (default=autodetect). A module is autodetected if -the filename extension is @code{.mjs} or if the first keyword of the -source is @code{import}. - -@item --script -Load as ES6 script (default=autodetect). - -@item -I file -@item --include file -Include an additional file. - -@end table - -Advanced options are: - -@table @code -@item --std -Make the @code{std} and @code{os} modules available to the loaded -script even if it is not a module. - -@item -d -@item --dump -Dump the memory usage stats. - -@item -q -@item --quit -just instantiate the interpreter and quit. - -@end table - -@subsection @code{qjsc} compiler - -@verbatim -usage: qjsc [options] [files] -@end verbatim - -Options are: -@table @code -@item -c -Only output bytecode in a C file. The default is to output an executable file. -@item -e -Output @code{main()} and bytecode in a C file. The default is to output an -executable file. -@item -o output -Set the output filename (default = @file{out.c} or @file{a.out}). - -@item -N cname -Set the C name of the generated data. - -@item -m -Compile as Javascript module (default=autodetect). - -@item -D module_name -Compile a dynamically loaded module and its dependencies. This option -is needed when your code uses the @code{import} keyword or the -@code{os.Worker} constructor because the compiler cannot statically -find the name of the dynamically loaded modules. - -@item -M module_name[,cname] -Add initialization code for an external C module. See the -@code{c_module} example. - -@item -x -Byte swapped output (only used for cross compilation). - -@item -flto -Use link time optimization. The compilation is slower but the -executable is smaller and faster. This option is automatically set -when the @code{-fno-x} options are used. - -@item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint] -Disable selected language features to produce a smaller executable file. - -@end table - -@section Built-in tests - -Run @code{make test} to run the few built-in tests included in the -QuickJS archive. - -@section Test262 (ECMAScript Test Suite) - -A test262 runner is included in the QuickJS archive. The test262 tests -can be installed in the QuickJS source directory with: - -@example -git clone https://github.com/tc39/test262.git test262 -cd test262 -patch -p1 < ../tests/test262.patch -cd .. -@end example - -The patch adds the implementation specific @code{harness} functions -and optimizes the inefficient RegExp character classes and Unicode -property escapes tests (the tests themselves are not modified, only a -slow string initialization function is optimized). - -The tests can be run with -@example -make test2 -@end example - -The configuration files @code{test262.conf} -(resp. @code{test262o.conf} for the old ES5.1 tests@footnote{The old -ES5.1 tests can be extracted with @code{git clone --single-branch ---branch es5-tests https://github.com/tc39/test262.git test262o}})) -contain the options to run the various tests. Tests can be excluded -based on features or filename. - -The file @code{test262_errors.txt} contains the current list of -errors. The runner displays a message when a new error appears or when -an existing error is corrected or modified. Use the @code{-u} option -to update the current list of errors (or @code{make test2-update}). - -The file @code{test262_report.txt} contains the logs of all the -tests. It is useful to have a clearer analysis of a particular -error. In case of crash, the last line corresponds to the failing -test. - -Use the syntax @code{./run-test262 -c test262.conf -f filename.js} to -run a single test. Use the syntax @code{./run-test262 -c test262.conf -N} to start testing at test number @code{N}. - -For more information, run @code{./run-test262} to see the command line -options of the test262 runner. - -@code{run-test262} accepts the @code{-N} option to be invoked from -@code{test262-harness}@footnote{@url{https://github.com/bterlson/test262-harness}} -thru @code{eshost}. Unless you want to compare QuickJS with other -engines under the same conditions, we do not recommend to run the -tests this way as it is much slower (typically half an hour instead of -about 100 seconds). - -@chapter Specifications - -@section Language support - -@subsection ES2024 support - -The ES2024 specification is almost fully supported including the Annex -B (legacy web compatibility) and the Unicode related features. - -The following features are not supported yet: - -@itemize - -@item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.} - -@item Atomics.waitAsync - -@end itemize - -@subsection ECMA402 - -ECMA402 (Internationalization API) is not supported. - -@section Modules - -ES6 modules are fully supported. The default name resolution is the -following: - -@itemize - -@item Module names with a leading @code{.} or @code{..} are relative -to the current module path. - -@item Module names without a leading @code{.} or @code{..} are system -modules, such as @code{std} or @code{os}. - -@item Module names ending with @code{.so} are native modules using the -QuickJS C API. - -@end itemize - -@section Standard library - -The standard library is included by default in the command line -interpreter. It contains the two modules @code{std} and @code{os} and -a few global objects. - -@subsection Global objects - -@table @code -@item scriptArgs -Provides the command line arguments. The first argument is the script name. -@item print(...args) -Print the arguments separated by spaces and a trailing newline. -@item console.log(...args) -Same as print(). - -@end table - -@subsection @code{std} module - -The @code{std} module provides wrappers to the libc @file{stdlib.h} -and @file{stdio.h} and a few other utilities. - -Available exports: - -@table @code - -@item exit(n) -Exit the process. - -@item evalScript(str, options = undefined) -Evaluate the string @code{str} as a script (global -eval). @code{options} is an optional object containing the following -optional properties: - - @table @code - @item backtrace_barrier - Boolean (default = false). If true, error backtraces do not list the - stack frames below the evalScript. - @item async - Boolean (default = false). If true, @code{await} is accepted in the - script and a promise is returned. The promise is resolved with an - object whose @code{value} property holds the value returned by the - script. - @end table - -@item loadScript(filename) -Evaluate the file @code{filename} as a script (global eval). - -@item loadFile(filename) -Load the file @code{filename} and return it as a string assuming UTF-8 -encoding. Return @code{null} in case of I/O error. - -@item open(filename, flags, errorObj = undefined) -Open a file (wrapper to the libc @code{fopen()}). Return the FILE -object or @code{null} in case of I/O error. If @code{errorObj} is not -undefined, set its @code{errno} property to the error code or to 0 if -no error occured. - -@item popen(command, flags, errorObj = undefined) -Open a process by creating a pipe (wrapper to the libc -@code{popen()}). Return the FILE -object or @code{null} in case of I/O error. If @code{errorObj} is not -undefined, set its @code{errno} property to the error code or to 0 if -no error occured. - -@item fdopen(fd, flags, errorObj = undefined) -Open a file from a file handle (wrapper to the libc -@code{fdopen()}). Return the FILE -object or @code{null} in case of I/O error. If @code{errorObj} is not -undefined, set its @code{errno} property to the error code or to 0 if -no error occured. - -@item tmpfile(errorObj = undefined) -Open a temporary file. Return the FILE -object or @code{null} in case of I/O error. If @code{errorObj} is not -undefined, set its @code{errno} property to the error code or to 0 if -no error occured. - -@item puts(str) -Equivalent to @code{std.out.puts(str)}. - -@item printf(fmt, ...args) -Equivalent to @code{std.out.printf(fmt, ...args)}. - -@item sprintf(fmt, ...args) -Equivalent to the libc sprintf(). - -@item in -@item out -@item err -Wrappers to the libc file @code{stdin}, @code{stdout}, @code{stderr}. - -@item SEEK_SET -@item SEEK_CUR -@item SEEK_END -Constants for seek(). - -@item Error - -Enumeration object containing the integer value of common errors -(additional error codes may be defined): - - @table @code - @item EINVAL - @item EIO - @item EACCES - @item EEXIST - @item ENOSPC - @item ENOSYS - @item EBUSY - @item ENOENT - @item EPERM - @item EPIPE - @end table - -@item strerror(errno) -Return a string that describes the error @code{errno}. - -@item gc() -Manually invoke the cycle removal algorithm. The cycle removal -algorithm is automatically started when needed, so this function is -useful in case of specific memory constraints or for testing. - -@item getenv(name) -Return the value of the environment variable @code{name} or -@code{undefined} if it is not defined. - -@item setenv(name, value) -Set the value of the environment variable @code{name} to the string -@code{value}. - -@item unsetenv(name) -Delete the environment variable @code{name}. - -@item getenviron() -Return an object containing the environment variables as key-value pairs. - -@item urlGet(url, options = undefined) - -Download @code{url} using the @file{curl} command line -utility. @code{options} is an optional object containing the following -optional properties: - - @table @code - @item binary - Boolean (default = false). If true, the response is an ArrayBuffer - instead of a string. When a string is returned, the data is assumed - to be UTF-8 encoded. - - @item full - - Boolean (default = false). If true, return the an object contains - the properties @code{response} (response content), - @code{responseHeaders} (headers separated by CRLF), @code{status} - (status code). @code{response} is @code{null} is case of protocol or - network error. If @code{full} is false, only the response is - returned if the status is between 200 and 299. Otherwise @code{null} - is returned. - - @end table - -@item parseExtJSON(str) - - Parse @code{str} using a superset of @code{JSON.parse}. The superset - is very close to the JSON5 specification. The following extensions - are accepted: - - @itemize - @item Single line and multiline comments - @item unquoted properties (ASCII-only Javascript identifiers) - @item trailing comma in array and object definitions - @item single quoted strings - @item @code{\v} escape and multi-line strings with trailing @code{\} - @item @code{\f} and @code{\v} are accepted as space characters - @item leading plus or decimal point in numbers - @item hexadecimal (@code{0x} prefix), octal (@code{0o} prefix) and binary (@code{0b} prefix) integers - @item @code{NaN} and @code{Infinity} are accepted as numbers - @end itemize -@end table - -FILE prototype: - -@table @code -@item close() -Close the file. Return 0 if OK or @code{-errno} in case of I/O error. -@item puts(str) -Outputs the string with the UTF-8 encoding. -@item printf(fmt, ...args) -Formatted printf. - -The same formats as the standard C library @code{printf} are -supported. Integer format types (e.g. @code{%d}) truncate the Numbers -or BigInts to 32 bits. Use the @code{l} modifier (e.g. @code{%ld}) to -truncate to 64 bits. - -@item flush() -Flush the buffered file. -@item seek(offset, whence) -Seek to a give file position (whence is -@code{std.SEEK_*}). @code{offset} can be a number or a bigint. Return -0 if OK or @code{-errno} in case of I/O error. -@item tell() -Return the current file position. -@item tello() -Return the current file position as a bigint. -@item eof() -Return true if end of file. -@item fileno() -Return the associated OS handle. -@item error() -Return true if there was an error. -@item clearerr() -Clear the error indication. - -@item read(buffer, position, length) -Read @code{length} bytes from the file to the ArrayBuffer @code{buffer} at byte -position @code{position} (wrapper to the libc @code{fread}). - -@item write(buffer, position, length) -Write @code{length} bytes to the file from the ArrayBuffer @code{buffer} at byte -position @code{position} (wrapper to the libc @code{fwrite}). - -@item getline() -Return the next line from the file, assuming UTF-8 encoding, excluding -the trailing line feed. - -@item readAsString(max_size = undefined) -Read @code{max_size} bytes from the file and return them as a string -assuming UTF-8 encoding. If @code{max_size} is not present, the file -is read up its end. - -@item getByte() -Return the next byte from the file. Return -1 if the end of file is reached. - -@item putByte(c) -Write one byte to the file. -@end table - -@subsection @code{os} module - -The @code{os} module provides Operating System specific functions: - -@itemize -@item low level file access -@item signals -@item timers -@item asynchronous I/O -@item workers (threads) -@end itemize - -The OS functions usually return 0 if OK or an OS specific negative -error code. - -Available exports: - -@table @code -@item open(filename, flags, mode = 0o666) -Open a file. Return a handle or < 0 if error. - -@item O_RDONLY -@item O_WRONLY -@item O_RDWR -@item O_APPEND -@item O_CREAT -@item O_EXCL -@item O_TRUNC -POSIX open flags. - -@item O_TEXT -(Windows specific). Open the file in text mode. The default is binary mode. - -@item close(fd) -Close the file handle @code{fd}. - -@item seek(fd, offset, whence) -Seek in the file. Use @code{std.SEEK_*} for -@code{whence}. @code{offset} is either a number or a bigint. If -@code{offset} is a bigint, a bigint is returned too. - -@item read(fd, buffer, offset, length) -Read @code{length} bytes from the file handle @code{fd} to the -ArrayBuffer @code{buffer} at byte position @code{offset}. -Return the number of read bytes or < 0 if error. - -@item write(fd, buffer, offset, length) -Write @code{length} bytes to the file handle @code{fd} from the -ArrayBuffer @code{buffer} at byte position @code{offset}. -Return the number of written bytes or < 0 if error. - -@item isatty(fd) -Return @code{true} is @code{fd} is a TTY (terminal) handle. - -@item ttyGetWinSize(fd) -Return the TTY size as @code{[width, height]} or @code{null} if not available. - -@item ttySetRaw(fd) -Set the TTY in raw mode. - -@item remove(filename) -Remove a file. Return 0 if OK or @code{-errno}. - -@item rename(oldname, newname) -Rename a file. Return 0 if OK or @code{-errno}. - -@item realpath(path) -Return @code{[str, err]} where @code{str} is the canonicalized absolute -pathname of @code{path} and @code{err} the error code. - -@item getcwd() -Return @code{[str, err]} where @code{str} is the current working directory -and @code{err} the error code. - -@item chdir(path) -Change the current directory. Return 0 if OK or @code{-errno}. - -@item mkdir(path, mode = 0o777) -Create a directory at @code{path}. Return 0 if OK or @code{-errno}. - -@item stat(path) -@item lstat(path) - -Return @code{[obj, err]} where @code{obj} is an object containing the -file status of @code{path}. @code{err} is the error code. The -following fields are defined in @code{obj}: dev, ino, mode, nlink, -uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are -specified in milliseconds since 1970. @code{lstat()} is the same as -@code{stat()} excepts that it returns information about the link -itself. - -@item S_IFMT -@item S_IFIFO -@item S_IFCHR -@item S_IFDIR -@item S_IFBLK -@item S_IFREG -@item S_IFSOCK -@item S_IFLNK -@item S_ISGID -@item S_ISUID -Constants to interpret the @code{mode} property returned by -@code{stat()}. They have the same value as in the C system header -@file{sys/stat.h}. - -@item utimes(path, atime, mtime) -Change the access and modification times of the file @code{path}. The -times are specified in milliseconds since 1970. Return 0 if OK or @code{-errno}. - -@item symlink(target, linkpath) -Create a link at @code{linkpath} containing the string @code{target}. Return 0 if OK or @code{-errno}. - -@item readlink(path) -Return @code{[str, err]} where @code{str} is the link target and @code{err} -the error code. - -@item readdir(path) -Return @code{[array, err]} where @code{array} is an array of strings -containing the filenames of the directory @code{path}. @code{err} is -the error code. - -@item setReadHandler(fd, func) -Add a read handler to the file handle @code{fd}. @code{func} is called -each time there is data pending for @code{fd}. A single read handler -per file handle is supported. Use @code{func = null} to remove the -handler. - -@item setWriteHandler(fd, func) -Add a write handler to the file handle @code{fd}. @code{func} is -called each time data can be written to @code{fd}. A single write -handler per file handle is supported. Use @code{func = null} to remove -the handler. - -@item signal(signal, func) -Call the function @code{func} when the signal @code{signal} -happens. Only a single handler per signal number is supported. Use -@code{null} to set the default handler or @code{undefined} to ignore -the signal. Signal handlers can only be defined in the main thread. - -@item SIGINT -@item SIGABRT -@item SIGFPE -@item SIGILL -@item SIGSEGV -@item SIGTERM -POSIX signal numbers. - -@item kill(pid, sig) -Send the signal @code{sig} to the process @code{pid}. - -@item exec(args[, options]) -Execute a process with the arguments @code{args}. @code{options} is an -object containing optional parameters: - - @table @code - @item block - Boolean (default = true). If true, wait until the process is - terminated. In this case, @code{exec} return the exit code if positive - or the negated signal number if the process was interrupted by a - signal. If false, do not block and return the process id of the child. - - @item usePath - Boolean (default = true). If true, the file is searched in the - @code{PATH} environment variable. - - @item file - String (default = @code{args[0]}). Set the file to be executed. - - @item cwd - String. If present, set the working directory of the new process. - - @item stdin - @item stdout - @item stderr - If present, set the handle in the child for stdin, stdout or stderr. - - @item env - Object. If present, set the process environment from the object - key-value pairs. Otherwise use the same environment as the current - process. - - @item uid - Integer. If present, the process uid with @code{setuid}. - - @item gid - Integer. If present, the process gid with @code{setgid}. - - @end table - -@item getpid() -Return the current process ID. - -@item waitpid(pid, options) -@code{waitpid} Unix system call. Return the array @code{[ret, -status]}. @code{ret} contains @code{-errno} in case of error. - -@item WNOHANG -Constant for the @code{options} argument of @code{waitpid}. - -@item dup(fd) -@code{dup} Unix system call. - -@item dup2(oldfd, newfd) -@code{dup2} Unix system call. - -@item pipe() -@code{pipe} Unix system call. Return two handles as @code{[read_fd, -write_fd]} or null in case of error. - -@item sleep(delay_ms) -Sleep during @code{delay_ms} milliseconds. - -@item sleepAsync(delay_ms) -Asynchronouse sleep during @code{delay_ms} milliseconds. Returns a promise. Example: -@example -await os.sleepAsync(500); -@end example - -@item now() -Return a timestamp in milliseconds with more precision than -@code{Date.now()}. The time origin is unspecified and is normally not -impacted by system clock adjustments. - -@item setTimeout(func, delay) -Call the function @code{func} after @code{delay} ms. Return a handle -to the timer. - -@item clearTimeout(handle) -Cancel a timer. - -@item platform -Return a string representing the platform: @code{"linux"}, @code{"darwin"}, -@code{"win32"} or @code{"js"}. - -@item Worker(module_filename) -Constructor to create a new thread (worker) with an API close to the -@code{WebWorkers}. @code{module_filename} is a string specifying the -module filename which is executed in the newly created thread. As for -dynamically imported module, it is relative to the current script or -module path. Threads normally don't share any data and communicate -between each other with messages. Nested workers are not supported. An -example is available in @file{tests/test_worker.js}. - -The worker class has the following static properties: - - @table @code - @item parent - In the created worker, @code{Worker.parent} represents the parent - worker and is used to send or receive messages. - @end table - -The worker instances have the following properties: - - @table @code - @item postMessage(msg) - - Send a message to the corresponding worker. @code{msg} is cloned in - the destination worker using an algorithm similar to the @code{HTML} - structured clone algorithm. @code{SharedArrayBuffer} are shared - between workers. - - Current limitations: @code{Map} and @code{Set} are not supported - yet. - - @item onmessage - - Getter and setter. Set a function which is called each time a - message is received. The function is called with a single - argument. It is an object with a @code{data} property containing the - received message. The thread is not terminated if there is at least - one non @code{null} @code{onmessage} handler. - - @end table - -@end table - -@section QuickJS C API - -The C API was designed to be simple and efficient. The C API is -defined in the header @code{quickjs.h}. - -@subsection Runtime and contexts - -@code{JSRuntime} represents a Javascript runtime corresponding to an -object heap. Several runtimes can exist at the same time but they -cannot exchange objects. Inside a given runtime, no multi-threading is -supported. - -@code{JSContext} represents a Javascript context (or Realm). Each -JSContext has its own global objects and system objects. There can be -several JSContexts per JSRuntime and they can share objects, similar -to frames of the same origin sharing Javascript objects in a -web browser. - -@subsection JSValue - -@code{JSValue} represents a Javascript value which can be a primitive -type or an object. Reference counting is used, so it is important to -explicitly duplicate (@code{JS_DupValue()}, increment the reference -count) or free (@code{JS_FreeValue()}, decrement the reference count) -JSValues. - -@subsection C functions - -C functions can be created with -@code{JS_NewCFunction()}. @code{JS_SetPropertyFunctionList()} is a -shortcut to easily add functions, setters and getters properties to a -given object. - -Unlike other embedded Javascript engines, there is no implicit stack, -so C functions get their parameters as normal C parameters. As a -general rule, C functions take constant @code{JSValue}s as parameters -(so they don't need to free them) and return a newly allocated (=live) -@code{JSValue}. - -@subsection Exceptions - -Exceptions: most C functions can return a Javascript exception. It -must be explicitly tested and handled by the C code. The specific -@code{JSValue} @code{JS_EXCEPTION} indicates that an exception -occurred. The actual exception object is stored in the -@code{JSContext} and can be retrieved with @code{JS_GetException()}. - -@subsection Script evaluation - -Use @code{JS_Eval()} to evaluate a script or module source. - -If the script or module was compiled to bytecode with @code{qjsc}, it -can be evaluated by calling @code{js_std_eval_binary()}. The advantage -is that no compilation is needed so it is faster and smaller because -the compiler can be removed from the executable if no @code{eval} is -required. - -Note: the bytecode format is linked to a given QuickJS -version. Moreover, no security check is done before its -execution. Hence the bytecode should not be loaded from untrusted -sources. That's why there is no option to output the bytecode to a -binary file in @code{qjsc}. - -@subsection JS Classes - -C opaque data can be attached to a Javascript object. The type of the -C opaque data is determined with the class ID (@code{JSClassID}) of -the object. Hence the first step is to register a new class ID and JS -class (@code{JS_NewClassID()}, @code{JS_NewClass()}). Then you can -create objects of this class with @code{JS_NewObjectClass()} and get or -set the C opaque point with -@code{JS_GetOpaque()}/@code{JS_SetOpaque()}. - -When defining a new JS class, it is possible to declare a finalizer -which is called when the object is destroyed. The finalizer should be -used to release C resources. It is invalid to execute JS code from -it. A @code{gc_mark} method can be provided so that the cycle removal -algorithm can find the other objects referenced by this object. Other -methods are available to define exotic object behaviors. - -The Class ID are globally allocated (i.e. for all runtimes). The -JSClass are allocated per @code{JSRuntime}. @code{JS_SetClassProto()} -is used to define a prototype for a given class in a given -JSContext. @code{JS_NewObjectClass()} sets this prototype in the -created object. - -Examples are available in @file{quickjs-libc.c}. - -@subsection C Modules - -Native ES6 modules are supported and can be dynamically or statically -linked. Look at the @file{test_bjson} and @file{bjson.so} -examples. The standard library @file{quickjs-libc.c} is also a good example -of a native module. - -@subsection Memory handling - -Use @code{JS_SetMemoryLimit()} to set a global memory allocation limit -to a given JSRuntime. - -Custom memory allocation functions can be provided with -@code{JS_NewRuntime2()}. - -The maximum system stack size can be set with @code{JS_SetMaxStackSize()}. - -@subsection Execution timeout and interrupts - -Use @code{JS_SetInterruptHandler()} to set a callback which is -regularly called by the engine when it is executing code. This -callback can be used to implement an execution timeout. - -It is used by the command line interpreter to implement a -@code{Ctrl-C} handler. - -@chapter Internals - -@section Bytecode - -The compiler generates bytecode directly with no intermediate -representation such as a parse tree, hence it is very fast. Several -optimizations passes are done over the generated bytecode. - -A stack-based bytecode was chosen because it is simple and generates -compact code. - -For each function, the maximum stack size is computed at compile time so that -no runtime stack overflow tests are needed. - -A separate compressed line number table is maintained for the debug -information. - -Access to closure variables is optimized and is almost as fast as local -variables. - -Direct @code{eval} in strict mode is optimized. - -@section Executable generation - -@subsection @code{qjsc} compiler - -The @code{qjsc} compiler generates C sources from Javascript files. By -default the C sources are compiled with the system compiler -(@code{gcc} or @code{clang}). - -The generated C source contains the bytecode of the compiled functions -or modules. If a full complete executable is needed, it also -contains a @code{main()} function with the necessary C code to initialize the -Javascript engine and to load and execute the compiled functions and -modules. - -Javascript code can be mixed with C modules. - -In order to have smaller executables, specific Javascript features can -be disabled, in particular @code{eval} or the regular expressions. The -code removal relies on the Link Time Optimization of the system -compiler. - -@subsection Binary JSON - -@code{qjsc} works by compiling scripts or modules and then serializing -them to a binary format. A subset of this format (without functions or -modules) can be used as binary JSON. The example @file{test_bjson.js} -shows how to use it. - -Warning: the binary JSON format may change without notice, so it -should not be used to store persistent data. The @file{test_bjson.js} -example is only used to test the binary object format functions. - -@section Runtime - -@subsection Strings - -Strings are stored either as an 8 bit or a 16 bit array of -characters. Hence random access to characters is always fast. - -The C API provides functions to convert Javascript Strings to C UTF-8 encoded -strings. The most common case where the Javascript string contains -only ASCII characters involves no copying. - -@subsection Objects - -The object shapes (object prototype, property names and flags) are shared -between objects to save memory. - -Arrays with no holes (except at the end of the array) are optimized. - -TypedArray accesses are optimized. - -@subsection Atoms - -Object property names and some strings are stored as Atoms (unique -strings) to save memory and allow fast comparison. Atoms are -represented as a 32 bit integer. Half of the atom range is reserved for -immediate integer literals from @math{0} to @math{2^{31}-1}. - -@subsection Numbers - -Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754 -floating point values. Most operations have fast paths for the 32-bit -integer case. - -@subsection Garbage collection - -Reference counting is used to free objects automatically and -deterministically. A separate cycle removal pass is done when the allocated -memory becomes too large. The cycle removal algorithm only uses the -reference counts and the object content, so no explicit garbage -collection roots need to be manipulated in the C code. - -@subsection JSValue - -It is a Javascript value which can be a primitive type (such as -Number, String, ...) or an Object. NaN boxing is used in the 32-bit version -to store 64-bit floating point numbers. The representation is -optimized so that 32-bit integers and reference counted values can be -efficiently tested. - -In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The -rationale is that in 64-bit code memory usage is less critical. - -In both cases (32 or 64 bits), JSValue exactly fits two CPU registers, -so it can be efficiently returned by C functions. - -@subsection Function call - -The engine is optimized so that function calls are fast. The system -stack holds the Javascript parameters and local variables. - -@section RegExp - -A specific regular expression engine was developed. It is both small -and efficient and supports all the ES2024 features including the -Unicode properties. As the Javascript compiler, it directly generates -bytecode without a parse tree. - -Backtracking with an explicit stack is used so that there is no -recursion on the system stack. Simple quantifiers are specifically -optimized to avoid recursions. - -The full regexp library weights about 15 KiB (x86 code), excluding the -Unicode library. - -@section Unicode - -A specific Unicode library was developed so that there is no -dependency on an external large Unicode library such as ICU. All the -Unicode tables are compressed while keeping a reasonable access -speed. - -The library supports case conversion, Unicode normalization, Unicode -script queries, Unicode general category queries and all Unicode -binary properties. - -The full Unicode library weights about 45 KiB (x86 code). - -@section BigInt - -BigInts are represented using binary two's complement notation. An -additional short bigint value is used to optimize the performance on -small BigInt values. - -@chapter License - -QuickJS is released under the MIT license. - -Unless otherwise specified, the QuickJS sources are copyright Fabrice -Bellard and Charlie Gordon. - -@bye diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.c deleted file mode 100755 index ac1be8d8..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.c +++ /dev/null @@ -1,1620 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "dtoa.h" - -/* - TODO: - - test n_digits=101 instead of 100 - - simplify subnormal handling - - reduce max memory usage - - free format: could add shortcut if exact result - - use 64 bit limb_t when possible - - use another algorithm for free format dtoa in base 10 (ryu ?) -*/ - -#define USE_POW5_TABLE -/* use fast path to print small integers in free format */ -#define USE_FAST_INT - -#define LIMB_LOG2_BITS 5 - -#define LIMB_BITS (1 << LIMB_LOG2_BITS) - -typedef int32_t slimb_t; -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; - -#define LIMB_DIGITS 9 - -#define JS_RADIX_MAX 36 - -#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */ -#define MANT_LEN_MAX 18 /* < 36^100 */ - -typedef intptr_t mp_size_t; - -/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */ -typedef struct { - int len; /* >= 1 */ - limb_t tab[]; -} mpb_t; - -static limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) -{ - size_t i; - limb_t k, a; - - k=b; - for(i=0;i> LIMB_BITS; - } - return l; -} - -/* WARNING: d must be >= 2^(LIMB_BITS-1) */ -static inline limb_t udiv1norm_init(limb_t d) -{ - limb_t a0, a1; - a1 = -d - 1; - a0 = -1; - return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; -} - -/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 - / d' with 0 <= a1 < d. */ -static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, - limb_t d, limb_t d_inv) -{ - limb_t n1m, n_adj, q, r, ah; - dlimb_t a; - n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); - n_adj = a0 + (n1m & d); - a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; - q = (a >> LIMB_BITS) + a1; - /* compute a - q * r and update q so that the remainder is between - 0 and d - 1 */ - a = ((dlimb_t)a1 << LIMB_BITS) | a0; - a = a - (dlimb_t)q * d - d; - ah = a >> LIMB_BITS; - q += 1 + ah; - r = (limb_t)a + (ah & d); - *pr = r; - return q; -} - -static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r) -{ - slimb_t i; - dlimb_t a1; - for(i = n - 1; i >= 0; i--) { - a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; - tabr[i] = a1 / b; - r = a1 % b; - } - return r; -} - -/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). - 1 <= shift <= LIMB_BITS - 1 */ -static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t high) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); - l = a; - } - return l & (((limb_t)1 << shift) - 1); -} - -/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low < - 2^shift. */ -static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t low) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = low; - for(i = 0; i < n; i++) { - a = tab[i]; - tab_r[i] = (a << shift) | l; - l = (a >> (LIMB_BITS - shift)); - } - return l; -} - -static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r, limb_t b_inv, int shift) -{ - slimb_t i; - - if (shift != 0) { - r = (r << shift) | mp_shl(tabr, taba, n, shift, 0); - } - for(i = n - 1; i >= 0; i--) { - tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); - } - r >>= shift; - return r; -} - -static __maybe_unused void mpb_dump(const char *str, const mpb_t *a) -{ - int i; - - printf("%s= 0x", str); - for(i = a->len - 1; i >= 0; i--) { - printf("%08x", a->tab[i]); - if (i != 0) - printf("_"); - } - printf("\n"); -} - -static void mpb_renorm(mpb_t *r) -{ - while (r->len > 1 && r->tab[r->len - 1] == 0) - r->len--; -} - -#ifdef USE_POW5_TABLE -static const uint32_t pow5_table[17] = { - 0x00000005, 0x00000019, 0x0000007d, 0x00000271, - 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1, - 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51, - 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1, - 0xa2bc2ec5, -}; - -static const uint8_t pow5h_table[4] = { - 0x00000001, 0x00000007, 0x00000023, 0x000000b1, -}; - -static const uint32_t pow5_inv_table[13] = { - 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1, - 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23, - 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812, - 0xc25c2684, -}; -#endif - -/* return a^b */ -static uint64_t pow_ui(uint32_t a, uint32_t b) -{ - int i, n_bits; - uint64_t r; - if (b == 0) - return 1; - if (b == 1) - return a; -#ifdef USE_POW5_TABLE - if ((a == 5 || a == 10) && b <= 17) { - r = pow5_table[b - 1]; - if (b >= 14) { - r |= (uint64_t)pow5h_table[b - 14] << 32; - } - if (a == 10) - r <<= b; - return r; - } -#endif - r = a; - n_bits = 32 - clz32(b); - for(i = n_bits - 2; i >= 0; i--) { - r *= r; - if ((b >> i) & 1) - r *= a; - } - return r; -} - -static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b) -{ - uint32_t r_inv, r; - int shift; -#ifdef USE_POW5_TABLE - if (a == 5 && b >= 1 && b <= 13) { - r = pow5_table[b - 1]; - shift = clz32(r); - r <<= shift; - r_inv = pow5_inv_table[b - 1]; - } else -#endif - { - r = pow_ui(a, b); - shift = clz32(r); - r <<= shift; - r_inv = udiv1norm_init(r); - } - *pshift = shift; - *pr_inv = r_inv; - return r; -} - -enum { - JS_RNDN, /* round to nearest, ties to even */ - JS_RNDNA, /* round to nearest, ties away from zero */ - JS_RNDZ, -}; - -static int mpb_get_bit(const mpb_t *r, int k) -{ - int l; - - l = (unsigned)k / LIMB_BITS; - k = k & (LIMB_BITS - 1); - if (l >= r->len) - return 0; - else - return (r->tab[l] >> k) & 1; -} - -/* compute round(r / 2^shift). 'shift' can be negative */ -static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode) -{ - int l, i; - - if (shift == 0) - return; - if (shift < 0) { - shift = -shift; - l = (unsigned)shift / LIMB_BITS; - shift = shift & (LIMB_BITS - 1); - if (shift != 0) { - r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0); - r->len++; - mpb_renorm(r); - } - if (l > 0) { - for(i = r->len - 1; i >= 0; i--) - r->tab[i + l] = r->tab[i]; - for(i = 0; i < l; i++) - r->tab[i] = 0; - r->len += l; - } - } else { - limb_t bit1, bit2; - int k, add_one; - - switch(rnd_mode) { - default: - case JS_RNDZ: - add_one = 0; - break; - case JS_RNDN: - case JS_RNDNA: - bit1 = mpb_get_bit(r, shift - 1); - if (bit1) { - if (rnd_mode == JS_RNDNA) { - bit2 = 1; - } else { - /* bit2 = oring of all the bits after bit1 */ - bit2 = 0; - if (shift >= 2) { - k = shift - 1; - l = (unsigned)k / LIMB_BITS; - k = k & (LIMB_BITS - 1); - for(i = 0; i < min_int(l, r->len); i++) - bit2 |= r->tab[i]; - if (l < r->len) - bit2 |= r->tab[l] & (((limb_t)1 << k) - 1); - } - } - if (bit2) { - add_one = 1; - } else { - /* round to even */ - add_one = mpb_get_bit(r, shift); - } - } else { - add_one = 0; - } - break; - } - - l = (unsigned)shift / LIMB_BITS; - shift = shift & (LIMB_BITS - 1); - if (l >= r->len) { - r->len = 1; - r->tab[0] = add_one; - } else { - if (l > 0) { - r->len -= l; - for(i = 0; i < r->len; i++) - r->tab[i] = r->tab[i + l]; - } - if (shift != 0) { - mp_shr(r->tab, r->tab, r->len, shift, 0); - mpb_renorm(r); - } - if (add_one) { - limb_t a; - a = mp_add_ui(r->tab, 1, r->len); - if (a) - r->tab[r->len++] = a; - } - } - } -} - -/* return -1, 0 or 1 */ -static int mpb_cmp(const mpb_t *a, const mpb_t *b) -{ - mp_size_t i; - if (a->len < b->len) - return -1; - else if (a->len > b->len) - return 1; - for(i = a->len - 1; i >= 0; i--) { - if (a->tab[i] != b->tab[i]) { - if (a->tab[i] < b->tab[i]) - return -1; - else - return 1; - } - } - return 0; -} - -static void mpb_set_u64(mpb_t *r, uint64_t m) -{ -#if LIMB_BITS == 64 - r->tab[0] = m; - r->len = 1; -#else - r->tab[0] = m; - r->tab[1] = m >> LIMB_BITS; - if (r->tab[1] == 0) - r->len = 1; - else - r->len = 2; -#endif -} - -static uint64_t mpb_get_u64(mpb_t *r) -{ -#if LIMB_BITS == 64 - return r->tab[0]; -#else - if (r->len == 1) { - return r->tab[0]; - } else { - return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS); - } -#endif -} - -/* floor_log2() = position of the first non zero bit or -1 if zero. */ -static int mpb_floor_log2(mpb_t *a) -{ - limb_t v; - v = a->tab[a->len - 1]; - if (v == 0) - return -1; - else - return a->len * LIMB_BITS - 1 - clz32(v); -} - -#define MUL_LOG2_RADIX_BASE_LOG2 24 - -/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */ -static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = { - 0x000000, 0xa1849d, 0x000000, 0x6e40d2, - 0x6308c9, 0x5b3065, 0x000000, 0x50c24e, - 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54, - 0x433d00, 0x418677, 0x000000, 0x3ea16b, - 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899, - 0x39680b, 0x3897b3, 0x37d5af, 0x372069, - 0x367686, 0x35d6df, 0x354072, 0x34b261, - 0x342bea, 0x33ac62, 0x000000, 0x32bfd9, - 0x3251dd, 0x31e8d6, 0x318465, -}; - -/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */ -static int mul_log2_radix(int a, int radix) -{ - int radix_bits, mult; - - if ((radix & (radix - 1)) == 0) { - /* if the radix is a power of two better to do it exactly */ - radix_bits = 31 - clz32(radix); - if (a < 0) - a -= radix_bits - 1; - return a / radix_bits; - } else { - mult = mul_log2_radix_table[radix - 2]; - return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2; - } -} - -#if 0 -static void build_mul_log2_radix_table(void) -{ - int base, radix, mult, col, base_log2; - - base_log2 = 24; - base = 1 << base_log2; - col = 0; - for(radix = 2; radix <= 36; radix++) { - if ((radix & (radix - 1)) == 0) - mult = 0; - else - mult = lrint((double)base / log2(radix)); - printf("0x%06x, ", mult); - if (++col == 4) { - printf("\n"); - col = 0; - } - } - printf("\n"); -} - -static void mul_log2_radix_test(void) -{ - int radix, i, ref, r; - - for(radix = 2; radix <= 36; radix++) { - for(i = -2048; i <= 2047; i++) { - ref = (int)floor((double)i / log2(radix)); - r = mul_log2_radix(i, radix); - if (ref != r) { - printf("ERROR: radix=%d i=%d r=%d ref=%d\n", - radix, i, r, ref); - exit(1); - } - } - } - if (0) - build_mul_log2_radix_table(); -} -#endif - -static void u32toa_len(char *buf, uint32_t n, size_t len) -{ - int digit, i; - for(i = len - 1; i >= 0; i--) { - digit = n % 10; - n = n / 10; - buf[i] = digit + '0'; - } -} - -/* for power of 2 radixes. len >= 1 */ -static void u64toa_bin_len(char *buf, uint64_t n, unsigned int radix_bits, int len) -{ - int digit, i; - unsigned int mask; - - mask = (1 << radix_bits) - 1; - for(i = len - 1; i >= 0; i--) { - digit = n & mask; - n >>= radix_bits; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } -} - -/* len >= 1. 2 <= radix <= 36 */ -static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) -{ - int digit, i; - - if (radix == 10) { - /* specific case with constant divisor */ -#if LIMB_BITS == 32 - u32toa_len(buf, n, len); -#else - /* XXX: optimize */ - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % 10; - n = (limb_t)n / 10; - buf[i] = digit + '0'; - } -#endif - } else { - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % radix; - n = (limb_t)n / radix; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } - } -} - -size_t u32toa(char *buf, uint32_t n) -{ - char buf1[10], *q; - size_t len; - - q = buf1 + sizeof(buf1); - do { - *--q = n % 10 + '0'; - n /= 10; - } while (n != 0); - len = buf1 + sizeof(buf1) - q; - memcpy(buf, q, len); - return len; -} - -size_t i32toa(char *buf, int32_t n) -{ - if (n >= 0) { - return u32toa(buf, n); - } else { - buf[0] = '-'; - return u32toa(buf + 1, -(uint32_t)n) + 1; - } -} - -#ifdef USE_FAST_INT -size_t u64toa(char *buf, uint64_t n) -{ - if (n < 0x100000000) { - return u32toa(buf, n); - } else { - uint64_t n1; - char *q = buf; - uint32_t n2; - - n1 = n / 1000000000; - n %= 1000000000; - if (n1 >= 0x100000000) { - n2 = n1 / 1000000000; - n1 = n1 % 1000000000; - /* at most two digits */ - if (n2 >= 10) { - *q++ = n2 / 10 + '0'; - n2 %= 10; - } - *q++ = n2 + '0'; - u32toa_len(q, n1, 9); - q += 9; - } else { - q += u32toa(q, n1); - } - u32toa_len(q, n, 9); - q += 9; - return q - buf; - } -} - -size_t i64toa(char *buf, int64_t n) -{ - if (n >= 0) { - return u64toa(buf, n); - } else { - buf[0] = '-'; - return u64toa(buf + 1, -(uint64_t)n) + 1; - } -} - -/* XXX: only tested for 1 <= n < 2^53 */ -size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix) -{ - int radix_bits, l; - if (likely(radix == 10)) - return u64toa(buf, n); - if ((radix & (radix - 1)) == 0) { - radix_bits = 31 - clz32(radix); - if (n == 0) - l = 1; - else - l = (64 - clz64(n) + radix_bits - 1) / radix_bits; - u64toa_bin_len(buf, n, radix_bits, l); - return l; - } else { - char buf1[41], *q; /* maximum length for radix = 3 */ - size_t len; - int digit; - q = buf1 + sizeof(buf1); - do { - digit = n % radix; - n /= radix; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - *--q = digit; - } while (n != 0); - len = buf1 + sizeof(buf1) - q; - memcpy(buf, q, len); - return len; - } -} - -size_t i64toa_radix(char *buf, int64_t n, unsigned int radix) -{ - if (n >= 0) { - return u64toa_radix(buf, n, radix); - } else { - buf[0] = '-'; - return u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1; - } -} -#endif /* USE_FAST_INT */ - -static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { -#if LIMB_BITS == 32 -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, -#else -64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, -#endif -}; - -static const uint32_t 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, -}; - -/* XXX: remove the table ? */ -static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = { - 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, -}; - -/* we limit the maximum number of significant digits for atod to about - 128 bits of precision for non power of two bases. The only - requirement for Javascript is at least 20 digits in base 10. For - power of two bases, we do an exact rounding in all the cases. */ -static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = { - 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24, -}; - -/* if abs(d) >= B^max_exponent, it is an overflow */ -static const int16_t max_exponent[JS_RADIX_MAX - 1] = { - 1024, 647, 512, 442, 397, 365, 342, 324, - 309, 297, 286, 277, 269, 263, 256, 251, - 246, 242, 237, 234, 230, 227, 224, 221, - 218, 216, 214, 211, 209, 207, 205, 203, - 202, 200, 199, -}; - -/* if abs(d) <= B^min_exponent, it is an underflow */ -static const int16_t min_exponent[JS_RADIX_MAX - 1] = { --1075, -679, -538, -463, -416, -383, -359, -340, - -324, -311, -300, -291, -283, -276, -269, -263, - -258, -254, -249, -245, -242, -238, -235, -232, - -229, -227, -224, -222, -220, -217, -215, -214, - -212, -210, -208, -}; - -#if 0 -void build_tables(void) -{ - int r, j, radix, n, col, i; - - /* radix_base_table */ - for(radix = 2; radix <= 36; radix++) { - r = 1; - for(j = 0; j < digits_per_limb_table[radix - 2]; j++) { - r *= radix; - } - printf(" 0x%08x,", r); - if ((radix % 4) == 1) - printf("\n"); - } - printf("\n"); - - /* dtoa_max_digits_table */ - for(radix = 2; radix <= 36; radix++) { - /* Note: over estimated when the radix is a power of two */ - printf(" %d,", 1 + (int)ceil(53.0 / log2(radix))); - } - printf("\n"); - - /* atod_max_digits_table */ - for(radix = 2; radix <= 36; radix++) { - if ((radix & (radix - 1)) == 0) { - /* 64 bits is more than enough */ - n = (int)floor(64.0 / log2(radix)); - } else { - n = (int)floor(128.0 / log2(radix)); - } - printf(" %d,", n); - } - printf("\n"); - - printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n"); - col = 0; - for(radix = 2; radix <= 36; radix++) { - printf("%5d, ", (int)ceil(1024 / log2(radix))); - if (++col == 8) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); - - printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n"); - col = 0; - for(radix = 2; radix <= 36; radix++) { - printf("%5d, ", (int)floor(-1075 / log2(radix))); - if (++col == 8) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); - - printf("static const uint32_t pow5_table[16] = {\n"); - col = 0; - for(i = 2; i <= 17; i++) { - r = 1; - for(j = 0; j < i; j++) { - r *= 5; - } - printf("0x%08x, ", r); - if (++col == 4) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); - - /* high part */ - printf("static const uint8_t pow5h_table[4] = {\n"); - col = 0; - for(i = 14; i <= 17; i++) { - uint64_t r1; - r1 = 1; - for(j = 0; j < i; j++) { - r1 *= 5; - } - printf("0x%08x, ", (uint32_t)(r1 >> 32)); - if (++col == 4) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); -} -#endif - -/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits, - the dot is not displayed. 'a' is modified. */ -static int output_digits(char *buf, - mpb_t *a, int radix, int n_digits1, - int dot_pos) -{ - int n_digits, digits_per_limb, radix_bits, n, len; - - n_digits = n_digits1; - if ((radix & (radix - 1)) == 0) { - /* radix = 2^radix_bits */ - radix_bits = 31 - clz32(radix); - } else { - radix_bits = 0; - } - digits_per_limb = digits_per_limb_table[radix - 2]; - if (radix_bits != 0) { - for(;;) { - n = min_int(n_digits, digits_per_limb); - n_digits -= n; - u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n); - if (n_digits == 0) - break; - mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ); - } - } else { - limb_t r; - while (n_digits != 0) { - n = min_int(n_digits, digits_per_limb); - n_digits -= n; - r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0); - mpb_renorm(a); - limb_to_a(buf + n_digits, r, radix, n); - } - } - - /* add the dot */ - len = n_digits1; - if (dot_pos != n_digits1) { - memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos); - buf[dot_pos] = '.'; - len++; - } - return len; -} - -/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f * - 2^-e_offset. 'f' can be negative. */ -static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, BOOL is_int, int e) -{ - int e_offset, d, n, n0; - - e_offset = -f * radix_shift; - if (radix1 != 1) { - d = digits_per_limb_table[radix1 - 2]; - if (f >= 0) { - limb_t h, b; - - b = 0; - n0 = 0; - while (f != 0) { - n = min_int(f, d); - if (n != n0) { - b = pow_ui(radix1, n); - n0 = n; - } - h = mp_mul1(a->tab, a->tab, a->len, b, 0); - if (h != 0) { - a->tab[a->len++] = h; - } - f -= n; - } - } else { - int extra_bits, l, shift; - limb_t r, rem, b, b_inv; - - f = -f; - l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */ - e_offset += l * LIMB_BITS; - if (!is_int) { - /* at least 'e' bits are needed in the final result for rounding */ - extra_bits = max_int(e - mpb_floor_log2(a), 0); - } else { - /* at least two extra bits are needed in the final result - for rounding */ - extra_bits = max_int(2 + e - e_offset, 0); - } - e_offset += extra_bits; - mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ); - - b = 0; - b_inv = 0; - shift = 0; - n0 = 0; - rem = 0; - while (f != 0) { - n = min_int(f, d); - if (n != n0) { - b = pow_ui_inv(&b_inv, &shift, radix1, n); - n0 = n; - } - r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift); - rem |= r; - mpb_renorm(a); - f -= n; - } - /* if the remainder is non zero, use it for rounding */ - a->tab[0] |= (rem != 0); - } - } - return e_offset; -} - -/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */ -static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f, - int rnd_mode) -{ - int e_offset; - - mpb_set_u64(tmp1, m); - e_offset = mul_pow(tmp1, radix1, radix_shift, f, TRUE, e); - mpb_shr_round(tmp1, -e + e_offset, rnd_mode); -} - -/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */ -static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode) -{ - int e; - uint64_t m; - - if (a->tab[0] == 0 && a->len == 1) { - /* zero result */ - m = 0; - e = 0; /* don't care */ - } else { - int prec, prec1, e_min; - e = mpb_floor_log2(a) + 1 - e_offset; - prec1 = 53; - e_min = -1021; - if (e < e_min) { - /* subnormal result or zero */ - prec = prec1 - (e_min - e); - } else { - prec = prec1; - } - mpb_shr_round(a, e + e_offset - prec, rnd_mode); - m = mpb_get_u64(a); - m <<= (53 - prec); - /* mantissa overflow due to rounding */ - if (m >= (uint64_t)1 << 53) { - m >>= 1; - e++; - } - } - *pe = e; - return m; -} - -/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52 - <= m < 2^53 or m = 0. - 'a' is modified. */ -static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a, - int radix1, int radix_shift, int f, int rnd_mode) -{ - int e_offset; - - e_offset = mul_pow(a, radix1, radix_shift, f, FALSE, 55); - return round_to_d(pe, a, e_offset, rnd_mode); -} - -#ifdef JS_DTOA_DUMP_STATS -static int out_len_count[17]; - -void js_dtoa_dump_stats(void) -{ - int i, sum; - sum = 0; - for(i = 0; i < 17; i++) - sum += out_len_count[i]; - for(i = 0; i < 17; i++) { - printf("%2d %8d %5.2f%%\n", - i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100); - } -} -#endif - -/* return a maximum bound of the string length. The bound depends on - 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED - is enabled. */ -int js_dtoa_max_len(double d, int radix, int n_digits, int flags) -{ - int fmt = flags & JS_DTOA_FORMAT_MASK; - int n, e; - uint64_t a; - - if (fmt != JS_DTOA_FORMAT_FRAC) { - if (fmt == JS_DTOA_FORMAT_FREE) { - n = dtoa_max_digits_table[radix - 2]; - } else { - n = n_digits; - } - if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) { - /* no exponential */ - a = float64_as_uint64(d); - e = (a >> 52) & 0x7ff; - if (e == 0x7ff) { - /* NaN, Infinity */ - n = 0; - } else { - e -= 1023; - /* XXX: adjust */ - n += 10 + abs(mul_log2_radix(e - 1, radix)); - } - } else { - /* extra: sign, 1 dot and exponent "e-1000" */ - n += 1 + 1 + 6; - } - } else { - a = float64_as_uint64(d); - e = (a >> 52) & 0x7ff; - if (e == 0x7ff) { - /* NaN, Infinity */ - n = 0; - } else { - /* high bound for the integer part */ - e -= 1023; - /* x < 2^(e + 1) */ - if (e < 0) { - n = 1; - } else { - n = 2 + mul_log2_radix(e - 1, radix); - } - /* sign, extra digit, 1 dot */ - n += 1 + 1 + 1 + n_digits; - } - } - return max_int(n, 9); /* also include NaN and [-]Infinity */ -} - -#if defined(__SANITIZE_ADDRESS__) && 0 -static void *dtoa_malloc(uint64_t **pptr, size_t size) -{ - return malloc(size); -} -static void dtoa_free(void *ptr) -{ - free(ptr); -} -#else -static void *dtoa_malloc(uint64_t **pptr, size_t size) -{ - void *ret; - ret = *pptr; - *pptr += (size + 7) / 8; - return ret; -} - -static void dtoa_free(void *ptr) -{ -} -#endif - -/* return the length */ -int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, - JSDTOATempMem *tmp_mem) -{ - uint64_t a, m, *mptr = tmp_mem->mem; - int e, sgn, l, E, P, i, E_max, radix1, radix_shift; - char *q; - mpb_t *tmp1, *mant_max; - int fmt = flags & JS_DTOA_FORMAT_MASK; - - tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); - mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX); - assert((mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0])); - - radix_shift = ctz32(radix); - radix1 = radix >> radix_shift; - a = float64_as_uint64(d); - sgn = a >> 63; - e = (a >> 52) & 0x7ff; - m = a & (((uint64_t)1 << 52) - 1); - q = buf; - if (e == 0x7ff) { - if (m == 0) { - if (sgn) - *q++ = '-'; - memcpy(q, "Infinity", 8); - q += 8; - } else { - memcpy(q, "NaN", 3); - q += 3; - } - goto done; - } else if (e == 0) { - if (m == 0) { - tmp1->len = 1; - tmp1->tab[0] = 0; - E = 1; - if (fmt == JS_DTOA_FORMAT_FREE) - P = 1; - else if (fmt == JS_DTOA_FORMAT_FRAC) - P = n_digits + 1; - else - P = n_digits; - /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */ - if (sgn && (flags & JS_DTOA_MINUS_ZERO)) - *q++ = '-'; - goto output; - } - /* denormal number: convert to a normal number */ - l = clz64(m) - 11; - e -= l - 1; - m <<= l; - } else { - m |= (uint64_t)1 << 52; - } - if (sgn) - *q++ = '-'; - /* remove the bias */ - e -= 1022; - /* d = 2^(e-53)*m */ - // printf("m=0x%016" PRIx64 " e=%d\n", m, e); -#ifdef USE_FAST_INT - if (fmt == JS_DTOA_FORMAT_FREE && - e >= 1 && e <= 53 && - (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 && - (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) { - m >>= 53 - e; - /* 'm' is never zero */ - q += u64toa_radix(q, m, radix); - goto done; - } -#endif - - /* this choice of E implies F=round(x*B^(P-E) is such as: - B^(P-1) <= F < 2.B^P. */ - E = 1 + mul_log2_radix(e - 1, radix); - - if (fmt == JS_DTOA_FORMAT_FREE) { - int P_max, E0, e1, E_found, P_found; - uint64_t m1, mant_found, mant, mant_max1; - /* P_max is guarranteed to work by construction */ - P_max = dtoa_max_digits_table[radix - 2]; - E0 = E; - E_found = 0; - P_found = 0; - mant_found = 0; - /* find the minimum number of digits by successive tries */ - P = P_max; /* P_max is guarateed to work */ - for(;;) { - /* mant_max always fits on 64 bits */ - mant_max1 = pow_ui(radix, P); - /* compute the mantissa in base B */ - E = E0; - for(;;) { - /* XXX: add inexact flag */ - mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN); - mant = mpb_get_u64(tmp1); - if (mant < mant_max1) - break; - E++; /* at most one iteration is possible */ - } - /* remove useless trailing zero digits */ - while ((mant % radix) == 0) { - mant /= radix; - P--; - } - /* garanteed to work for P = P_max */ - if (P_found == 0) - goto prec_found; - /* convert back to base 2 */ - mpb_set_u64(tmp1, mant); - m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN); - // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1); - /* Note: (m, e) is never zero here, so the exponent for m1 - = 0 does not matter */ - if (m1 == m && e1 == e) { - prec_found: - P_found = P; - E_found = E; - mant_found = mant; - if (P == 1) - break; - P--; /* try lower exponent */ - } else { - break; - } - } - P = P_found; - E = E_found; - mpb_set_u64(tmp1, mant_found); -#ifdef JS_DTOA_DUMP_STATS - if (radix == 10) { - out_len_count[P - 1]++; - } -#endif - } else if (fmt == JS_DTOA_FORMAT_FRAC) { - int len; - - assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS); - /* P = max_int(E, 1) + n_digits; */ - /* frac is rounded using RNDNA */ - mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA); - - /* we add one extra digit on the left and remove it if needed - to avoid testing if the result is < radix^P */ - len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits, - max_int(E + 1, 1)); - if (q[0] == '0' && len >= 2 && q[1] != '.') { - len--; - memmove(q, q + 1, len); - } - q += len; - goto done; - } else { - int pow_shift; - assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS); - P = n_digits; - /* mant_max = radix^P */ - mant_max->len = 1; - mant_max->tab[0] = 1; - pow_shift = mul_pow(mant_max, radix1, radix_shift, P, FALSE, 0); - mpb_shr_round(mant_max, pow_shift, JS_RNDZ); - - for(;;) { - /* fixed and frac are rounded using RNDNA */ - mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA); - if (mpb_cmp(tmp1, mant_max) < 0) - break; - E++; /* at most one iteration is possible */ - } - } - output: - if (fmt == JS_DTOA_FORMAT_FIXED) - E_max = n_digits; - else - E_max = dtoa_max_digits_table[radix - 2] + 4; - if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED || - ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) { - q += output_digits(q, tmp1, radix, P, 1); - E--; - if (radix == 10) { - *q++ = 'e'; - } else if (radix1 == 1 && radix_shift <= 4) { - E *= radix_shift; - *q++ = 'p'; - } else { - *q++ = '@'; - } - if (E < 0) { - *q++ = '-'; - E = -E; - } else { - *q++ = '+'; - } - q += u32toa(q, E); - } else if (E <= 0) { - *q++ = '0'; - *q++ = '.'; - for(i = 0; i < -E; i++) - *q++ = '0'; - q += output_digits(q, tmp1, radix, P, P); - } else { - q += output_digits(q, tmp1, radix, P, min_int(P, E)); - for(i = 0; i < E - P; i++) - *q++ = '0'; - } - done: - *q = '\0'; - dtoa_free(mant_max); - dtoa_free(tmp1); - return q - buf; -} - -static inline int 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; -} - -/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */ -static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a) -{ - int i; - if (r->tab[0] == 0 && r->len == 1) { - r->tab[0] = a; - } else { - if (radix_base == 0) { - for(i = r->len; i >= 0; i--) { - r->tab[i + 1] = r->tab[i]; - } - r->tab[0] = a; - } else { - r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len, - radix_base, a); - } - r->len++; - mpb_renorm(r); - } -} - -/* XXX: add fast path for small integers */ -double js_atod(const char *str, const char **pnext, int radix, int flags, - JSATODTempMem *tmp_mem) -{ - uint64_t *mptr = tmp_mem->mem; - const char *p, *p_start; - limb_t cur_limb, radix_base, extra_digits; - int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift; - int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos; - mpb_t *tmp0; - double dval; - BOOL is_bin_exp, is_zero, expn_overflow; - uint64_t m, a; - - tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); - assert((mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0])); - /* optional separator between digits */ - sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - - p = str; - is_neg = 0; - if (p[0] == '+') { - p++; - p_start = p; - } else if (p[0] == '-') { - is_neg = 1; - p++; - p_start = p; - } else { - p_start = p; - } - - 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 & JS_ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 8; - } else if ((p[1] == 'b' || p[1] == 'B') && - radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 2; - } else if ((p[1] >= '0' && p[1] <= '9') && - radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) { - int i; - 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 (to_digit((uint8_t)*p) >= radix) - goto fail; - no_prefix: ; - } else { - if (!(flags & JS_ATOD_INT_ONLY) && strstart(p, "Infinity", &p)) - goto overflow; - } - if (radix == 0) - radix = 10; - - cur_limb = 0; - expn_offset = 0; - digit_count = 0; - limb_digit_count = 0; - max_digits = atod_max_digits_table[radix - 2]; - digits_per_limb = digits_per_limb_table[radix - 2]; - radix_base = radix_base_table[radix - 2]; - radix_shift = ctz32(radix); - radix1 = radix >> radix_shift; - if (radix1 == 1) { - /* radix = 2^radix_bits */ - radix_bits = radix_shift; - } else { - radix_bits = 0; - } - tmp0->len = 1; - tmp0->tab[0] = 0; - extra_digits = 0; - pos = 0; - dot_pos = -1; - /* skip leading zeros */ - for(;;) { - if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && - !(flags & JS_ATOD_INT_ONLY)) { - if (*p == sep) - goto fail; - if (dot_pos >= 0) - break; - dot_pos = pos; - p++; - } - if (*p == sep && p > p_start && p[1] == '0') - p++; - if (*p != '0') - break; - p++; - pos++; - } - - sig_pos = pos; - for(;;) { - limb_t c; - if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && - !(flags & JS_ATOD_INT_ONLY)) { - if (*p == sep) - goto fail; - if (dot_pos >= 0) - break; - dot_pos = pos; - p++; - } - if (*p == sep && p > p_start && to_digit(p[1]) < radix) - p++; - c = to_digit(*p); - if (c >= radix) - break; - p++; - pos++; - if (digit_count < max_digits) { - /* XXX: could be faster when radix_bits != 0 */ - cur_limb = cur_limb * radix + c; - limb_digit_count++; - if (limb_digit_count == digits_per_limb) { - mpb_mul1_base(tmp0, radix_base, cur_limb); - cur_limb = 0; - limb_digit_count = 0; - } - digit_count++; - } else { - extra_digits |= c; - } - } - if (limb_digit_count != 0) { - mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb); - } - if (digit_count == 0) { - is_zero = TRUE; - expn_offset = 0; - } else { - is_zero = FALSE; - if (dot_pos < 0) - dot_pos = pos; - expn_offset = sig_pos + digit_count - dot_pos; - } - - /* Use the extra digits for rounding if the base is a power of - two. Otherwise they are just truncated. */ - if (radix_bits != 0 && extra_digits != 0) { - tmp0->tab[0] |= 1; - } - - /* parse the exponent, if any */ - expn = 0; - expn_overflow = FALSE; - is_bin_exp = FALSE; - if (!(flags & JS_ATOD_INT_ONLY) && - ((radix == 10 && (*p == 'e' || *p == 'E')) || - (radix != 10 && (*p == '@' || - (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) && - p > p_start) { - BOOL exp_is_neg; - int c; - is_bin_exp = (*p == 'p' || *p == 'P'); - p++; - exp_is_neg = 0; - if (*p == '+') { - p++; - } else if (*p == '-') { - exp_is_neg = 1; - p++; - } - c = to_digit(*p); - if (c >= 10) - goto fail; /* XXX: could stop before the exponent part */ - expn = c; - p++; - for(;;) { - if (*p == sep && to_digit(p[1]) < 10) - p++; - c = to_digit(*p); - if (c >= 10) - break; - if (!expn_overflow) { - if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) { - expn_overflow = TRUE; - } else { - expn = expn * 10 + c; - } - } - p++; - } - if (exp_is_neg) - expn = -expn; - /* if zero result, the exponent can be arbitrarily large */ - if (!is_zero && expn_overflow) { - if (exp_is_neg) - a = 0; - else - a = (uint64_t)0x7ff << 52; /* infinity */ - goto done; - } - } - - if (p == p_start) - goto fail; - - if (is_zero) { - a = 0; - } else { - int expn1; - if (radix_bits != 0) { - if (!is_bin_exp) - expn *= radix_bits; - expn -= expn_offset * radix_bits; - expn1 = expn + digit_count * radix_bits; - if (expn1 >= 1024 + radix_bits) - goto overflow; - else if (expn1 <= -1075) - goto underflow; - m = round_to_d(&e, tmp0, -expn, JS_RNDN); - } else { - expn -= expn_offset; - expn1 = expn + digit_count; - if (expn1 >= max_exponent[radix - 2] + 1) - goto overflow; - else if (expn1 <= min_exponent[radix - 2]) - goto underflow; - m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN); - } - if (m == 0) { - underflow: - a = 0; - } else if (e > 1024) { - overflow: - /* overflow */ - a = (uint64_t)0x7ff << 52; - } else if (e < -1073) { - /* underflow */ - /* XXX: check rounding */ - a = 0; - } else if (e < -1021) { - /* subnormal */ - a = m >> (-e - 1021); - } else { - a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1)); - } - } - done: - a |= (uint64_t)is_neg << 63; - dval = uint64_as_float64(a); - done1: - if (pnext) - *pnext = p; - dtoa_free(tmp0); - return dval; - fail: - dval = NAN; - goto done1; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.h deleted file mode 100755 index de76f1a3..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -//#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); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/fib.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/fib.c deleted file mode 100755 index be90af57..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/fib.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * QuickJS: Example of C module - * - * 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. - */ -#include "../quickjs.h" - -#define countof(x) (sizeof(x) / sizeof((x)[0])) - -static int fib(int n) -{ - if (n <= 0) - return 0; - else if (n == 1) - return 1; - else - return fib(n - 1) + fib(n - 2); -} - -static JSValue js_fib(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int n, res; - if (JS_ToInt32(ctx, &n, argv[0])) - return JS_EXCEPTION; - res = fib(n); - return JS_NewInt32(ctx, res); -} - -static const JSCFunctionListEntry js_fib_funcs[] = { - JS_CFUNC_DEF("fib", 1, js_fib ), -}; - -static int js_fib_init(JSContext *ctx, JSModuleDef *m) -{ - return JS_SetModuleExportList(ctx, m, js_fib_funcs, - countof(js_fib_funcs)); -} - -#ifdef JS_SHARED_LIBRARY -#define JS_INIT_MODULE js_init_module -#else -#define JS_INIT_MODULE js_init_module_fib -#endif - -JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_fib_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs)); - return m; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/fib_module.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/fib_module.js deleted file mode 100755 index 6a810716..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/fib_module.js +++ /dev/null @@ -1,10 +0,0 @@ -/* fib module */ -export function fib(n) -{ - if (n <= 0) - return 0; - else if (n == 1) - return 1; - else - return fib(n - 1) + fib(n - 2); -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/hello.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/hello.js deleted file mode 100755 index accefceb..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/hello.js +++ /dev/null @@ -1 +0,0 @@ -console.log("Hello World"); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/hello_module.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/hello_module.js deleted file mode 100755 index 5d4c78ef..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/hello_module.js +++ /dev/null @@ -1,8 +0,0 @@ -/* example of JS and JSON modules */ - -import { fib } from "./fib_module.js"; -import msg from "./message.json"; - -console.log("Hello World"); -console.log("fib(10)=", fib(10)); -console.log("msg=", msg); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/message.json b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/message.json deleted file mode 100755 index 3b7fe480..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/message.json +++ /dev/null @@ -1,2 +0,0 @@ -{ "x" : 1, "tab": [ 1, 2, 3 ] } - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/pi_bigint.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/pi_bigint.js deleted file mode 100755 index e15abd17..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/pi_bigint.js +++ /dev/null @@ -1,118 +0,0 @@ -/* - * PI computation in Javascript using the BigInt type - */ -"use strict"; - -/* return floor(log2(a)) for a > 0 and 0 for a = 0 */ -function floor_log2(a) -{ - var k_max, a1, k, i; - k_max = 0n; - while ((a >> (2n ** k_max)) != 0n) { - k_max++; - } - k = 0n; - a1 = a; - for(i = k_max - 1n; i >= 0n; i--) { - a1 = a >> (2n ** i); - if (a1 != 0n) { - a = a1; - k |= (1n << i); - } - } - return k; -} - -/* return ceil(log2(a)) for a > 0 */ -function ceil_log2(a) -{ - return floor_log2(a - 1n) + 1n; -} - -/* return floor(sqrt(a)) (not efficient but simple) */ -function int_sqrt(a) -{ - var l, u, s; - if (a == 0n) - return a; - l = ceil_log2(a); - u = 1n << ((l + 1n) / 2n); - /* u >= floor(sqrt(a)) */ - for(;;) { - s = u; - u = ((a / s) + s) / 2n; - if (u >= s) - break; - } - return s; -} - -/* return pi * 2**prec */ -function calc_pi(prec) { - const CHUD_A = 13591409n; - const CHUD_B = 545140134n; - const CHUD_C = 640320n; - const CHUD_C3 = 10939058860032000n; /* C^3/24 */ - const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */ - - /* return [P, Q, G] */ - function chud_bs(a, b, need_G) { - var c, P, Q, G, P1, Q1, G1, P2, Q2, G2; - if (a == (b - 1n)) { - G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n); - P = G * (CHUD_B * b + CHUD_A); - if (b & 1n) - P = -P; - Q = b * b * b * CHUD_C3; - } else { - c = (a + b) >> 1n; - [P1, Q1, G1] = chud_bs(a, c, true); - [P2, Q2, G2] = chud_bs(c, b, need_G); - P = P1 * Q2 + P2 * G1; - Q = Q1 * Q2; - if (need_G) - G = G1 * G2; - else - G = 0n; - } - return [P, Q, G]; - } - - var n, P, Q, G; - /* number of serie terms */ - n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n; - [P, Q, G] = chud_bs(0n, n, false); - Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A); - G = int_sqrt(CHUD_C << (2n * prec)); - return (Q * G) >> prec; -} - -function main(args) { - var r, n_digits, n_bits, out; - if (args.length < 1) { - print("usage: pi n_digits"); - return; - } - n_digits = args[0] | 0; - - /* we add more bits to reduce the probability of bad rounding for - the last digits */ - n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n; - r = calc_pi(n_bits); - r = ((10n ** BigInt(n_digits)) * r) >> n_bits; - out = r.toString(); - print(out[0] + "." + out.slice(1)); -} - -var args; -if (typeof scriptArgs != "undefined") { - args = scriptArgs; - args.shift(); -} else if (typeof arguments != "undefined") { - args = arguments; -} else { - /* default: 1000 digits */ - args=[1000]; -} - -main(args); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/point.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/point.c deleted file mode 100755 index 0386230e..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/point.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * QuickJS: Example of C module with a class - * - * Copyright (c) 2019 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. - */ -#include "../quickjs.h" -#include - -#define countof(x) (sizeof(x) / sizeof((x)[0])) - -/* Point Class */ - -typedef struct { - int x; - int y; -} JSPointData; - -static JSClassID js_point_class_id; - -static void js_point_finalizer(JSRuntime *rt, JSValue val) -{ - JSPointData *s = JS_GetOpaque(val, js_point_class_id); - /* Note: 's' can be NULL in case JS_SetOpaque() was not called */ - js_free_rt(rt, s); -} - -static JSValue js_point_ctor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSPointData *s; - JSValue obj = JS_UNDEFINED; - JSValue proto; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &s->x, argv[0])) - goto fail; - if (JS_ToInt32(ctx, &s->y, argv[1])) - goto fail; - /* using new_target to get the prototype is necessary when the - class is extended. */ - proto = JS_GetPropertyStr(ctx, new_target, "prototype"); - if (JS_IsException(proto)) - goto fail; - obj = JS_NewObjectProtoClass(ctx, proto, js_point_class_id); - JS_FreeValue(ctx, proto); - if (JS_IsException(obj)) - goto fail; - JS_SetOpaque(obj, s); - return obj; - fail: - js_free(ctx, s); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_point_get_xy(JSContext *ctx, JSValueConst this_val, int magic) -{ - JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id); - if (!s) - return JS_EXCEPTION; - if (magic == 0) - return JS_NewInt32(ctx, s->x); - else - return JS_NewInt32(ctx, s->y); -} - -static JSValue js_point_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) -{ - JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id); - int v; - if (!s) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &v, val)) - return JS_EXCEPTION; - if (magic == 0) - s->x = v; - else - s->y = v; - return JS_UNDEFINED; -} - -static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id); - if (!s) - return JS_EXCEPTION; - return JS_NewFloat64(ctx, sqrt((double)s->x * s->x + (double)s->y * s->y)); -} - -static JSClassDef js_point_class = { - "Point", - .finalizer = js_point_finalizer, -}; - -static const JSCFunctionListEntry js_point_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0), - JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1), - JS_CFUNC_DEF("norm", 0, js_point_norm), -}; - -static int js_point_init(JSContext *ctx, JSModuleDef *m) -{ - JSValue point_proto, point_class; - - /* create the Point class */ - JS_NewClassID(&js_point_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class); - - point_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs)); - - point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0); - /* set proto.constructor and ctor.prototype */ - JS_SetConstructor(ctx, point_class, point_proto); - JS_SetClassProto(ctx, js_point_class_id, point_proto); - - JS_SetModuleExport(ctx, m, "Point", point_class); - return 0; -} - -JSModuleDef *js_init_module(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_point_init); - if (!m) - return NULL; - JS_AddModuleExport(ctx, m, "Point"); - return m; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/test_fib.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/test_fib.js deleted file mode 100755 index 70d26bd8..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/test_fib.js +++ /dev/null @@ -1,6 +0,0 @@ -/* example of JS module importing a C module */ - -import { fib } from "./fib.so"; - -console.log("Hello World"); -console.log("fib(10)=", fib(10)); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/test_point.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/test_point.js deleted file mode 100755 index 0659bc35..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/examples/test_point.js +++ /dev/null @@ -1,40 +0,0 @@ -/* example of JS module importing a C module */ -import { Point } from "./point.so"; - -function assert(b, str) -{ - if (b) { - return; - } else { - throw Error("assertion failed: " + str); - } -} - -class ColorPoint extends Point { - constructor(x, y, color) { - super(x, y); - this.color = color; - } - get_color() { - return this.color; - } -}; - -function main() -{ - var pt, pt2; - - pt = new Point(2, 3); - assert(pt.x === 2); - assert(pt.y === 3); - pt.x = 4; - assert(pt.x === 4); - assert(pt.norm() == 5); - - pt2 = new ColorPoint(2, 3, 0xffffff); - assert(pt2.x === 2); - assert(pt2.color === 0xffffff); - assert(pt2.get_color() === 0xffffff); -} - -main(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/README b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/README deleted file mode 100755 index 18c71cd3..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/README +++ /dev/null @@ -1,27 +0,0 @@ -libFuzzer support for QuickJS -============================= - -Build QuickJS with libFuzzer support as follows: - - CONFIG_CLANG=y make libfuzzer - -This can be extended with sanitizer support to improve efficacy: - - CONFIG_CLANG=y CONFIG_ASAN=y make libfuzzer - - -Currently, there are three fuzzing targets defined: fuzz_eval, fuzz_compile and fuzz_regexp. -The above build command will produce an executable binary for each of them, which can be -simply executed as: - - ./fuzz_eval - -or with an initial corpus: - - ./fuzz_compile corpus_dir/ - -or with a predefined dictionary to improve its efficacy: - - ./fuzz_eval -dict fuzz/fuzz.dict - -or with arbitrary CLI arguments provided by libFuzzer (https://llvm.org/docs/LibFuzzer.html). diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz.dict b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz.dict deleted file mode 100755 index f31eb9f4..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz.dict +++ /dev/null @@ -1,254 +0,0 @@ -"__loadScript" -"abs" -"acos" -"acosh" -"add" -"AggregateError" -"and" -"apply" -"Array" -"ArrayBuffer" -"asin" -"asinh" -"atan" -"atan2" -"atanh" -"Atomics" -"BigInt" -"BigInt64Array" -"BigUint64Array" -"Boolean" -"cbrt" -"ceil" -"chdir" -"clearTimeout" -"close" -"clz32" -"compareExchange" -"console" -"construct" -"cos" -"cosh" -"DataView" -"Date" -"decodeURI" -"decodeURIComponent" -"defineProperty" -"deleteProperty" -"dup" -"dup2" -"E" -"encodeURI" -"encodeURIComponent" -"err" -"Error" -"escape" -"eval" -"EvalError" -"evalScript" -"exchange" -"exec" -"exit" -"exp" -"expm1" -"fdopen" -"Float32Array" -"Float64Array" -"floor" -"fround" -"Function" -"gc" -"get" -"getcwd" -"getenv" -"getenviron" -"getOwnPropertyDescriptor" -"getpid" -"getPrototypeOf" -"globalThis" -"has" -"hypot" -"imul" -"in" -"Infinity" -"Int16Array" -"Int32Array" -"Int8Array" -"InternalError" -"isatty" -"isExtensible" -"isFinite" -"isLockFree" -"isNaN" -"iterateBuiltIns" -"JSON" -"kill" -"length" -"LN10" -"LN2" -"load" -"loadFile" -"loadScript" -"log" -"log10" -"LOG10E" -"log1p" -"log2" -"LOG2E" -"lstat" -"Map" -"Math" -"max" -"min" -"mkdir" -"NaN" -"notify" -"now" -"Number" -"O_APPEND" -"O_CREAT" -"O_EXCL" -"O_RDONLY" -"O_RDWR" -"O_TRUNC" -"O_WRONLY" -"Object" -"open" -"Operators" -"or" -"os" -"out" -"ownKeys" -"parse" -"parseExtJSON" -"parseFloat" -"parseInt" -"PI" -"pipe" -"platform" -"popen" -"pow" -"preventExtensions" -"print" -"printf" -"Promise" -"Proxy" -"puts" -"random" -"RangeError" -"read" -"readdir" -"readlink" -"realpath" -"ReferenceError" -"Reflect" -"RegExp" -"remove" -"rename" -"round" -"S_IFBLK" -"S_IFCHR" -"S_IFDIR" -"S_IFIFO" -"S_IFLNK" -"S_IFMT" -"S_IFREG" -"S_IFSOCK" -"S_ISGID" -"S_ISUID" -"scriptArgs" -"seek" -"SEEK_CUR" -"SEEK_END" -"SEEK_SET" -"set" -"Set" -"setenv" -"setPrototypeOf" -"setReadHandler" -"setTimeout" -"setWriteHandler" -"SharedArrayBuffer" -"SIGABRT" -"SIGALRM" -"SIGCHLD" -"SIGCONT" -"SIGFPE" -"SIGILL" -"SIGINT" -"sign" -"signal" -"SIGPIPE" -"SIGQUIT" -"SIGSEGV" -"SIGSTOP" -"SIGTERM" -"SIGTSTP" -"SIGTTIN" -"SIGTTOU" -"SIGUSR1" -"SIGUSR2" -"sin" -"sinh" -"sleep" -"sleepAsync" -"sprintf" -"sqrt" -"SQRT1_2" -"SQRT2" -"stat" -"std" -"store" -"strerror" -"String" -"stringify" -"sub" -"Symbol" -"symlink" -"SyntaxError" -"tan" -"tanh" -"tmpfile" -"trunc" -"ttyGetWinSize" -"ttySetRaw" -"TypeError" -"Uint16Array" -"Uint32Array" -"Uint8Array" -"Uint8ClampedArray" -"undefined" -"unescape" -"unsetenv" -"URIError" -"urlGet" -"utimes" -"wait" -"waitpid" -"WeakMap" -"WeakSet" -"WNOHANG" -"Worker" -"write" -"xor" -"v0" -"v1" -"v2" -"v3" -"v4" -"v5" -"v6" -"v7" -"v8" -"v9" -"v10" -"v11" -"v12" -"v13" -"v14" -"v15" -"v16" -"v17" -"v18" -"v19" -"v20" diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_common.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_common.c deleted file mode 100755 index 92548d20..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_common.c +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2020 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include - -#include "fuzz/fuzz_common.h" - -// handle timeouts from infinite loops -static int interrupt_handler(JSRuntime *rt, void *opaque) -{ - nbinterrupts++; - return (nbinterrupts > 100); -} - -void reset_nbinterrupts() { - nbinterrupts = 0; -} - -void test_one_input_init(JSRuntime *rt, JSContext *ctx) { - // 64 Mo - JS_SetMemoryLimit(rt, 0x4000000); - // 64 Kb - JS_SetMaxStackSize(rt, 0x10000); - - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); - JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); - js_std_add_helpers(ctx, 0, NULL); - - // Load os and std - js_std_init_handlers(rt); - js_init_module_std(ctx, "std"); - js_init_module_os(ctx, "os"); - const char *str = "import * as std from 'std';\n" - "import * as os from 'os';\n" - "globalThis.std = std;\n" - "globalThis.os = os;\n"; - JSValue std_val = JS_Eval(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - if (!JS_IsException(std_val)) { - js_module_set_import_meta(ctx, std_val, 1, 1); - std_val = JS_EvalFunction(ctx, std_val); - } else { - js_std_dump_error(ctx); - } - std_val = js_std_await(ctx, std_val); - JS_FreeValue(ctx, std_val); -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_common.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_common.h deleted file mode 100755 index 10cb4976..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_common.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2020 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "quickjs.h" -#include "quickjs-libc.h" - -static int nbinterrupts = 0; - -void reset_nbinterrupts(); -void test_one_input_init(JSRuntime *rt, JSContext *ctx); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_compile.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_compile.c deleted file mode 100755 index 0ab1b033..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_compile.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright 2020 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "quickjs.h" -#include "quickjs-libc.h" -#include "cutils.h" -#include "fuzz/fuzz_common.h" - -#include -#include - - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - if (size == 0) - return 0; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - test_one_input_init(rt, ctx); - - uint8_t *null_terminated_data = malloc(size + 1); - memcpy(null_terminated_data, data, size); - null_terminated_data[size] = 0; - - JSValue obj = JS_Eval(ctx, (const char *)null_terminated_data, size, "", JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_MODULE); - free(null_terminated_data); - //TODO target with JS_ParseJSON - if (JS_IsException(obj)) { - js_std_free_handlers(rt); - JS_FreeValue(ctx, obj); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; - } - obj = js_std_await(ctx, obj); - size_t bytecode_size; - uint8_t* bytecode = JS_WriteObject(ctx, &bytecode_size, obj, JS_WRITE_OBJ_BYTECODE); - JS_FreeValue(ctx, obj); - if (!bytecode) { - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; - } - obj = JS_ReadObject(ctx, bytecode, bytecode_size, JS_READ_OBJ_BYTECODE); - js_free(ctx, bytecode); - if (JS_IsException(obj)) { - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; - } - reset_nbinterrupts(); - /* this is based on - * js_std_eval_binary(ctx, bytecode, bytecode_size, 0); - * modified so as not to exit on JS exception - */ - JSValue val; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - if (JS_ResolveModule(ctx, obj) < 0) { - JS_FreeValue(ctx, obj); - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; - } - js_module_set_import_meta(ctx, obj, FALSE, TRUE); - } - val = JS_EvalFunction(ctx, obj); - if (JS_IsException(val)) { - js_std_dump_error(ctx); - } else { - js_std_loop(ctx); - } - JS_FreeValue(ctx, val); - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_eval.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_eval.c deleted file mode 100755 index aa26f1ef..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_eval.c +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2020 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "quickjs.h" -#include "quickjs-libc.h" -#include "fuzz/fuzz_common.h" - -#include -#include -#include - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - if (size == 0) - return 0; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - test_one_input_init(rt, ctx); - - uint8_t *null_terminated_data = malloc(size + 1); - memcpy(null_terminated_data, data, size); - null_terminated_data[size] = 0; - - reset_nbinterrupts(); - //the final 0 does not count (as in strlen) - JSValue val = JS_Eval(ctx, (const char *)null_terminated_data, size, "", JS_EVAL_TYPE_GLOBAL); - free(null_terminated_data); - //TODO targets with JS_ParseJSON, JS_ReadObject - if (!JS_IsException(val)) { - js_std_loop(ctx); - JS_FreeValue(ctx, val); - } - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_regexp.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_regexp.c deleted file mode 100755 index ae929e84..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/fuzz_regexp.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright 2020 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "libregexp.h" -#include "quickjs-libc.h" - -static int nbinterrupts = 0; - -int lre_check_stack_overflow(void *opaque, size_t alloca_size) { return 0; } - -void *lre_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -int lre_check_timeout(void *opaque) - { - nbinterrupts++; - return (nbinterrupts > 100); - } - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - int len, ret, i; - uint8_t *bc; - char error_msg[64]; - const uint8_t *input; - uint8_t *capture[255 * 2]; - size_t size1 = size; - - //Splits buffer into 2 sub buffers delimited by null character - for (i = 0; i < size; i++) { - if (data[i] == 0) { - size1 = i; - break; - } - } - if (size1 == size) { - //missing delimiter - return 0; - } - bc = lre_compile(&len, error_msg, sizeof(error_msg), (const char *) data, - size1, 0, NULL); - if (!bc) { - return 0; - } - input = data + size1 + 1; - ret = lre_exec(capture, bc, input, 0, size - (size1 + 1), 0, NULL); - if (ret == 1) { - lre_get_capture_count(bc); - } - free(bc); - - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/generate_dict.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/generate_dict.js deleted file mode 100755 index 1366fea1..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz/generate_dict.js +++ /dev/null @@ -1,24 +0,0 @@ -// Function to recursively iterate through built-in names. -function collectBuiltinNames(obj, visited = new Set(), result = new Set()) { - // Check if the object has already been visited to avoid infinite recursion. - if (visited.has(obj)) - return; - - // Add the current object to the set of visited objects - visited.add(obj); - // Get the property names of the current object - const properties = Object.getOwnPropertyNames(obj); - // Iterate through each property - for (var i=0; i < properties.length; i++) { - var property = properties[i]; - if (property != "collectBuiltinNames" && typeof property != "number") - result.add(property); - // Check if the property is an object and if so, recursively iterate through its properties. - if (typeof obj[property] === 'object' && obj[property] !== null) - collectBuiltinNames(obj[property], visited, result); - } - return result; -} - -// Start the recursive iteration with the global object. -console.log(Array.from(collectBuiltinNames(this)).join('\n')); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp-opcode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp-opcode.h deleted file mode 100755 index 9908cf37..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp-opcode.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -#ifdef DEF - -DEF(invalid, 1) /* never used */ -DEF(char, 3) -DEF(char_i, 3) -DEF(char32, 5) -DEF(char32_i, 5) -DEF(dot, 1) -DEF(any, 1) /* same as dot but match any character including line terminator */ -DEF(line_start, 1) -DEF(line_start_m, 1) -DEF(line_end, 1) -DEF(line_end_m, 1) -DEF(goto, 5) -DEF(split_goto_first, 5) -DEF(split_next_first, 5) -DEF(match, 1) -DEF(lookahead_match, 1) -DEF(negative_lookahead_match, 1) /* must come after */ -DEF(save_start, 2) /* save start position */ -DEF(save_end, 2) /* save end position, must come after saved_start */ -DEF(save_reset, 3) /* reset save positions */ -DEF(loop, 6) /* decrement the top the stack and goto if != 0 */ -DEF(push_i32, 6) /* push integer on the stack */ -DEF(word_boundary, 1) -DEF(word_boundary_i, 1) -DEF(not_word_boundary, 1) -DEF(not_word_boundary_i, 1) -DEF(back_reference, 2) -DEF(back_reference_i, 2) /* must come after */ -DEF(backward_back_reference, 2) /* must come after */ -DEF(backward_back_reference_i, 2) /* must come after */ -DEF(range, 3) /* variable length */ -DEF(range_i, 3) /* variable length */ -DEF(range32, 3) /* variable length */ -DEF(range32_i, 3) /* variable length */ -DEF(lookahead, 5) -DEF(negative_lookahead, 5) /* must come after */ -DEF(push_char_pos, 2) /* push the character position on the stack */ -DEF(check_advance, 2) /* pop one stack element and check that it is different from the character position */ -DEF(prev, 1) /* go to the previous char */ - -#endif /* DEF */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c deleted file mode 100755 index 28f407b7..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c +++ /dev/null @@ -1,3261 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "libregexp.h" -#include "libunicode.h" - -/* - TODO: - - - Add a lock step execution mode (=linear time execution guaranteed) - when the regular expression is "simple" i.e. no backreference nor - complicated lookahead. The opcodes are designed for this execution - model. -*/ - -#if defined(TEST) -#define DUMP_REOP -#endif -//#define DUMP_REOP -//#define DUMP_EXEC - -typedef enum { -#define DEF(id, size) REOP_ ## id, -#include "libregexp-opcode.h" -#undef DEF - REOP_COUNT, -} REOPCodeEnum; - -#define CAPTURE_COUNT_MAX 255 -#define STACK_SIZE_MAX 255 -/* must be large enough to have a negligible runtime cost and small - enough to call the interrupt callback often. */ -#define INTERRUPT_COUNTER_INIT 10000 - -/* unicode code points */ -#define CP_LS 0x2028 -#define CP_PS 0x2029 - -#define TMP_BUF_SIZE 128 - -typedef struct { - DynBuf byte_code; - const uint8_t *buf_ptr; - const uint8_t *buf_end; - const uint8_t *buf_start; - int re_flags; - BOOL is_unicode; - BOOL unicode_sets; /* if set, is_unicode is also set */ - BOOL ignore_case; - BOOL multi_line; - BOOL dotall; - int capture_count; - int total_capture_count; /* -1 = not computed yet */ - int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ - void *opaque; - DynBuf group_names; - union { - char error_msg[TMP_BUF_SIZE]; - char tmp_buf[TMP_BUF_SIZE]; - } u; -} REParseState; - -typedef struct { -#ifdef DUMP_REOP - const char *name; -#endif - uint8_t size; -} REOpCode; - -static const REOpCode reopcode_info[REOP_COUNT] = { -#ifdef DUMP_REOP -#define DEF(id, size) { #id, size }, -#else -#define DEF(id, size) { size }, -#endif -#include "libregexp-opcode.h" -#undef DEF -}; - -#define RE_HEADER_FLAGS 0 -#define RE_HEADER_CAPTURE_COUNT 2 -#define RE_HEADER_STACK_SIZE 3 -#define RE_HEADER_BYTECODE_LEN 4 - -#define RE_HEADER_LEN 8 - -static inline int is_digit(int c) { - return c >= '0' && c <= '9'; -} - -/* insert 'len' bytes at position 'pos'. Return < 0 if error. */ -static int dbuf_insert(DynBuf *s, int pos, int len) -{ - if (dbuf_claim(s, len)) - return -1; - memmove(s->buf + pos + len, s->buf + pos, s->size - pos); - s->size += len; - return 0; -} - -typedef struct REString { - struct REString *next; - uint32_t hash; - uint32_t len; - uint32_t buf[]; -} REString; - -typedef struct { - /* the string list is the union of 'char_range' and of the strings - in hash_table[]. The strings in hash_table[] have a length != - 1. */ - CharRange cr; - uint32_t n_strings; - uint32_t hash_size; - int hash_bits; - REString **hash_table; -} REStringList; - -static uint32_t re_string_hash(int len, const uint32_t *buf) -{ - int i; - uint32_t h; - h = 1; - for(i = 0; i < len; i++) - h = h * 263 + buf[i]; - return h * 0x61C88647; -} - -static void re_string_list_init(REParseState *s1, REStringList *s) -{ - cr_init(&s->cr, s1->opaque, lre_realloc); - s->n_strings = 0; - s->hash_size = 0; - s->hash_bits = 0; - s->hash_table = NULL; -} - -static void re_string_list_free(REStringList *s) -{ - REString *p, *p_next; - int i; - for(i = 0; i < s->hash_size; i++) { - for(p = s->hash_table[i]; p != NULL; p = p_next) { - p_next = p->next; - lre_realloc(s->cr.mem_opaque, p, 0); - } - } - lre_realloc(s->cr.mem_opaque, s->hash_table, 0); - - cr_free(&s->cr); -} - -static void lre_print_char(int c, BOOL is_range) -{ - if (c == '\'' || c == '\\' || - (is_range && (c == '-' || c == ']'))) { - printf("\\%c", c); - } else if (c >= ' ' && c <= 126) { - printf("%c", c); - } else { - printf("\\u{%04x}", c); - } -} - -static __maybe_unused void re_string_list_dump(const char *str, const REStringList *s) -{ - REString *p; - const CharRange *cr; - int i, j, k; - - printf("%s:\n", str); - printf(" ranges: ["); - cr = &s->cr; - for(i = 0; i < cr->len; i += 2) { - lre_print_char(cr->points[i], TRUE); - if (cr->points[i] != cr->points[i + 1] - 1) { - printf("-"); - lre_print_char(cr->points[i + 1] - 1, TRUE); - } - } - printf("]\n"); - - j = 0; - for(i = 0; i < s->hash_size; i++) { - for(p = s->hash_table[i]; p != NULL; p = p->next) { - printf(" %d/%d: '", j, s->n_strings); - for(k = 0; k < p->len; k++) { - lre_print_char(p->buf[k], FALSE); - } - printf("'\n"); - j++; - } - } -} - -static int re_string_find2(REStringList *s, int len, const uint32_t *buf, - uint32_t h0, BOOL add_flag) -{ - uint32_t h = 0; /* avoid warning */ - REString *p; - if (s->n_strings != 0) { - h = h0 >> (32 - s->hash_bits); - for(p = s->hash_table[h]; p != NULL; p = p->next) { - if (p->hash == h0 && p->len == len && - !memcmp(p->buf, buf, len * sizeof(buf[0]))) { - return 1; - } - } - } - /* not found */ - if (!add_flag) - return 0; - /* increase the size of the hash table if needed */ - if (unlikely((s->n_strings + 1) > s->hash_size)) { - REString **new_hash_table, *p_next; - int new_hash_bits, i; - uint32_t new_hash_size; - new_hash_bits = max_int(s->hash_bits + 1, 4); - new_hash_size = 1 << new_hash_bits; - new_hash_table = lre_realloc(s->cr.mem_opaque, NULL, - sizeof(new_hash_table[0]) * new_hash_size); - if (!new_hash_table) - return -1; - memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size); - for(i = 0; i < s->hash_size; i++) { - for(p = s->hash_table[i]; p != NULL; p = p_next) { - p_next = p->next; - h = p->hash >> (32 - new_hash_bits); - p->next = new_hash_table[h]; - new_hash_table[h] = p; - } - } - lre_realloc(s->cr.mem_opaque, s->hash_table, 0); - s->hash_bits = new_hash_bits; - s->hash_size = new_hash_size; - s->hash_table = new_hash_table; - h = h0 >> (32 - s->hash_bits); - } - - p = lre_realloc(s->cr.mem_opaque, NULL, sizeof(REString) + len * sizeof(buf[0])); - if (!p) - return -1; - p->next = s->hash_table[h]; - s->hash_table[h] = p; - s->n_strings++; - p->hash = h0; - p->len = len; - memcpy(p->buf, buf, sizeof(buf[0]) * len); - return 1; -} - -static int re_string_find(REStringList *s, int len, const uint32_t *buf, - BOOL add_flag) -{ - uint32_t h0; - h0 = re_string_hash(len, buf); - return re_string_find2(s, len, buf, h0, add_flag); -} - -/* return -1 if memory error, 0 if OK */ -static int re_string_add(REStringList *s, int len, const uint32_t *buf) -{ - if (len == 1) { - return cr_union_interval(&s->cr, buf[0], buf[0]); - } - if (re_string_find(s, len, buf, TRUE) < 0) - return -1; - return 0; -} - -/* a = a op b */ -static int re_string_list_op(REStringList *a, REStringList *b, int op) -{ - int i, ret; - REString *p, **pp; - - if (cr_op1(&a->cr, b->cr.points, b->cr.len, op)) - return -1; - - switch(op) { - case CR_OP_UNION: - if (b->n_strings != 0) { - for(i = 0; i < b->hash_size; i++) { - for(p = b->hash_table[i]; p != NULL; p = p->next) { - if (re_string_find2(a, p->len, p->buf, p->hash, TRUE) < 0) - return -1; - } - } - } - break; - case CR_OP_INTER: - case CR_OP_SUB: - for(i = 0; i < a->hash_size; i++) { - pp = &a->hash_table[i]; - for(;;) { - p = *pp; - if (p == NULL) - break; - ret = re_string_find2(b, p->len, p->buf, p->hash, FALSE); - if (op == CR_OP_SUB) - ret = !ret; - if (!ret) { - /* remove it */ - *pp = p->next; - a->n_strings--; - lre_realloc(a->cr.mem_opaque, p, 0); - } else { - /* keep it */ - pp = &p->next; - } - } - } - break; - default: - abort(); - } - return 0; -} - -static int re_string_list_canonicalize(REParseState *s1, - REStringList *s, BOOL is_unicode) -{ - if (cr_regexp_canonicalize(&s->cr, is_unicode)) - return -1; - if (s->n_strings != 0) { - REStringList a_s, *a = &a_s; - int i, j; - REString *p; - - /* XXX: simplify */ - re_string_list_init(s1, a); - - a->n_strings = s->n_strings; - a->hash_size = s->hash_size; - a->hash_bits = s->hash_bits; - a->hash_table = s->hash_table; - - s->n_strings = 0; - s->hash_size = 0; - s->hash_bits = 0; - s->hash_table = NULL; - - for(i = 0; i < a->hash_size; i++) { - for(p = a->hash_table[i]; p != NULL; p = p->next) { - for(j = 0; j < p->len; j++) { - p->buf[j] = lre_canonicalize(p->buf[j], is_unicode); - } - if (re_string_add(s, p->len, p->buf)) { - re_string_list_free(a); - return -1; - } - } - } - re_string_list_free(a); - } - return 0; -} - -static const uint16_t char_range_d[] = { - 1, - 0x0030, 0x0039 + 1, -}; - -/* code point ranges for Zs,Zl or Zp property */ -static const uint16_t char_range_s[] = { - 10, - 0x0009, 0x000D + 1, - 0x0020, 0x0020 + 1, - 0x00A0, 0x00A0 + 1, - 0x1680, 0x1680 + 1, - 0x2000, 0x200A + 1, - /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ - /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ - 0x2028, 0x2029 + 1, - 0x202F, 0x202F + 1, - 0x205F, 0x205F + 1, - 0x3000, 0x3000 + 1, - /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ - 0xFEFF, 0xFEFF + 1, -}; - -static const uint16_t char_range_w[] = { - 4, - 0x0030, 0x0039 + 1, - 0x0041, 0x005A + 1, - 0x005F, 0x005F + 1, - 0x0061, 0x007A + 1, -}; - -#define CLASS_RANGE_BASE 0x40000000 - -typedef enum { - CHAR_RANGE_d, - CHAR_RANGE_D, - CHAR_RANGE_s, - CHAR_RANGE_S, - CHAR_RANGE_w, - CHAR_RANGE_W, -} CharRangeEnum; - -static const uint16_t * const char_range_table[] = { - char_range_d, - char_range_s, - char_range_w, -}; - -static int cr_init_char_range(REParseState *s, REStringList *cr, uint32_t c) -{ - BOOL invert; - const uint16_t *c_pt; - int len, i; - - invert = c & 1; - c_pt = char_range_table[c >> 1]; - len = *c_pt++; - re_string_list_init(s, cr); - for(i = 0; i < len * 2; i++) { - if (cr_add_point(&cr->cr, c_pt[i])) - goto fail; - } - if (invert) { - if (cr_invert(&cr->cr)) - goto fail; - } - return 0; - fail: - re_string_list_free(cr); - return -1; -} - -#ifdef DUMP_REOP -static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, - int buf_len) -{ - int pos, len, opcode, bc_len, re_flags, i; - uint32_t val, val2; - - assert(buf_len >= RE_HEADER_LEN); - - re_flags = lre_get_flags(buf); - bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); - assert(bc_len + RE_HEADER_LEN <= buf_len); - printf("flags: 0x%x capture_count=%d aux_stack_size=%d\n", - re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); - if (re_flags & LRE_FLAG_NAMED_GROUPS) { - const char *p; - p = (char *)buf + RE_HEADER_LEN + bc_len; - printf("named groups: "); - for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) { - if (i != 1) - printf(","); - printf("<%s>", p); - p += strlen(p) + 1; - } - printf("\n"); - assert(p == (char *)(buf + buf_len)); - } - printf("bytecode_len=%d\n", bc_len); - - buf += RE_HEADER_LEN; - pos = 0; - while (pos < bc_len) { - printf("%5u: ", pos); - opcode = buf[pos]; - len = reopcode_info[opcode].size; - if (opcode >= REOP_COUNT) { - printf(" invalid opcode=0x%02x\n", opcode); - break; - } - if ((pos + len) > bc_len) { - printf(" buffer overflow (opcode=0x%02x)\n", opcode); - break; - } - printf("%s", reopcode_info[opcode].name); - switch(opcode) { - case REOP_char: - case REOP_char_i: - val = get_u16(buf + pos + 1); - if (val >= ' ' && val <= 126) - printf(" '%c'", val); - else - printf(" 0x%04x", val); - break; - case REOP_char32: - case REOP_char32_i: - val = get_u32(buf + pos + 1); - if (val >= ' ' && val <= 126) - printf(" '%c'", val); - else - printf(" 0x%08x", val); - break; - case REOP_goto: - case REOP_split_goto_first: - case REOP_split_next_first: - case REOP_lookahead: - case REOP_negative_lookahead: - val = get_u32(buf + pos + 1); - val += (pos + 5); - printf(" %u", val); - break; - case REOP_loop: - val2 = buf[pos + 1]; - val = get_u32(buf + pos + 2); - val += (pos + 6); - printf(" %u, %u", val2, val); - break; - case REOP_save_start: - case REOP_save_end: - case REOP_back_reference: - case REOP_back_reference_i: - case REOP_backward_back_reference: - case REOP_backward_back_reference_i: - printf(" %u", buf[pos + 1]); - break; - case REOP_save_reset: - printf(" %u %u", buf[pos + 1], buf[pos + 2]); - break; - case REOP_push_i32: - val = buf[pos + 1]; - val2 = get_u32(buf + pos + 2); - printf(" %u, %d", val, val2); - break; - case REOP_push_char_pos: - case REOP_check_advance: - val = buf[pos + 1]; - printf(" %u", val); - break; - case REOP_range: - case REOP_range_i: - { - int n, i; - n = get_u16(buf + pos + 1); - len += n * 4; - for(i = 0; i < n * 2; i++) { - val = get_u16(buf + pos + 3 + i * 2); - printf(" 0x%04x", val); - } - } - break; - case REOP_range32: - case REOP_range32_i: - { - int n, i; - n = get_u16(buf + pos + 1); - len += n * 8; - for(i = 0; i < n * 2; i++) { - val = get_u32(buf + pos + 3 + i * 4); - printf(" 0x%08x", val); - } - } - break; - default: - break; - } - printf("\n"); - pos += len; - } -} -#endif - -static void re_emit_op(REParseState *s, int op) -{ - dbuf_putc(&s->byte_code, op); -} - -/* return the offset of the u32 value */ -static int re_emit_op_u32(REParseState *s, int op, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val); - return pos; -} - -static int re_emit_goto(REParseState *s, int op, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val - (pos + 4)); - return pos; -} - -static int re_emit_goto_u8(REParseState *s, int op, uint32_t arg, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - dbuf_putc(&s->byte_code, arg); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val - (pos + 4)); - return pos; -} - -static void re_emit_op_u8(REParseState *s, int op, uint32_t val) -{ - dbuf_putc(&s->byte_code, op); - dbuf_putc(&s->byte_code, val); -} - -static void re_emit_op_u16(REParseState *s, int op, uint32_t val) -{ - dbuf_putc(&s->byte_code, op); - dbuf_put_u16(&s->byte_code, val); -} - -static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap); - va_end(ap); - return -1; -} - -static int re_parse_out_of_memory(REParseState *s) -{ - return re_parse_error(s, "out of memory"); -} - -/* If allow_overflow is false, return -1 in case of - overflow. Otherwise return INT32_MAX. */ -static int parse_digits(const uint8_t **pp, BOOL allow_overflow) -{ - const uint8_t *p; - uint64_t v; - int c; - - p = *pp; - v = 0; - for(;;) { - c = *p; - if (c < '0' || c > '9') - break; - v = v * 10 + c - '0'; - if (v >= INT32_MAX) { - if (allow_overflow) - v = INT32_MAX; - else - return -1; - } - p++; - } - *pp = p; - return v; -} - -static int re_parse_expect(REParseState *s, const uint8_t **pp, int c) -{ - const uint8_t *p; - p = *pp; - if (*p != c) - return re_parse_error(s, "expecting '%c'", c); - p++; - *pp = p; - return 0; -} - -/* Parse an escape sequence, *pp points after the '\': - allow_utf16 value: - 0 : no UTF-16 escapes allowed - 1 : UTF-16 escapes allowed - 2 : UTF-16 escapes allowed and escapes of surrogate pairs are - converted to a unicode character (unicode regexp case). - - Return the unicode char and update *pp if recognized, - return -1 if malformed escape, - return -2 otherwise. */ -int lre_parse_escape(const uint8_t **pp, int allow_utf16) -{ - const uint8_t *p; - uint32_t c; - - p = *pp; - c = *p++; - switch(c) { - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case 'x': - case 'u': - { - int h, n, i; - uint32_t c1; - - if (*p == '{' && allow_utf16) { - p++; - c = 0; - for(;;) { - h = from_hex(*p++); - if (h < 0) - return -1; - c = (c << 4) | h; - if (c > 0x10FFFF) - return -1; - if (*p == '}') - break; - } - p++; - } else { - if (c == 'x') { - n = 2; - } else { - n = 4; - } - - c = 0; - for(i = 0; i < n; i++) { - h = from_hex(*p++); - if (h < 0) { - return -1; - } - c = (c << 4) | h; - } - if (is_hi_surrogate(c) && - allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { - /* convert an escaped surrogate pair into a - unicode char */ - c1 = 0; - for(i = 0; i < 4; i++) { - h = from_hex(p[2 + i]); - if (h < 0) - break; - c1 = (c1 << 4) | h; - } - if (i == 4 && is_lo_surrogate(c1)) { - p += 6; - c = from_surrogate(c, c1); - } - } - } - } - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - c -= '0'; - if (allow_utf16 == 2) { - /* only accept \0 not followed by digit */ - if (c != 0 || is_digit(*p)) - return -1; - } else { - /* parse a legacy octal sequence */ - uint32_t v; - v = *p - '0'; - if (v > 7) - break; - c = (c << 3) | v; - p++; - if (c >= 32) - break; - v = *p - '0'; - if (v > 7) - break; - c = (c << 3) | v; - p++; - } - break; - default: - return -2; - } - *pp = p; - return c; -} - -#ifdef CONFIG_ALL_UNICODE -/* XXX: we use the same chars for name and value */ -static BOOL is_unicode_char(int c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c == '_')); -} - -/* XXX: memory error test */ -static void seq_prop_cb(void *opaque, const uint32_t *seq, int seq_len) -{ - REStringList *sl = opaque; - re_string_add(sl, seq_len, seq); -} - -static int parse_unicode_property(REParseState *s, REStringList *cr, - const uint8_t **pp, BOOL is_inv, - BOOL allow_sequence_prop) -{ - const uint8_t *p; - char name[64], value[64]; - char *q; - BOOL script_ext; - int ret; - - p = *pp; - if (*p != '{') - return re_parse_error(s, "expecting '{' after \\p"); - p++; - q = name; - while (is_unicode_char(*p)) { - if ((q - name) >= sizeof(name) - 1) - goto unknown_property_name; - *q++ = *p++; - } - *q = '\0'; - q = value; - if (*p == '=') { - p++; - while (is_unicode_char(*p)) { - if ((q - value) >= sizeof(value) - 1) - return re_parse_error(s, "unknown unicode property value"); - *q++ = *p++; - } - } - *q = '\0'; - if (*p != '}') - return re_parse_error(s, "expecting '}'"); - p++; - // printf("name=%s value=%s\n", name, value); - - if (!strcmp(name, "Script") || !strcmp(name, "sc")) { - script_ext = FALSE; - goto do_script; - } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { - script_ext = TRUE; - do_script: - re_string_list_init(s, cr); - ret = unicode_script(&cr->cr, value, script_ext); - if (ret) { - re_string_list_free(cr); - if (ret == -2) - return re_parse_error(s, "unknown unicode script"); - else - goto out_of_memory; - } - } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { - re_string_list_init(s, cr); - ret = unicode_general_category(&cr->cr, value); - if (ret) { - re_string_list_free(cr); - if (ret == -2) - return re_parse_error(s, "unknown unicode general category"); - else - goto out_of_memory; - } - } else if (value[0] == '\0') { - re_string_list_init(s, cr); - ret = unicode_general_category(&cr->cr, name); - if (ret == -1) { - re_string_list_free(cr); - goto out_of_memory; - } - if (ret < 0) { - ret = unicode_prop(&cr->cr, name); - if (ret == -1) { - re_string_list_free(cr); - goto out_of_memory; - } - } - if (ret < 0 && !is_inv && allow_sequence_prop) { - CharRange cr_tmp; - cr_init(&cr_tmp, s->opaque, lre_realloc); - ret = unicode_sequence_prop(name, seq_prop_cb, cr, &cr_tmp); - cr_free(&cr_tmp); - if (ret == -1) { - re_string_list_free(cr); - goto out_of_memory; - } - } - if (ret < 0) - goto unknown_property_name; - } else { - unknown_property_name: - return re_parse_error(s, "unknown unicode property name"); - } - - /* the ordering of case folding and inversion differs with - unicode_sets. 'unicode_sets' ordering is more consistent */ - /* XXX: the spec seems incorrect, we do it as the other engines - seem to do it. */ - if (s->ignore_case && s->unicode_sets) { - if (re_string_list_canonicalize(s, cr, s->is_unicode)) { - re_string_list_free(cr); - goto out_of_memory; - } - } - if (is_inv) { - if (cr_invert(&cr->cr)) { - re_string_list_free(cr); - goto out_of_memory; - } - } - if (s->ignore_case && !s->unicode_sets) { - if (re_string_list_canonicalize(s, cr, s->is_unicode)) { - re_string_list_free(cr); - goto out_of_memory; - } - } - *pp = p; - return 0; - out_of_memory: - return re_parse_out_of_memory(s); -} -#endif /* CONFIG_ALL_UNICODE */ - -static int get_class_atom(REParseState *s, REStringList *cr, - const uint8_t **pp, BOOL inclass); - -static int parse_class_string_disjunction(REParseState *s, REStringList *cr, - const uint8_t **pp) -{ - const uint8_t *p; - DynBuf str; - int c; - - p = *pp; - if (*p != '{') - return re_parse_error(s, "expecting '{' after \\q"); - - dbuf_init2(&str, s->opaque, lre_realloc); - re_string_list_init(s, cr); - - p++; - for(;;) { - str.size = 0; - while (*p != '}' && *p != '|') { - c = get_class_atom(s, NULL, &p, FALSE); - if (c < 0) - goto fail; - if (dbuf_put_u32(&str, c)) { - re_parse_out_of_memory(s); - goto fail; - } - } - if (re_string_add(cr, str.size / 4, (uint32_t *)str.buf)) { - re_parse_out_of_memory(s); - goto fail; - } - if (*p == '}') - break; - p++; - } - if (s->ignore_case) { - if (re_string_list_canonicalize(s, cr, TRUE)) - goto fail; - } - p++; /* skip the '}' */ - dbuf_free(&str); - *pp = p; - return 0; - fail: - dbuf_free(&str); - re_string_list_free(cr); - return -1; -} - -/* return -1 if error otherwise the character or a class range - (CLASS_RANGE_BASE) if cr != NULL. In case of class range, 'cr' is - initialized. Otherwise, it is ignored. */ -static int get_class_atom(REParseState *s, REStringList *cr, - const uint8_t **pp, BOOL inclass) -{ - const uint8_t *p; - uint32_t c; - int ret; - - p = *pp; - - c = *p; - switch(c) { - case '\\': - p++; - if (p >= s->buf_end) - goto unexpected_end; - c = *p++; - switch(c) { - case 'd': - c = CHAR_RANGE_d; - goto class_range; - case 'D': - c = CHAR_RANGE_D; - goto class_range; - case 's': - c = CHAR_RANGE_s; - goto class_range; - case 'S': - c = CHAR_RANGE_S; - goto class_range; - case 'w': - c = CHAR_RANGE_w; - goto class_range; - case 'W': - c = CHAR_RANGE_W; - class_range: - if (!cr) - goto default_escape; - if (cr_init_char_range(s, cr, c)) - return -1; - c = CLASS_RANGE_BASE; - break; - case 'c': - c = *p; - if ((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (((c >= '0' && c <= '9') || c == '_') && - inclass && !s->is_unicode)) { /* Annex B.1.4 */ - c &= 0x1f; - p++; - } else if (s->is_unicode) { - goto invalid_escape; - } else { - /* otherwise return '\' and 'c' */ - p--; - c = '\\'; - } - break; - case '-': - if (!inclass && s->is_unicode) - goto invalid_escape; - break; - case '^': - case '$': - case '\\': - case '.': - case '*': - case '+': - case '?': - case '(': - case ')': - case '[': - case ']': - case '{': - case '}': - case '|': - case '/': - /* always valid to escape these characters */ - break; -#ifdef CONFIG_ALL_UNICODE - case 'p': - case 'P': - if (s->is_unicode && cr) { - if (parse_unicode_property(s, cr, &p, (c == 'P'), s->unicode_sets)) - return -1; - c = CLASS_RANGE_BASE; - break; - } - goto default_escape; -#endif - case 'q': - if (s->unicode_sets && cr && inclass) { - if (parse_class_string_disjunction(s, cr, &p)) - return -1; - c = CLASS_RANGE_BASE; - break; - } - goto default_escape; - default: - default_escape: - p--; - ret = lre_parse_escape(&p, s->is_unicode * 2); - if (ret >= 0) { - c = ret; - } else { - if (s->is_unicode) { - invalid_escape: - return re_parse_error(s, "invalid escape sequence in regular expression"); - } else { - /* just ignore the '\' */ - goto normal_char; - } - } - break; - } - break; - case '\0': - if (p >= s->buf_end) { - unexpected_end: - return re_parse_error(s, "unexpected end"); - } - /* fall thru */ - goto normal_char; - - case '&': - case '!': - case '#': - case '$': - case '%': - case '*': - case '+': - case ',': - case '.': - case ':': - case ';': - case '<': - case '=': - case '>': - case '?': - case '@': - case '^': - case '`': - case '~': - if (s->unicode_sets && p[1] == c) { - /* forbidden double characters */ - return re_parse_error(s, "invalid class set operation in regular expression"); - } - goto normal_char; - - case '(': - case ')': - case '[': - case ']': - case '{': - case '}': - case '/': - case '-': - case '|': - if (s->unicode_sets) { - /* invalid characters in unicode sets */ - return re_parse_error(s, "invalid character in class in regular expression"); - } - goto normal_char; - - default: - normal_char: - /* normal char */ - if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if ((unsigned)c > 0xffff && !s->is_unicode) { - /* XXX: should handle non BMP-1 code points */ - return re_parse_error(s, "malformed unicode char"); - } - } else { - p++; - } - break; - } - *pp = p; - return c; -} - -static int re_emit_range(REParseState *s, const CharRange *cr) -{ - int len, i; - uint32_t high; - - len = (unsigned)cr->len / 2; - if (len >= 65535) - return re_parse_error(s, "too many ranges"); - if (len == 0) { - re_emit_op_u32(s, REOP_char32, -1); - } else { - high = cr->points[cr->len - 1]; - if (high == UINT32_MAX) - high = cr->points[cr->len - 2]; - if (high <= 0xffff) { - /* can use 16 bit ranges with the conversion that 0xffff = - infinity */ - re_emit_op_u16(s, s->ignore_case ? REOP_range_i : REOP_range, len); - for(i = 0; i < cr->len; i += 2) { - dbuf_put_u16(&s->byte_code, cr->points[i]); - high = cr->points[i + 1] - 1; - if (high == UINT32_MAX - 1) - high = 0xffff; - dbuf_put_u16(&s->byte_code, high); - } - } else { - re_emit_op_u16(s, s->ignore_case ? REOP_range32_i : REOP_range32, len); - for(i = 0; i < cr->len; i += 2) { - dbuf_put_u32(&s->byte_code, cr->points[i]); - dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); - } - } - } - return 0; -} - -static int re_string_cmp_len(const void *a, const void *b, void *arg) -{ - REString *p1 = *(REString **)a; - REString *p2 = *(REString **)b; - return (p1->len < p2->len) - (p1->len > p2->len); -} - -static void re_emit_char(REParseState *s, int c) -{ - if (c <= 0xffff) - re_emit_op_u16(s, s->ignore_case ? REOP_char_i : REOP_char, c); - else - re_emit_op_u32(s, s->ignore_case ? REOP_char32_i : REOP_char32, c); -} - -static int re_emit_string_list(REParseState *s, const REStringList *sl) -{ - REString **tab, *p; - int i, j, split_pos, last_match_pos, n; - BOOL has_empty_string, is_last; - - // re_string_list_dump("sl", sl); - if (sl->n_strings == 0) { - /* simple case: only characters */ - if (re_emit_range(s, &sl->cr)) - return -1; - } else { - /* at least one string list is present : match the longest ones first */ - /* XXX: add a new op_switch opcode to compile as a trie */ - tab = lre_realloc(s->opaque, NULL, sizeof(tab[0]) * sl->n_strings); - if (!tab) { - re_parse_out_of_memory(s); - return -1; - } - has_empty_string = FALSE; - n = 0; - for(i = 0; i < sl->hash_size; i++) { - for(p = sl->hash_table[i]; p != NULL; p = p->next) { - if (p->len == 0) { - has_empty_string = TRUE; - } else { - tab[n++] = p; - } - } - } - assert(n <= sl->n_strings); - - rqsort(tab, n, sizeof(tab[0]), re_string_cmp_len, NULL); - - last_match_pos = -1; - for(i = 0; i < n; i++) { - p = tab[i]; - is_last = !has_empty_string && sl->cr.len == 0 && i == (n - 1); - if (!is_last) - split_pos = re_emit_op_u32(s, REOP_split_next_first, 0); - else - split_pos = 0; - for(j = 0; j < p->len; j++) { - re_emit_char(s, p->buf[j]); - } - if (!is_last) { - last_match_pos = re_emit_op_u32(s, REOP_goto, last_match_pos); - put_u32(s->byte_code.buf + split_pos, s->byte_code.size - (split_pos + 4)); - } - } - - if (sl->cr.len != 0) { - /* char range */ - is_last = !has_empty_string; - if (!is_last) - split_pos = re_emit_op_u32(s, REOP_split_next_first, 0); - else - split_pos = 0; /* not used */ - if (re_emit_range(s, &sl->cr)) { - lre_realloc(s->opaque, tab, 0); - return -1; - } - if (!is_last) - put_u32(s->byte_code.buf + split_pos, s->byte_code.size - (split_pos + 4)); - } - - /* patch the 'goto match' */ - while (last_match_pos != -1) { - int next_pos = get_u32(s->byte_code.buf + last_match_pos); - put_u32(s->byte_code.buf + last_match_pos, s->byte_code.size - (last_match_pos + 4)); - last_match_pos = next_pos; - } - - lre_realloc(s->opaque, tab, 0); - } - return 0; -} - -static int re_parse_nested_class(REParseState *s, REStringList *cr, const uint8_t **pp); - -static int re_parse_class_set_operand(REParseState *s, REStringList *cr, const uint8_t **pp) -{ - int c1; - const uint8_t *p = *pp; - - if (*p == '[') { - if (re_parse_nested_class(s, cr, pp)) - return -1; - } else { - c1 = get_class_atom(s, cr, pp, TRUE); - if (c1 < 0) - return -1; - if (c1 < CLASS_RANGE_BASE) { - /* create a range with a single character */ - re_string_list_init(s, cr); - if (s->ignore_case) - c1 = lre_canonicalize(c1, s->is_unicode); - if (cr_union_interval(&cr->cr, c1, c1)) { - re_string_list_free(cr); - return -1; - } - } - } - return 0; -} - -static int re_parse_nested_class(REParseState *s, REStringList *cr, const uint8_t **pp) -{ - const uint8_t *p; - uint32_t c1, c2; - int ret; - REStringList cr1_s, *cr1 = &cr1_s; - BOOL invert, is_first; - - if (lre_check_stack_overflow(s->opaque, 0)) - return re_parse_error(s, "stack overflow"); - - re_string_list_init(s, cr); - p = *pp; - p++; /* skip '[' */ - - invert = FALSE; - if (*p == '^') { - p++; - invert = TRUE; - } - - /* handle unions */ - is_first = TRUE; - for(;;) { - if (*p == ']') - break; - if (*p == '[' && s->unicode_sets) { - if (re_parse_nested_class(s, cr1, &p)) - goto fail; - goto class_union; - } else { - c1 = get_class_atom(s, cr1, &p, TRUE); - if ((int)c1 < 0) - goto fail; - if (*p == '-' && p[1] != ']') { - const uint8_t *p0 = p + 1; - if (p[1] == '-' && s->unicode_sets && is_first) - goto class_atom; /* first character class followed by '--' */ - if (c1 >= CLASS_RANGE_BASE) { - if (s->is_unicode) { - re_string_list_free(cr1); - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - c2 = get_class_atom(s, cr1, &p0, TRUE); - if ((int)c2 < 0) - goto fail; - if (c2 >= CLASS_RANGE_BASE) { - re_string_list_free(cr1); - if (s->is_unicode) { - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - p = p0; - if (c2 < c1) { - invalid_class_range: - re_parse_error(s, "invalid class range"); - goto fail; - } - if (s->ignore_case) { - CharRange cr2_s, *cr2 = &cr2_s; - cr_init(cr2, s->opaque, lre_realloc); - if (cr_add_interval(cr2, c1, c2 + 1) || - cr_regexp_canonicalize(cr2, s->is_unicode) || - cr_op1(&cr->cr, cr2->points, cr2->len, CR_OP_UNION)) { - cr_free(cr2); - goto memory_error; - } - cr_free(cr2); - } else { - if (cr_union_interval(&cr->cr, c1, c2)) - goto memory_error; - } - is_first = FALSE; /* union operation */ - } else { - class_atom: - if (c1 >= CLASS_RANGE_BASE) { - class_union: - ret = re_string_list_op(cr, cr1, CR_OP_UNION); - re_string_list_free(cr1); - if (ret) - goto memory_error; - } else { - if (s->ignore_case) - c1 = lre_canonicalize(c1, s->is_unicode); - if (cr_union_interval(&cr->cr, c1, c1)) - goto memory_error; - } - } - } - if (s->unicode_sets && is_first) { - if (*p == '&' && p[1] == '&' && p[2] != '&') { - /* handle '&&' */ - for(;;) { - if (*p == ']') { - break; - } else if (*p == '&' && p[1] == '&' && p[2] != '&') { - p += 2; - } else { - goto invalid_operation; - } - if (re_parse_class_set_operand(s, cr1, &p)) - goto fail; - ret = re_string_list_op(cr, cr1, CR_OP_INTER); - re_string_list_free(cr1); - if (ret) - goto memory_error; - } - } else if (*p == '-' && p[1] == '-') { - /* handle '--' */ - for(;;) { - if (*p == ']') { - break; - } else if (*p == '-' && p[1] == '-') { - p += 2; - } else { - invalid_operation: - re_parse_error(s, "invalid operation in regular expression"); - goto fail; - } - if (re_parse_class_set_operand(s, cr1, &p)) - goto fail; - ret = re_string_list_op(cr, cr1, CR_OP_SUB); - re_string_list_free(cr1); - if (ret) - goto memory_error; - } - } - } - is_first = FALSE; - } - - p++; /* skip ']' */ - *pp = p; - if (invert) { - /* XXX: add may_contain_string syntax check to be fully - compliant. The test here accepts more input than the - spec. */ - if (cr->n_strings != 0) { - re_parse_error(s, "negated character class with strings in regular expression debugger eval code"); - goto fail; - } - if (cr_invert(&cr->cr)) - goto memory_error; - } - return 0; - memory_error: - re_parse_out_of_memory(s); - fail: - re_string_list_free(cr); - return -1; -} - -static int re_parse_char_class(REParseState *s, const uint8_t **pp) -{ - REStringList cr_s, *cr = &cr_s; - - if (re_parse_nested_class(s, cr, pp)) - return -1; - if (re_emit_string_list(s, cr)) - goto fail; - re_string_list_free(cr); - return 0; - fail: - re_string_list_free(cr); - return -1; -} - -/* Return: - - true if the opcodes may not advance the char pointer - - false if the opcodes always advance the char pointer -*/ -static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len) -{ - int pos, opcode, len; - uint32_t val; - BOOL ret; - - ret = TRUE; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - switch(opcode) { - case REOP_range: - case REOP_range_i: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - goto simple_char; - case REOP_range32: - case REOP_range32_i: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - goto simple_char; - case REOP_char: - case REOP_char_i: - case REOP_char32: - case REOP_char32_i: - case REOP_dot: - case REOP_any: - simple_char: - ret = FALSE; - break; - case REOP_line_start: - case REOP_line_start_m: - case REOP_line_end: - case REOP_line_end_m: - case REOP_push_i32: - case REOP_push_char_pos: - case REOP_word_boundary: - case REOP_word_boundary_i: - case REOP_not_word_boundary: - case REOP_not_word_boundary_i: - case REOP_prev: - /* no effect */ - break; - case REOP_save_start: - case REOP_save_end: - case REOP_save_reset: - case REOP_back_reference: - case REOP_back_reference_i: - case REOP_backward_back_reference: - case REOP_backward_back_reference_i: - break; - default: - /* safe behavior: we cannot predict the outcome */ - return TRUE; - } - pos += len; - } - return ret; -} - -/* '*pp' is the first char after '<' */ -static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) -{ - const uint8_t *p, *p1; - uint32_t c, d; - char *q; - - p = *pp; - q = buf; - for(;;) { - c = *p; - if (c == '\\') { - p++; - if (*p != 'u') - return -1; - c = lre_parse_escape(&p, 2); // accept surrogate pairs - } else if (c == '>') { - break; - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (is_hi_surrogate(c)) { - d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - if (is_lo_surrogate(d)) { - c = from_surrogate(c, d); - p = p1; - } - } - } else { - p++; - } - if (c > 0x10FFFF) - return -1; - if (q == buf) { - if (!lre_js_is_ident_first(c)) - return -1; - } else { - if (!lre_js_is_ident_next(c)) - return -1; - } - if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size) - return -1; - if (c < 128) { - *q++ = c; - } else { - q += unicode_to_utf8((uint8_t*)q, c); - } - } - if (q == buf) - return -1; - *q = '\0'; - p++; - *pp = p; - return 0; -} - -/* if capture_name = NULL: return the number of captures + 1. - Otherwise, return the capture index corresponding to capture_name - or -1 if none */ -static int re_parse_captures(REParseState *s, int *phas_named_captures, - const char *capture_name) -{ - const uint8_t *p; - int capture_index; - char name[TMP_BUF_SIZE]; - - capture_index = 1; - *phas_named_captures = 0; - for (p = s->buf_start; p < s->buf_end; p++) { - switch (*p) { - case '(': - if (p[1] == '?') { - if (p[2] == '<' && p[3] != '=' && p[3] != '!') { - *phas_named_captures = 1; - /* potential named capture */ - if (capture_name) { - p += 3; - if (re_parse_group_name(name, sizeof(name), &p) == 0) { - if (!strcmp(name, capture_name)) - return capture_index; - } - } - capture_index++; - if (capture_index >= CAPTURE_COUNT_MAX) - goto done; - } - } else { - capture_index++; - if (capture_index >= CAPTURE_COUNT_MAX) - goto done; - } - break; - case '\\': - p++; - break; - case '[': - for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) { - if (*p == '\\') - p++; - } - break; - } - } - done: - if (capture_name) - return -1; - else - return capture_index; -} - -static int re_count_captures(REParseState *s) -{ - if (s->total_capture_count < 0) { - s->total_capture_count = re_parse_captures(s, &s->has_named_captures, - NULL); - } - return s->total_capture_count; -} - -static BOOL re_has_named_captures(REParseState *s) -{ - if (s->has_named_captures < 0) - re_count_captures(s); - return s->has_named_captures; -} - -static int find_group_name(REParseState *s, const char *name) -{ - const char *p, *buf_end; - size_t len, name_len; - int capture_index; - - p = (char *)s->group_names.buf; - if (!p) return -1; - buf_end = (char *)s->group_names.buf + s->group_names.size; - name_len = strlen(name); - capture_index = 1; - while (p < buf_end) { - len = strlen(p); - if (len == name_len && memcmp(name, p, name_len) == 0) - return capture_index; - p += len + 1; - capture_index++; - } - return -1; -} - -static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir); - -static int re_parse_modifiers(REParseState *s, const uint8_t **pp) -{ - const uint8_t *p = *pp; - int mask = 0; - int val; - - for(;;) { - if (*p == 'i') { - val = LRE_FLAG_IGNORECASE; - } else if (*p == 'm') { - val = LRE_FLAG_MULTILINE; - } else if (*p == 's') { - val = LRE_FLAG_DOTALL; - } else { - break; - } - if (mask & val) - return re_parse_error(s, "duplicate modifier: '%c'", *p); - mask |= val; - p++; - } - *pp = p; - return mask; -} - -static BOOL update_modifier(BOOL val, int add_mask, int remove_mask, - int mask) -{ - if (add_mask & mask) - val = TRUE; - if (remove_mask & mask) - val = FALSE; - return val; -} - -static int re_parse_term(REParseState *s, BOOL is_backward_dir) -{ - const uint8_t *p; - int c, last_atom_start, quant_min, quant_max, last_capture_count; - BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; - REStringList cr_s, *cr = &cr_s; - - last_atom_start = -1; - last_capture_count = 0; - p = s->buf_ptr; - c = *p; - switch(c) { - case '^': - p++; - re_emit_op(s, s->multi_line ? REOP_line_start_m : REOP_line_start); - break; - case '$': - p++; - re_emit_op(s, s->multi_line ? REOP_line_end_m : REOP_line_end); - break; - case '.': - p++; - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - re_emit_op(s, s->dotall ? REOP_any : REOP_dot); - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - case '{': - if (s->is_unicode) { - return re_parse_error(s, "syntax error"); - } else if (!is_digit(p[1])) { - /* Annex B: we accept '{' not followed by digits as a - normal atom */ - goto parse_class_atom; - } else { - const uint8_t *p1 = p + 1; - /* Annex B: error if it is like a repetition count */ - parse_digits(&p1, TRUE); - if (*p1 == ',') { - p1++; - if (is_digit(*p1)) { - parse_digits(&p1, TRUE); - } - } - if (*p1 != '}') { - goto parse_class_atom; - } - } - /* fall thru */ - case '*': - case '+': - case '?': - return re_parse_error(s, "nothing to repeat"); - case '(': - if (p[1] == '?') { - if (p[2] == ':') { - p += 3; - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - } else if (p[2] == 'i' || p[2] == 'm' || p[2] == 's' || p[2] == '-') { - BOOL saved_ignore_case, saved_multi_line, saved_dotall; - int add_mask, remove_mask; - p += 2; - remove_mask = 0; - add_mask = re_parse_modifiers(s, &p); - if (add_mask < 0) - return -1; - if (*p == '-') { - p++; - remove_mask = re_parse_modifiers(s, &p); - if (remove_mask < 0) - return -1; - } - if ((add_mask == 0 && remove_mask == 0) || - (add_mask & remove_mask) != 0) { - return re_parse_error(s, "invalid modifiers"); - } - if (re_parse_expect(s, &p, ':')) - return -1; - saved_ignore_case = s->ignore_case; - saved_multi_line = s->multi_line; - saved_dotall = s->dotall; - s->ignore_case = update_modifier(s->ignore_case, add_mask, remove_mask, LRE_FLAG_IGNORECASE); - s->multi_line = update_modifier(s->multi_line, add_mask, remove_mask, LRE_FLAG_MULTILINE); - s->dotall = update_modifier(s->dotall, add_mask, remove_mask, LRE_FLAG_DOTALL); - - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - s->ignore_case = saved_ignore_case; - s->multi_line = saved_multi_line; - s->dotall = saved_dotall; - } else if ((p[2] == '=' || p[2] == '!')) { - is_neg = (p[2] == '!'); - is_backward_lookahead = FALSE; - p += 3; - goto lookahead; - } else if (p[2] == '<' && - (p[3] == '=' || p[3] == '!')) { - int pos; - is_neg = (p[3] == '!'); - is_backward_lookahead = TRUE; - p += 4; - /* lookahead */ - lookahead: - /* Annex B allows lookahead to be used as an atom for - the quantifiers */ - if (!s->is_unicode && !is_backward_lookahead) { - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - } - pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_lookahead)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - re_emit_op(s, REOP_lookahead_match + is_neg); - /* jump after the 'match' after the lookahead is successful */ - if (dbuf_error(&s->byte_code)) - return -1; - put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); - } else if (p[2] == '<') { - p += 3; - if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p)) { - return re_parse_error(s, "invalid group name"); - } - if (find_group_name(s, s->u.tmp_buf) > 0) { - return re_parse_error(s, "duplicate group name"); - } - /* group name with a trailing zero */ - dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf, - strlen(s->u.tmp_buf) + 1); - s->has_named_captures = 1; - goto parse_capture; - } else { - return re_parse_error(s, "invalid group"); - } - } else { - int capture_index; - p++; - /* capture without group name */ - dbuf_putc(&s->group_names, 0); - parse_capture: - if (s->capture_count >= CAPTURE_COUNT_MAX) - return re_parse_error(s, "too many captures"); - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - capture_index = s->capture_count++; - re_emit_op_u8(s, REOP_save_start + is_backward_dir, - capture_index); - - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - - re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, - capture_index); - - if (re_parse_expect(s, &p, ')')) - return -1; - } - break; - case '\\': - switch(p[1]) { - case 'b': - case 'B': - if (p[1] != 'b') { - re_emit_op(s, s->ignore_case ? REOP_not_word_boundary_i : REOP_not_word_boundary); - } else { - re_emit_op(s, s->ignore_case ? REOP_word_boundary_i : REOP_word_boundary); - } - p += 2; - break; - case 'k': - { - const uint8_t *p1; - int dummy_res; - - p1 = p; - if (p1[2] != '<') { - /* annex B: we tolerate invalid group names in non - unicode mode if there is no named capture - definition */ - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "expecting group name"); - else - goto parse_class_atom; - } - p1 += 3; - if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p1)) { - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "invalid group name"); - else - goto parse_class_atom; - } - c = find_group_name(s, s->u.tmp_buf); - if (c < 0) { - /* no capture name parsed before, try to look - after (inefficient, but hopefully not common */ - c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); - if (c < 0) { - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "group name not defined"); - else - goto parse_class_atom; - } - } - p = p1; - } - goto emit_back_reference; - case '0': - p += 2; - c = 0; - if (s->is_unicode) { - if (is_digit(*p)) { - return re_parse_error(s, "invalid decimal escape in regular expression"); - } - } else { - /* Annex B.1.4: accept legacy octal */ - if (*p >= '0' && *p <= '7') { - c = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - } - } - } - goto normal_char; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': - case '9': - { - const uint8_t *q = ++p; - - c = parse_digits(&p, FALSE); - if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { - if (!s->is_unicode) { - /* Annex B.1.4: accept legacy octal */ - p = q; - if (*p <= '7') { - c = 0; - if (*p <= '3') - c = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - } - } - } else { - c = *p++; - } - goto normal_char; - } - return re_parse_error(s, "back reference out of range in regular expression"); - } - emit_back_reference: - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - - re_emit_op_u8(s, REOP_back_reference + 2 * is_backward_dir + s->ignore_case, c); - } - break; - default: - goto parse_class_atom; - } - break; - case '[': - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - if (re_parse_char_class(s, &p)) - return -1; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - case ']': - case '}': - if (s->is_unicode) - return re_parse_error(s, "syntax error"); - goto parse_class_atom; - default: - parse_class_atom: - c = get_class_atom(s, cr, &p, FALSE); - if ((int)c < 0) - return -1; - normal_char: - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - if (c >= CLASS_RANGE_BASE) { - int ret; - ret = re_emit_string_list(s, cr); - re_string_list_free(cr); - if (ret) - return -1; - } else { - if (s->ignore_case) - c = lre_canonicalize(c, s->is_unicode); - re_emit_char(s, c); - } - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - } - - /* quantifier */ - if (last_atom_start >= 0) { - c = *p; - switch(c) { - case '*': - p++; - quant_min = 0; - quant_max = INT32_MAX; - goto quantifier; - case '+': - p++; - quant_min = 1; - quant_max = INT32_MAX; - goto quantifier; - case '?': - p++; - quant_min = 0; - quant_max = 1; - goto quantifier; - case '{': - { - const uint8_t *p1 = p; - /* As an extension (see ES6 annex B), we accept '{' not - followed by digits as a normal atom */ - if (!is_digit(p[1])) { - if (s->is_unicode) - goto invalid_quant_count; - break; - } - p++; - quant_min = parse_digits(&p, TRUE); - quant_max = quant_min; - if (*p == ',') { - p++; - if (is_digit(*p)) { - quant_max = parse_digits(&p, TRUE); - if (quant_max < quant_min) { - invalid_quant_count: - return re_parse_error(s, "invalid repetition count"); - } - } else { - quant_max = INT32_MAX; /* infinity */ - } - } - if (*p != '}' && !s->is_unicode) { - /* Annex B: normal atom if invalid '{' syntax */ - p = p1; - break; - } - if (re_parse_expect(s, &p, '}')) - return -1; - } - quantifier: - greedy = TRUE; - if (*p == '?') { - p++; - greedy = FALSE; - } - if (last_atom_start < 0) { - return re_parse_error(s, "nothing to repeat"); - } - /* the spec tells that if there is no advance when - running the atom after the first quant_min times, - then there is no match. We remove this test when we - are sure the atom always advances the position. */ - add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, - s->byte_code.size - last_atom_start); - - { - int len, pos; - len = s->byte_code.size - last_atom_start; - if (quant_min == 0) { - /* need to reset the capture in case the atom is - not executed */ - if (last_capture_count != s->capture_count) { - if (dbuf_insert(&s->byte_code, last_atom_start, 3)) - goto out_of_memory; - s->byte_code.buf[last_atom_start++] = REOP_save_reset; - s->byte_code.buf[last_atom_start++] = last_capture_count; - s->byte_code.buf[last_atom_start++] = s->capture_count - 1; - } - if (quant_max == 0) { - s->byte_code.size = last_atom_start; - } else if (quant_max == 1 || quant_max == INT32_MAX) { - BOOL has_goto = (quant_max == INT32_MAX); - if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check * 2)) - goto out_of_memory; - s->byte_code.buf[last_atom_start] = REOP_split_goto_first + - greedy; - put_u32(s->byte_code.buf + last_atom_start + 1, - len + 5 * has_goto + add_zero_advance_check * 2 * 2); - if (add_zero_advance_check) { - s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; - s->byte_code.buf[last_atom_start + 1 + 4 + 1] = 0; - re_emit_op_u8(s, REOP_check_advance, 0); - } - if (has_goto) - re_emit_goto(s, REOP_goto, last_atom_start); - } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 11 + add_zero_advance_check * 2)) - goto out_of_memory; - pos = last_atom_start; - s->byte_code.buf[pos++] = REOP_push_i32; - s->byte_code.buf[pos++] = 0; - put_u32(s->byte_code.buf + pos, quant_max); - pos += 4; - - s->byte_code.buf[pos++] = REOP_split_goto_first + greedy; - put_u32(s->byte_code.buf + pos, len + 6 + add_zero_advance_check * 2 * 2); - pos += 4; - if (add_zero_advance_check) { - s->byte_code.buf[pos++] = REOP_push_char_pos; - s->byte_code.buf[pos++] = 0; - re_emit_op_u8(s, REOP_check_advance, 0); - } - re_emit_goto_u8(s, REOP_loop, 0, last_atom_start + 6); - } - } else if (quant_min == 1 && quant_max == INT32_MAX && - !add_zero_advance_check) { - re_emit_goto(s, REOP_split_next_first - greedy, - last_atom_start); - } else { - if (quant_min == 1) { - /* nothing to add */ - } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 6)) - goto out_of_memory; - s->byte_code.buf[last_atom_start++] = REOP_push_i32; - s->byte_code.buf[last_atom_start++] = 0; - put_u32(s->byte_code.buf + last_atom_start, quant_min); - last_atom_start += 4; - re_emit_goto_u8(s, REOP_loop, 0, last_atom_start); - } - if (quant_max == INT32_MAX) { - pos = s->byte_code.size; - re_emit_op_u32(s, REOP_split_goto_first + greedy, - len + 5 + add_zero_advance_check * 2 * 2); - if (add_zero_advance_check) - re_emit_op_u8(s, REOP_push_char_pos, 0); - /* copy the atom */ - dbuf_put_self(&s->byte_code, last_atom_start, len); - if (add_zero_advance_check) - re_emit_op_u8(s, REOP_check_advance, 0); - re_emit_goto(s, REOP_goto, pos); - } else if (quant_max > quant_min) { - re_emit_op_u8(s, REOP_push_i32, 0); - dbuf_put_u32(&s->byte_code, quant_max - quant_min); - - pos = s->byte_code.size; - re_emit_op_u32(s, REOP_split_goto_first + greedy, - len + 6 + add_zero_advance_check * 2 * 2); - if (add_zero_advance_check) - re_emit_op_u8(s, REOP_push_char_pos, 0); - /* copy the atom */ - dbuf_put_self(&s->byte_code, last_atom_start, len); - if (add_zero_advance_check) - re_emit_op_u8(s, REOP_check_advance, 0); - re_emit_goto_u8(s, REOP_loop, 0, pos); - } - } - last_atom_start = -1; - } - break; - default: - break; - } - } - s->buf_ptr = p; - return 0; - out_of_memory: - return re_parse_out_of_memory(s); -} - -static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) -{ - const uint8_t *p; - int ret; - size_t start, term_start, end, term_size; - - start = s->byte_code.size; - for(;;) { - p = s->buf_ptr; - if (p >= s->buf_end) - break; - if (*p == '|' || *p == ')') - break; - term_start = s->byte_code.size; - ret = re_parse_term(s, is_backward_dir); - if (ret) - return ret; - if (is_backward_dir) { - /* reverse the order of the terms (XXX: inefficient, but - speed is not really critical here) */ - end = s->byte_code.size; - term_size = end - term_start; - if (dbuf_claim(&s->byte_code, term_size)) - return -1; - memmove(s->byte_code.buf + start + term_size, - s->byte_code.buf + start, - end - start); - memcpy(s->byte_code.buf + start, s->byte_code.buf + end, - term_size); - } - } - return 0; -} - -static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) -{ - int start, len, pos; - - if (lre_check_stack_overflow(s->opaque, 0)) - return re_parse_error(s, "stack overflow"); - - start = s->byte_code.size; - if (re_parse_alternative(s, is_backward_dir)) - return -1; - while (*s->buf_ptr == '|') { - s->buf_ptr++; - - len = s->byte_code.size - start; - - /* insert a split before the first alternative */ - if (dbuf_insert(&s->byte_code, start, 5)) { - return re_parse_out_of_memory(s); - } - s->byte_code.buf[start] = REOP_split_next_first; - put_u32(s->byte_code.buf + start + 1, len + 5); - - pos = re_emit_op_u32(s, REOP_goto, 0); - - if (re_parse_alternative(s, is_backward_dir)) - return -1; - - /* patch the goto */ - len = s->byte_code.size - (pos + 4); - put_u32(s->byte_code.buf + pos, len); - } - return 0; -} - -/* the control flow is recursive so the analysis can be linear. As a - side effect, the auxiliary stack addresses are computed. */ -static int compute_stack_size(uint8_t *bc_buf, int bc_buf_len) -{ - int stack_size, stack_size_max, pos, opcode, len; - uint32_t val; - - stack_size = 0; - stack_size_max = 0; - bc_buf += RE_HEADER_LEN; - bc_buf_len -= RE_HEADER_LEN; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - assert(opcode < REOP_COUNT); - assert((pos + len) <= bc_buf_len); - switch(opcode) { - case REOP_push_i32: - case REOP_push_char_pos: - bc_buf[pos + 1] = stack_size; - stack_size++; - if (stack_size > stack_size_max) { - if (stack_size > STACK_SIZE_MAX) - return -1; - stack_size_max = stack_size; - } - break; - case REOP_check_advance: - case REOP_loop: - assert(stack_size > 0); - stack_size--; - bc_buf[pos + 1] = stack_size; - break; - case REOP_range: - case REOP_range_i: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - break; - case REOP_range32: - case REOP_range32_i: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - break; - } - pos += len; - } - return stack_size_max; -} - -static void *lre_bytecode_realloc(void *opaque, void *ptr, size_t size) -{ - if (size > (INT32_MAX / 2)) { - /* the bytecode cannot be larger than 2G. Leave some slack to - avoid some overflows. */ - return NULL; - } else { - return lre_realloc(opaque, ptr, size); - } -} - -/* 'buf' must be a zero terminated UTF-8 string of length buf_len. - Return NULL if error and allocate an error message in *perror_msg, - otherwise the compiled bytecode and its length in plen. -*/ -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) -{ - REParseState s_s, *s = &s_s; - int stack_size; - BOOL is_sticky; - - memset(s, 0, sizeof(*s)); - s->opaque = opaque; - s->buf_ptr = (const uint8_t *)buf; - s->buf_end = s->buf_ptr + buf_len; - s->buf_start = s->buf_ptr; - s->re_flags = re_flags; - s->is_unicode = ((re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0); - is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); - s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); - s->multi_line = ((re_flags & LRE_FLAG_MULTILINE) != 0); - s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); - s->unicode_sets = ((re_flags & LRE_FLAG_UNICODE_SETS) != 0); - s->capture_count = 1; - s->total_capture_count = -1; - s->has_named_captures = -1; - - dbuf_init2(&s->byte_code, opaque, lre_bytecode_realloc); - dbuf_init2(&s->group_names, opaque, lre_realloc); - - dbuf_put_u16(&s->byte_code, re_flags); /* first element is the flags */ - dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ - dbuf_putc(&s->byte_code, 0); /* stack size */ - dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ - - if (!is_sticky) { - /* iterate thru all positions (about the same as .*?( ... ) ) - . We do it without an explicit loop so that lock step - thread execution will be possible in an optimized - implementation */ - re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); - re_emit_op(s, REOP_any); - re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); - } - re_emit_op_u8(s, REOP_save_start, 0); - - if (re_parse_disjunction(s, FALSE)) { - error: - dbuf_free(&s->byte_code); - dbuf_free(&s->group_names); - pstrcpy(error_msg, error_msg_size, s->u.error_msg); - *plen = 0; - return NULL; - } - - re_emit_op_u8(s, REOP_save_end, 0); - - re_emit_op(s, REOP_match); - - if (*s->buf_ptr != '\0') { - re_parse_error(s, "extraneous characters at the end"); - goto error; - } - - if (dbuf_error(&s->byte_code)) { - re_parse_out_of_memory(s); - goto error; - } - - stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size); - if (stack_size < 0) { - re_parse_error(s, "too many imbricated quantifiers"); - goto error; - } - - s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; - s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; - put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, - s->byte_code.size - RE_HEADER_LEN); - - /* add the named groups if needed */ - if (s->group_names.size > (s->capture_count - 1)) { - dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size); - put_u16(s->byte_code.buf + RE_HEADER_FLAGS, - lre_get_flags(s->byte_code.buf) | LRE_FLAG_NAMED_GROUPS); - } - dbuf_free(&s->group_names); - -#ifdef DUMP_REOP - lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); -#endif - - error_msg[0] = '\0'; - *plen = s->byte_code.size; - return s->byte_code.buf; -} - -static BOOL is_line_terminator(uint32_t c) -{ - return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); -} - -static BOOL is_word_char(uint32_t c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c == '_')); -} - -#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = *cptr++; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr; \ - const uint16_t *_end = (const uint16_t *)cbuf_end; \ - c = *_p++; \ - if (is_hi_surrogate(c) && cbuf_type == 2) { \ - if (_p < _end && is_lo_surrogate(*_p)) { \ - c = from_surrogate(c, *_p++); \ - } \ - } \ - cptr = (const void *)_p; \ - } \ - } while (0) - -#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[0]; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr; \ - const uint16_t *_end = (const uint16_t *)cbuf_end; \ - c = *_p++; \ - if (is_hi_surrogate(c) && cbuf_type == 2) { \ - if (_p < _end && is_lo_surrogate(*_p)) { \ - c = from_surrogate(c, *_p); \ - } \ - } \ - } \ - } while (0) - -#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[-1]; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr - 1; \ - const uint16_t *_start = (const uint16_t *)cbuf_start; \ - c = *_p; \ - if (is_lo_surrogate(c) && cbuf_type == 2) { \ - if (_p > _start && is_hi_surrogate(_p[-1])) { \ - c = from_surrogate(*--_p, c); \ - } \ - } \ - } \ - } while (0) - -#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - c = cptr[0]; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr - 1; \ - const uint16_t *_start = (const uint16_t *)cbuf_start; \ - c = *_p; \ - if (is_lo_surrogate(c) && cbuf_type == 2) { \ - if (_p > _start && is_hi_surrogate(_p[-1])) { \ - c = from_surrogate(*--_p, c); \ - } \ - } \ - cptr = (const void *)_p; \ - } \ - } while (0) - -#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr - 1; \ - const uint16_t *_start = (const uint16_t *)cbuf_start; \ - if (is_lo_surrogate(*_p) && cbuf_type == 2) { \ - if (_p > _start && is_hi_surrogate(_p[-1])) { \ - --_p; \ - } \ - } \ - cptr = (const void *)_p; \ - } \ - } while (0) - -typedef enum { - RE_EXEC_STATE_SPLIT, - RE_EXEC_STATE_LOOKAHEAD, - RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, -} REExecStateEnum; - -#if INTPTR_MAX >= INT64_MAX -#define BP_TYPE_BITS 3 -#else -#define BP_TYPE_BITS 2 -#endif - -typedef union { - uint8_t *ptr; - intptr_t val; /* for bp, the low BP_SHIFT bits store REExecStateEnum */ - struct { - uintptr_t val : sizeof(uintptr_t) * 8 - BP_TYPE_BITS; - uintptr_t type : BP_TYPE_BITS; - } bp; -} StackElem; - -typedef struct { - const uint8_t *cbuf; - const uint8_t *cbuf_end; - /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ - int cbuf_type; - int capture_count; - int stack_size_max; - BOOL is_unicode; - int interrupt_counter; - void *opaque; /* used for stack overflow check */ - - StackElem *stack_buf; - size_t stack_size; - StackElem static_stack_buf[32]; /* static stack to avoid allocation in most cases */ -} REExecContext; - -static int lre_poll_timeout(REExecContext *s) -{ - if (unlikely(--s->interrupt_counter <= 0)) { - s->interrupt_counter = INTERRUPT_COUNTER_INIT; - if (lre_check_timeout(s->opaque)) - return LRE_RET_TIMEOUT; - } - return 0; -} - -static no_inline int stack_realloc(REExecContext *s, size_t n) -{ - StackElem *new_stack; - size_t new_size; - new_size = s->stack_size * 3 / 2; - if (new_size < n) - new_size = n; - if (s->stack_buf == s->static_stack_buf) { - new_stack = lre_realloc(s->opaque, NULL, new_size * sizeof(StackElem)); - if (!new_stack) - return -1; - /* XXX: could use correct size */ - memcpy(new_stack, s->stack_buf, s->stack_size * sizeof(StackElem)); - } else { - new_stack = lre_realloc(s->opaque, s->stack_buf, new_size * sizeof(StackElem)); - if (!new_stack) - return -1; - } - s->stack_size = new_size; - s->stack_buf = new_stack; - return 0; -} - -/* return 1 if match, 0 if not match or < 0 if error. */ -static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, - uint8_t **aux_stack, const uint8_t *pc, const uint8_t *cptr) -{ - int opcode; - int cbuf_type; - uint32_t val, c, idx; - const uint8_t *cbuf_end; - StackElem *sp, *bp, *stack_end; -#ifdef DUMP_EXEC - const uint8_t *pc_start = pc; /* TEST */ -#endif - cbuf_type = s->cbuf_type; - cbuf_end = s->cbuf_end; - - sp = s->stack_buf; - bp = s->stack_buf; - stack_end = s->stack_buf + s->stack_size; - -#define CHECK_STACK_SPACE(n) \ - if (unlikely((stack_end - sp) < (n))) { \ - size_t saved_sp = sp - s->stack_buf; \ - size_t saved_bp = bp - s->stack_buf; \ - if (stack_realloc(s, sp - s->stack_buf + (n))) \ - return LRE_RET_MEMORY_ERROR; \ - stack_end = s->stack_buf + s->stack_size; \ - sp = s->stack_buf + saved_sp; \ - bp = s->stack_buf + saved_bp; \ - } - - /* XXX: could test if the value was saved to reduce the stack size - but slower */ -#define SAVE_CAPTURE(idx, value) \ - { \ - CHECK_STACK_SPACE(2); \ - sp[0].val = idx; \ - sp[1].ptr = capture[idx]; \ - sp += 2; \ - capture[idx] = (value); \ - } - - /* avoid saving the previous value if already saved */ -#define SAVE_AUX_STACK(idx, value) \ - { \ - StackElem *sp1; \ - sp1 = sp; \ - for(;;) { \ - if (sp1 > bp) { \ - if (sp1[-2].val == -(int)(idx + 1)) \ - break; \ - sp1 -= 2; \ - } else { \ - CHECK_STACK_SPACE(2); \ - sp[0].val = -(int)(idx + 1); \ - sp[1].ptr = aux_stack[idx]; \ - sp += 2; \ - break; \ - } \ - } \ - aux_stack[idx] = (value); \ - } - - -#ifdef DUMP_EXEC - printf("%5s %5s %5s %5s %s\n", "PC", "CP", "BP", "SP", "OPCODE"); -#endif - for(;;) { - opcode = *pc++; -#ifdef DUMP_EXEC - printf("%5ld %5ld %5ld %5ld %s\n", - pc - 1 - pc_start, - cbuf_type == 0 ? cptr - s->cbuf : (cptr - s->cbuf) / 2, - bp - s->stack_buf, - sp - s->stack_buf, - reopcode_info[opcode].name); -#endif - switch(opcode) { - case REOP_match: - return 1; - no_match: - for(;;) { - REExecStateEnum type; - if (bp == s->stack_buf) - return 0; - /* undo the modifications to capture[] and aux_stack[] */ - while (sp > bp) { - intptr_t idx2 = sp[-2].val; - if (idx2 >= 0) - capture[idx2] = sp[-1].ptr; - else - aux_stack[-idx2 - 1] = sp[-1].ptr; - sp -= 2; - } - - pc = sp[-3].ptr; - cptr = sp[-2].ptr; - type = sp[-1].bp.type; - bp = s->stack_buf + sp[-1].bp.val; - sp -= 3; - if (type != RE_EXEC_STATE_LOOKAHEAD) - break; - } - break; - case REOP_lookahead_match: - /* pop all the saved states until reaching the start of - the lookahead and keep the updated captures and - variables and the corresponding undo info. */ - { - StackElem *sp1, *sp_top, *next_sp; - REExecStateEnum type; - - sp_top = sp; - for(;;) { - sp1 = sp; - sp = bp; - pc = sp[-3].ptr; - cptr = sp[-2].ptr; - type = sp[-1].bp.type; - bp = s->stack_buf + sp[-1].bp.val; - sp[-1].ptr = (void *)sp1; /* save the next value for the copy step */ - sp -= 3; - if (type == RE_EXEC_STATE_LOOKAHEAD) - break; - } - if (sp != s->stack_buf) { - /* keep the undo info if there is a saved state */ - sp1 = sp; - while (sp1 < sp_top) { - next_sp = (void *)sp1[2].ptr; - sp1 += 3; - while (sp1 < next_sp) - *sp++ = *sp1++; - } - } - } - break; - case REOP_negative_lookahead_match: - /* pop all the saved states until reaching start of the negative lookahead */ - for(;;) { - REExecStateEnum type; - type = bp[-1].bp.type; - /* undo the modifications to capture[] and aux_stack[] */ - while (sp > bp) { - intptr_t idx2 = sp[-2].val; - if (idx2 >= 0) - capture[idx2] = sp[-1].ptr; - else - aux_stack[-idx2 - 1] = sp[-1].ptr; - sp -= 2; - } - pc = sp[-3].ptr; - cptr = sp[-2].ptr; - type = sp[-1].bp.type; - bp = s->stack_buf + sp[-1].bp.val; - sp -= 3; - if (type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD) - break; - } - goto no_match; - case REOP_char32: - case REOP_char32_i: - val = get_u32(pc); - pc += 4; - goto test_char; - case REOP_char: - case REOP_char_i: - val = get_u16(pc); - pc += 2; - test_char: - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (opcode == REOP_char_i || opcode == REOP_char32_i) { - c = lre_canonicalize(c, s->is_unicode); - } - if (val != c) - goto no_match; - break; - case REOP_split_goto_first: - case REOP_split_next_first: - { - const uint8_t *pc1; - - val = get_u32(pc); - pc += 4; - if (opcode == REOP_split_next_first) { - pc1 = pc + (int)val; - } else { - pc1 = pc; - pc = pc + (int)val; - } - CHECK_STACK_SPACE(3); - sp[0].ptr = (uint8_t *)pc1; - sp[1].ptr = (uint8_t *)cptr; - sp[2].bp.val = bp - s->stack_buf; - sp[2].bp.type = RE_EXEC_STATE_SPLIT; - sp += 3; - bp = sp; - } - break; - case REOP_lookahead: - case REOP_negative_lookahead: - val = get_u32(pc); - pc += 4; - if (opcode == REOP_lookahead && bp != s->stack_buf && 0) { - int i; - /* save all the capture state so that they can be - restored in case of failure after the lookahead - matches */ - idx = 4 * s->capture_count; - CHECK_STACK_SPACE(idx); - for(i = 0; i < 2 * s->capture_count; i++) { - sp[0].val = i; - sp[1].ptr = capture[i]; - sp += 2; - } - } - CHECK_STACK_SPACE(3); - sp[0].ptr = (uint8_t *)(pc + (int)val); - sp[1].ptr = (uint8_t *)cptr; - sp[2].bp.val = bp - s->stack_buf; - sp[2].bp.type = RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead; - sp += 3; - bp = sp; - break; - case REOP_goto: - val = get_u32(pc); - pc += 4 + (int)val; - if (lre_poll_timeout(s)) - return LRE_RET_TIMEOUT; - break; - case REOP_line_start: - case REOP_line_start_m: - if (cptr == s->cbuf) - break; - if (opcode == REOP_line_start) - goto no_match; - PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); - if (!is_line_terminator(c)) - goto no_match; - break; - case REOP_line_end: - case REOP_line_end_m: - if (cptr == cbuf_end) - break; - if (opcode == REOP_line_end) - goto no_match; - PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); - if (!is_line_terminator(c)) - goto no_match; - break; - case REOP_dot: - if (cptr == cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (is_line_terminator(c)) - goto no_match; - break; - case REOP_any: - if (cptr == cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - break; - case REOP_save_start: - case REOP_save_end: - val = *pc++; - assert(val < s->capture_count); - idx = 2 * val + opcode - REOP_save_start; - SAVE_CAPTURE(idx, (uint8_t *)cptr); - break; - case REOP_save_reset: - { - uint32_t val2; - val = pc[0]; - val2 = pc[1]; - pc += 2; - assert(val2 < s->capture_count); - CHECK_STACK_SPACE(2 * (val2 - val + 1)); - while (val <= val2) { - idx = 2 * val; - SAVE_CAPTURE(idx, NULL); - idx = 2 * val + 1; - SAVE_CAPTURE(idx, NULL); - val++; - } - } - break; - case REOP_push_i32: - idx = pc[0]; - val = get_u32(pc + 1); - pc += 5; - SAVE_AUX_STACK(idx, (void *)(uintptr_t)val); - break; - case REOP_loop: - { - uint32_t val2; - idx = pc[0]; - val = get_u32(pc + 1); - pc += 5; - - val2 = (uintptr_t)aux_stack[idx] - 1; - SAVE_AUX_STACK(idx, (void *)(uintptr_t)val2); - if (val2 != 0) { - pc += (int)val; - if (lre_poll_timeout(s)) - return LRE_RET_TIMEOUT; - } - } - break; - case REOP_push_char_pos: - idx = pc[0]; - pc++; - SAVE_AUX_STACK(idx, (uint8_t *)cptr); - break; - case REOP_check_advance: - idx = pc[0]; - pc++; - if (aux_stack[idx] == cptr) - goto no_match; - break; - case REOP_word_boundary: - case REOP_word_boundary_i: - case REOP_not_word_boundary: - case REOP_not_word_boundary_i: - { - BOOL v1, v2; - int ignore_case = (opcode == REOP_word_boundary_i || opcode == REOP_not_word_boundary_i); - BOOL is_boundary = (opcode == REOP_word_boundary || opcode == REOP_word_boundary_i); - /* char before */ - if (cptr == s->cbuf) { - v1 = FALSE; - } else { - PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); - if (ignore_case) - c = lre_canonicalize(c, s->is_unicode); - v1 = is_word_char(c); - } - /* current char */ - if (cptr >= cbuf_end) { - v2 = FALSE; - } else { - PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); - if (ignore_case) - c = lre_canonicalize(c, s->is_unicode); - v2 = is_word_char(c); - } - if (v1 ^ v2 ^ is_boundary) - goto no_match; - } - break; - case REOP_back_reference: - case REOP_back_reference_i: - case REOP_backward_back_reference: - case REOP_backward_back_reference_i: - { - const uint8_t *cptr1, *cptr1_end, *cptr1_start; - uint32_t c1, c2; - - val = *pc++; - if (val >= s->capture_count) - goto no_match; - cptr1_start = capture[2 * val]; - cptr1_end = capture[2 * val + 1]; - if (!cptr1_start || !cptr1_end) - break; - if (opcode == REOP_back_reference || - opcode == REOP_back_reference_i) { - cptr1 = cptr1_start; - while (cptr1 < cptr1_end) { - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); - GET_CHAR(c2, cptr, cbuf_end, cbuf_type); - if (opcode == REOP_back_reference_i) { - c1 = lre_canonicalize(c1, s->is_unicode); - c2 = lre_canonicalize(c2, s->is_unicode); - } - if (c1 != c2) - goto no_match; - } - } else { - cptr1 = cptr1_end; - while (cptr1 > cptr1_start) { - if (cptr == s->cbuf) - goto no_match; - GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); - GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); - if (opcode == REOP_backward_back_reference_i) { - c1 = lre_canonicalize(c1, s->is_unicode); - c2 = lre_canonicalize(c2, s->is_unicode); - } - if (c1 != c2) - goto no_match; - } - } - } - break; - case REOP_range: - case REOP_range_i: - { - int n; - uint32_t low, high, idx_min, idx_max, idx; - - n = get_u16(pc); /* n must be >= 1 */ - pc += 2; - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (opcode == REOP_range_i) { - c = lre_canonicalize(c, s->is_unicode); - } - idx_min = 0; - low = get_u16(pc + 0 * 4); - if (c < low) - goto no_match; - idx_max = n - 1; - high = get_u16(pc + idx_max * 4 + 2); - /* 0xffff in for last value means +infinity */ - if (unlikely(c >= 0xffff) && high == 0xffff) - goto range_match; - if (c > high) - goto no_match; - while (idx_min <= idx_max) { - idx = (idx_min + idx_max) / 2; - low = get_u16(pc + idx * 4); - high = get_u16(pc + idx * 4 + 2); - if (c < low) - idx_max = idx - 1; - else if (c > high) - idx_min = idx + 1; - else - goto range_match; - } - goto no_match; - range_match: - pc += 4 * n; - } - break; - case REOP_range32: - case REOP_range32_i: - { - int n; - uint32_t low, high, idx_min, idx_max, idx; - - n = get_u16(pc); /* n must be >= 1 */ - pc += 2; - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (opcode == REOP_range32_i) { - c = lre_canonicalize(c, s->is_unicode); - } - idx_min = 0; - low = get_u32(pc + 0 * 8); - if (c < low) - goto no_match; - idx_max = n - 1; - high = get_u32(pc + idx_max * 8 + 4); - if (c > high) - goto no_match; - while (idx_min <= idx_max) { - idx = (idx_min + idx_max) / 2; - low = get_u32(pc + idx * 8); - high = get_u32(pc + idx * 8 + 4); - if (c < low) - idx_max = idx - 1; - else if (c > high) - idx_min = idx + 1; - else - goto range32_match; - } - goto no_match; - range32_match: - pc += 8 * n; - } - break; - case REOP_prev: - /* go to the previous char */ - if (cptr == s->cbuf) - goto no_match; - PREV_CHAR(cptr, s->cbuf, cbuf_type); - break; - default: -#ifdef DUMP_EXEC - printf("unknown opcode pc=%ld\n", pc - 1 - pc_start); -#endif - abort(); - } - } -} - -/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the - starting position of the match and must be such as 0 <= cindex <= - clen. */ -int lre_exec(uint8_t **capture, - const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, - int cbuf_type, void *opaque) -{ - REExecContext s_s, *s = &s_s; - int re_flags, i, ret; - uint8_t **aux_stack; - const uint8_t *cptr; - - re_flags = lre_get_flags(bc_buf); - s->is_unicode = (re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0; - s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; - s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; - s->cbuf = cbuf; - s->cbuf_end = cbuf + (clen << cbuf_type); - s->cbuf_type = cbuf_type; - if (s->cbuf_type == 1 && s->is_unicode) - s->cbuf_type = 2; - s->interrupt_counter = INTERRUPT_COUNTER_INIT; - s->opaque = opaque; - - s->stack_buf = s->static_stack_buf; - s->stack_size = countof(s->static_stack_buf); - - for(i = 0; i < s->capture_count * 2; i++) - capture[i] = NULL; - aux_stack = alloca(s->stack_size_max * sizeof(aux_stack[0])); - - cptr = cbuf + (cindex << cbuf_type); - if (0 < cindex && cindex < clen && s->cbuf_type == 2) { - const uint16_t *p = (const uint16_t *)cptr; - if (is_lo_surrogate(*p) && is_hi_surrogate(p[-1])) { - cptr = (const uint8_t *)(p - 1); - } - } - - ret = lre_exec_backtrack(s, capture, aux_stack, bc_buf + RE_HEADER_LEN, - cptr); - if (s->stack_buf != s->static_stack_buf) - lre_realloc(s->opaque, s->stack_buf, 0); - return ret; -} - -int lre_get_capture_count(const uint8_t *bc_buf) -{ - return bc_buf[RE_HEADER_CAPTURE_COUNT]; -} - -int lre_get_flags(const uint8_t *bc_buf) -{ - return get_u16(bc_buf + RE_HEADER_FLAGS); -} - -/* Return NULL if no group names. Otherwise, return a pointer to - 'capture_count - 1' zero terminated UTF-8 strings. */ -const char *lre_get_groupnames(const uint8_t *bc_buf) -{ - uint32_t re_bytecode_len; - if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) - return NULL; - re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN); - return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); -} - -#ifdef TEST - -BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) -{ - return FALSE; -} - -void *lre_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -int main(int argc, char **argv) -{ - int len, flags, ret, i; - uint8_t *bc; - char error_msg[64]; - uint8_t *capture[CAPTURE_COUNT_MAX * 2]; - const char *input; - int input_len, capture_count; - - if (argc < 4) { - printf("usage: %s regexp flags input\n", argv[0]); - return 1; - } - flags = atoi(argv[2]); - bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], - strlen(argv[1]), flags, NULL); - if (!bc) { - fprintf(stderr, "error: %s\n", error_msg); - exit(1); - } - - input = argv[3]; - input_len = strlen(input); - - ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); - printf("ret=%d\n", ret); - if (ret == 1) { - capture_count = lre_get_capture_count(bc); - for(i = 0; i < 2 * capture_count; i++) { - uint8_t *ptr; - ptr = capture[i]; - printf("%d: ", i); - if (!ptr) - printf(""); - else - printf("%u", (int)(ptr - (uint8_t *)input)); - printf("\n"); - } - } - return 0; -} -#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h deleted file mode 100755 index da76e4ce..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 - -#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); - -/* must be provided by the user, return non zero if overflow */ -int 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); - -#endif /* LIBREGEXP_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode-table.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode-table.h deleted file mode 100755 index 67df6b3a..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode-table.h +++ /dev/null @@ -1,5149 +0,0 @@ -/* 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, 0x53e60240, - 0x53e80240, 0x53eb0640, 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, - 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, 0x52, 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, 0x55, 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, - 0x54, 0x00, 0x3e, 0x69, 0xbb, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x25, 0x00, 0x48, 0xaa, 0x8a, 0x8b, - 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, - 0x6f, 0xb2, 0x63, 0x62, 0x65, 0x64, 0x67, 0x66, - 0x6c, 0x6d, 0x6e, 0x6f, 0x68, 0x69, 0x6a, 0x6b, - 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[193] = { - 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, 0x00, 0x39, 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, 0xa2, 0x00, 0x80, 0x9b, - 0x12, 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, // 002B9 at 36 - 0xa0, 0x1e, 0x40, // 01EA0 at 66 - 0x9e, 0xa6, 0x40, // 0A69E at 98 - 0xbb, 0x07, 0x01, // 107BB at 128 - 0xdb, 0xd6, 0x01, // 1D6DB at 160 - 0x8a, 0xf1, 0x01, // 1F18A at 192 (upper bound) -}; - -static const uint8_t unicode_prop_Case_Ignorable_table[764] = { - 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, - 0x9e, 0xb0, 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, 0xe6, 0x12, 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, - 0xce, 0x83, 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, - 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, - 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, - 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 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, 0x60, 0x40, 0x0a, - 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, 0x42, 0xdf, 0x86, 0xec, 0x87, - 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, 0x80, 0x9d, - 0xdf, 0xff, 0x40, 0xef, -}; - -static const uint8_t unicode_prop_Case_Ignorable_index[72] = { - 0xbe, 0x05, 0x00, // 005BE at 32 - 0xfe, 0x07, 0x00, // 007FE at 64 - 0x52, 0x0a, 0xa0, // 00A52 at 101 - 0xc1, 0x0b, 0x00, // 00BC1 at 128 - 0x82, 0x0d, 0x00, // 00D82 at 160 - 0x3f, 0x10, 0x80, // 0103F at 196 - 0xd4, 0x17, 0x40, // 017D4 at 226 - 0xcf, 0x1a, 0x20, // 01ACF at 257 - 0xf5, 0x1c, 0x00, // 01CF5 at 288 - 0x80, 0x20, 0x00, // 02080 at 320 - 0x16, 0xa0, 0x00, // 0A016 at 352 - 0xc6, 0xa8, 0x00, // 0A8C6 at 384 - 0xc2, 0xaa, 0x60, // 0AAC2 at 419 - 0x56, 0xfe, 0x20, // 0FE56 at 449 - 0xb1, 0x07, 0x01, // 107B1 at 480 - 0x02, 0x10, 0x01, // 11002 at 512 - 0x42, 0x12, 0x41, // 11242 at 546 - 0xc4, 0x14, 0x21, // 114C4 at 577 - 0xe1, 0x19, 0x81, // 119E1 at 612 - 0x48, 0x1d, 0x01, // 11D48 at 640 - 0x44, 0x6b, 0x01, // 16B44 at 672 - 0x83, 0xd1, 0x21, // 1D183 at 705 - 0x3e, 0xe1, 0x01, // 1E13E at 736 - 0xf0, 0x01, 0x0e, // E01F0 at 768 (upper bound) -}; - -static const uint8_t unicode_prop_ID_Start_table[1133] = { - 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, 0x05, 0x90, 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, 0x11, 0x01, 0x81, 0x9d, 0x03, - 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e, - 0x08, 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, 0xc2, 0x09, 0x00, 0x07, - 0x94, 0x8f, 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, 0xc5, - 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, - 0x82, 0xba, 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, 0x41, 0x46, 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, 0xff, - 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, - 0x57, 0xf7, 0x87, 0x44, 0xd5, 0xa8, 0x89, 0x60, - 0x22, 0xe6, 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, 0x41, 0xee, 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, 0x50, 0x39, 0x85, 0x40, - 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, - 0x42, 0x6d, 0x49, 0xa1, 0x42, 0x1d, 0x45, 0xe1, - 0x53, 0x4a, 0x84, 0x50, 0x5f, -}; - -static const uint8_t unicode_prop_ID_Start_index[108] = { - 0xf6, 0x03, 0x20, // 003F6 at 33 - 0xa6, 0x07, 0x00, // 007A6 at 64 - 0xa9, 0x09, 0x20, // 009A9 at 97 - 0xb1, 0x0a, 0x00, // 00AB1 at 128 - 0xba, 0x0b, 0x20, // 00BBA at 161 - 0x3b, 0x0d, 0x20, // 00D3B at 193 - 0xc7, 0x0e, 0x20, // 00EC7 at 225 - 0x49, 0x12, 0x00, // 01249 at 256 - 0x9b, 0x16, 0x00, // 0169B at 288 - 0xac, 0x19, 0x00, // 019AC at 320 - 0xc0, 0x1d, 0x80, // 01DC0 at 356 - 0x80, 0x20, 0x20, // 02080 at 385 - 0x70, 0x2d, 0x00, // 02D70 at 416 - 0x00, 0x32, 0x00, // 03200 at 448 - 0xdd, 0xa7, 0x00, // 0A7DD at 480 - 0x4c, 0xaa, 0x20, // 0AA4C at 513 - 0xc7, 0xd7, 0x20, // 0D7C7 at 545 - 0xfc, 0xfd, 0x20, // 0FDFC at 577 - 0x9d, 0x02, 0x21, // 1029D at 609 - 0x96, 0x05, 0x01, // 10596 at 640 - 0x9f, 0x08, 0x01, // 1089F at 672 - 0x49, 0x0c, 0x21, // 10C49 at 705 - 0x76, 0x10, 0x21, // 11076 at 737 - 0xa9, 0x12, 0x01, // 112A9 at 768 - 0xb0, 0x14, 0x01, // 114B0 at 800 - 0x42, 0x19, 0x41, // 11942 at 834 - 0x90, 0x1c, 0x01, // 11C90 at 864 - 0xf1, 0x2f, 0x21, // 12FF1 at 897 - 0x90, 0x6b, 0x21, // 16B90 at 929 - 0x33, 0xb1, 0x21, // 1B133 at 961 - 0x06, 0xd5, 0x01, // 1D506 at 992 - 0xc3, 0xd7, 0x01, // 1D7C3 at 1024 - 0xff, 0xe7, 0x21, // 1E7FF at 1057 - 0x63, 0xee, 0x01, // 1EE63 at 1088 - 0x5e, 0xee, 0x42, // 2EE5E at 1122 - 0xb0, 0x23, 0x03, // 323B0 at 1152 (upper bound) -}; - -static const uint8_t unicode_prop_ID_Continue1_table[695] = { - 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, 0x8f, 0xb0, 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, 0xce, 0x83, - 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, - 0x41, 0x55, 0x89, 0xb4, 0x38, 0x87, 0x8f, 0x89, - 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, - 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, - 0x41, 0x48, 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, 0x42, 0xd4, 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, // 006FA at 32 - 0x70, 0x09, 0x00, // 00970 at 64 - 0xf0, 0x0a, 0x40, // 00AF0 at 98 - 0x57, 0x0c, 0x00, // 00C57 at 128 - 0xf0, 0x0d, 0x60, // 00DF0 at 163 - 0xc7, 0x0f, 0x20, // 00FC7 at 193 - 0xea, 0x17, 0x40, // 017EA at 226 - 0x05, 0x1b, 0x00, // 01B05 at 256 - 0x0e, 0x20, 0x00, // 0200E at 288 - 0xa0, 0xa6, 0x20, // 0A6A0 at 321 - 0xe6, 0xa9, 0x20, // 0A9E6 at 353 - 0x10, 0xfe, 0x00, // 0FE10 at 384 - 0x40, 0x0a, 0x01, // 10A40 at 416 - 0xc3, 0x10, 0x01, // 110C3 at 448 - 0x4e, 0x13, 0x01, // 1134E at 480 - 0x41, 0x16, 0x01, // 11641 at 512 - 0x0b, 0x1a, 0x01, // 11A0B at 544 - 0xaa, 0x1d, 0x01, // 11DAA at 576 - 0x7a, 0x6d, 0x21, // 16D7A at 609 - 0x45, 0xd2, 0x21, // 1D245 at 641 - 0xaf, 0xe2, 0x01, // 1E2AF at 672 - 0xf0, 0x01, 0x0e, // E01F0 at 704 (upper bound) -}; - -#ifdef CONFIG_ALL_UNICODE - -static const uint8_t unicode_cc_table[916] = { - 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, 0xc3, 0xb0, 0x34, 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, 0x1f, 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, 0xb2, 0xaf, 0x06, 0xdc, 0xb0, - 0x3c, 0xc5, 0x00, 0x07, -}; - -static const uint8_t unicode_cc_index[87] = { - 0x4d, 0x03, 0x00, // 0034D at 32 - 0x97, 0x05, 0x20, // 00597 at 65 - 0xc6, 0x05, 0x00, // 005C6 at 96 - 0xe7, 0x06, 0x00, // 006E7 at 128 - 0x45, 0x07, 0x00, // 00745 at 160 - 0x9c, 0x08, 0x00, // 0089C at 192 - 0x4d, 0x09, 0x00, // 0094D at 224 - 0x3c, 0x0b, 0x00, // 00B3C at 256 - 0x3d, 0x0d, 0x00, // 00D3D at 288 - 0x36, 0x0f, 0x00, // 00F36 at 320 - 0x38, 0x10, 0x20, // 01038 at 353 - 0x3a, 0x19, 0x00, // 0193A at 384 - 0xcb, 0x1a, 0x20, // 01ACB at 417 - 0xd3, 0x1c, 0x00, // 01CD3 at 448 - 0xcf, 0x1d, 0x00, // 01DCF at 480 - 0xe2, 0x20, 0x00, // 020E2 at 512 - 0x2e, 0x30, 0x20, // 0302E at 545 - 0x2b, 0xa9, 0x20, // 0A92B at 577 - 0xed, 0xab, 0x00, // 0ABED at 608 - 0x39, 0x0a, 0x01, // 10A39 at 640 - 0x4c, 0x0f, 0x01, // 10F4C at 672 - 0x35, 0x11, 0x21, // 11135 at 705 - 0x66, 0x13, 0x01, // 11366 at 736 - 0x40, 0x16, 0x01, // 11640 at 768 - 0x47, 0x1a, 0x01, // 11A47 at 800 - 0xf0, 0x6a, 0x21, // 16AF0 at 833 - 0x8a, 0xd1, 0x01, // 1D18A at 864 - 0xec, 0xe4, 0x21, // 1E4EC at 897 - 0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound) -}; - -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, 0x29fc8195, 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, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e, - 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e, - 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5, - 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e, - 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3, - 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada, - 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52, - 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24, - 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c4d, 0x1c52, 0x1c88, 0x1cce, - 0x1cdc, 0x1ce1, 0x1cea, 0x1cf3, 0x1d01, 0x1d06, 0x1d0b, 0x1d1d, - 0x1d2f, 0x1d38, 0x1d3d, 0x1d61, 0x1d6f, 0x1d71, 0x1d73, 0x1d93, - 0x1dae, 0x1db0, 0x1db2, 0x1db4, 0x1db6, 0x1db8, 0x1dba, 0x1dbc, - 0x1ddc, 0x1dde, 0x1de0, 0x1de2, 0x1de4, 0x1deb, 0x1ded, 0x1def, - 0x1df1, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, - 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, - 0x1e20, 0x03f4, 0x1e22, 0x2207, 0x1e24, 0x2202, 0x1e26, 0x1e2e, - 0x03f4, 0x1e30, 0x2207, 0x1e32, 0x2202, 0x1e34, 0x1e3c, 0x03f4, - 0x1e3e, 0x2207, 0x1e40, 0x2202, 0x1e42, 0x1e4a, 0x03f4, 0x1e4c, - 0x2207, 0x1e4e, 0x2202, 0x1e50, 0x1e58, 0x03f4, 0x1e5a, 0x2207, - 0x1e5c, 0x2202, 0x1e5e, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, - 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e80, 0x1ea3, 0x1ea7, 0x1ead, - 0x1eca, 0x062d, 0x1ed2, 0x1ede, 0x062c, 0x1eee, 0x1f5e, 0x1f6a, - 0x1f7d, 0x1f8f, 0x1fa2, 0x1fa4, 0x1fa8, 0x1fae, 0x1fb4, 0x1fb6, - 0x1fba, 0x1fbc, 0x1fc4, 0x1fc7, 0x1fc9, 0x1fcf, 0x1fd1, 0x30b5, - 0x1fd7, 0x202f, 0x2045, 0x2049, 0x204b, 0x2050, 0x209d, 0x20ae, - 0x21af, 0x21bf, 0x21c5, 0x22bf, 0x23dd, -}; - -static const uint8_t unicode_decomp_data[9451] = { - 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, 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[4070] = { - 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, 0x05, - 0xe2, 0x13, 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, 0xa5, 0x00, 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, 0x20, 0x05, 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, 0xa0, - 0x25, 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, 0x08, 0xe0, 0x29, 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, 0x19, - 0xe0, 0x07, 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, 0x18, 0x00, 0xef, - 0x61, 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, 0x3f, 0x20, 0x3f, 0x00, 0x02, 0x00, 0x02, - 0xdf, 0xe0, 0x0d, 0x44, 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, 0xe0, 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, - 0xef, 0x08, 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, - 0x0f, 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, 0xe0, 0x38, - 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, 0xe0, - 0x2f, 0x66, 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, 0x80, 0x2e, 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, 0xe0, - 0x80, 0xae, 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, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, - 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, - 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, - 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, - 0x4e, 0xe0, 0x21, 0xe5, 0x02, 0xe0, 0xa2, 0x5f, - 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, 0xa0, 0xef, 0x81, 0x2c, - 0xe0, 0x44, 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, 0x81, - 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, 0x50, - 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, 0xef, - 0x6f, 0x60, 0xef, 0x57, 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, 0x36, 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, - 0x06, 0x20, 0xef, 0x05, 0x40, 0xef, 0x02, 0x80, - 0xef, 0x30, 0xc0, 0xef, 0x07, 0x20, 0xef, 0x03, - 0xa0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, 0x00, - 0xef, 0x54, 0xe9, 0x02, 0xe0, 0x83, 0x7e, 0xe5, - 0xc0, 0x66, 0x58, 0xe0, 0x18, 0xe5, 0x8f, 0xb2, - 0xa0, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, - 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x07, 0xe5, - 0x81, 0xe6, 0xe0, 0x89, 0x1a, 0xe5, 0x81, 0x96, - 0xe0, 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, - 0x8f, 0xd8, 0xe0, 0xca, 0x9b, 0xc9, 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_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_Garay, - UNICODE_SCRIPT_Georgian, - UNICODE_SCRIPT_Glagolitic, - UNICODE_SCRIPT_Gothic, - 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_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_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_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_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[] = - "Unknown,Zzzz" "\0" - "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" - "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" - "Garay,Gara" "\0" - "Georgian,Geor" "\0" - "Glagolitic,Glag" "\0" - "Gothic,Goth" "\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" - "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" - "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" - "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" - "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[2803] = { - 0xc0, 0x19, 0x99, 0x4a, 0x85, 0x19, 0x99, 0x4a, - 0xae, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x80, 0x4a, - 0x84, 0x19, 0x96, 0x4a, 0x80, 0x19, 0x9e, 0x4a, - 0x80, 0x19, 0xe1, 0x60, 0x4a, 0xa6, 0x19, 0x84, - 0x4a, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, - 0x0f, 0x3a, 0x83, 0x2d, 0x80, 0x19, 0x82, 0x2d, - 0x01, 0x83, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x03, - 0x80, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x80, 0x19, - 0x82, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x93, 0x2d, - 0x00, 0xbe, 0x2d, 0x8d, 0x1a, 0x8f, 0x2d, 0xe0, - 0x24, 0x1d, 0x81, 0x3a, 0xe0, 0x48, 0x1d, 0x00, - 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, - 0x00, 0xb6, 0x37, 0x07, 0x9a, 0x37, 0x03, 0x85, - 0x37, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, - 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04, - 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04, - 0x8a, 0x3a, 0x99, 0x04, 0x80, 0x3a, 0xe0, 0x0b, - 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x90, 0x00, - 0xbb, 0x90, 0x01, 0x82, 0x90, 0xaf, 0x04, 0xb1, - 0x9a, 0x0d, 0xba, 0x69, 0x01, 0x82, 0x69, 0xad, - 0x83, 0x01, 0x8e, 0x83, 0x00, 0x9b, 0x55, 0x01, - 0x80, 0x55, 0x00, 0x8a, 0x90, 0x04, 0x9e, 0x04, - 0x00, 0x81, 0x04, 0x04, 0xca, 0x04, 0x80, 0x19, - 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x3a, 0x8e, 0x20, - 0x81, 0x19, 0x99, 0x20, 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, 0x30, - 0x00, 0x85, 0x30, 0x03, 0x81, 0x30, 0x01, 0x95, - 0x30, 0x00, 0x86, 0x30, 0x00, 0x81, 0x30, 0x00, - 0x81, 0x30, 0x00, 0x81, 0x30, 0x01, 0x80, 0x30, - 0x00, 0x84, 0x30, 0x03, 0x81, 0x30, 0x01, 0x82, - 0x30, 0x02, 0x80, 0x30, 0x06, 0x83, 0x30, 0x00, - 0x80, 0x30, 0x06, 0x90, 0x30, 0x09, 0x82, 0x2e, - 0x00, 0x88, 0x2e, 0x00, 0x82, 0x2e, 0x00, 0x95, - 0x2e, 0x00, 0x86, 0x2e, 0x00, 0x81, 0x2e, 0x00, - 0x84, 0x2e, 0x01, 0x89, 0x2e, 0x00, 0x82, 0x2e, - 0x00, 0x82, 0x2e, 0x01, 0x80, 0x2e, 0x0e, 0x83, - 0x2e, 0x01, 0x8b, 0x2e, 0x06, 0x86, 0x2e, 0x00, - 0x82, 0x78, 0x00, 0x87, 0x78, 0x01, 0x81, 0x78, - 0x01, 0x95, 0x78, 0x00, 0x86, 0x78, 0x00, 0x81, - 0x78, 0x00, 0x84, 0x78, 0x01, 0x88, 0x78, 0x01, - 0x81, 0x78, 0x01, 0x82, 0x78, 0x06, 0x82, 0x78, - 0x03, 0x81, 0x78, 0x00, 0x84, 0x78, 0x01, 0x91, - 0x78, 0x09, 0x81, 0x97, 0x00, 0x85, 0x97, 0x02, - 0x82, 0x97, 0x00, 0x83, 0x97, 0x02, 0x81, 0x97, - 0x00, 0x80, 0x97, 0x00, 0x81, 0x97, 0x02, 0x81, - 0x97, 0x02, 0x82, 0x97, 0x02, 0x8b, 0x97, 0x03, - 0x84, 0x97, 0x02, 0x82, 0x97, 0x00, 0x83, 0x97, - 0x01, 0x80, 0x97, 0x05, 0x80, 0x97, 0x0d, 0x94, - 0x97, 0x04, 0x8c, 0x99, 0x00, 0x82, 0x99, 0x00, - 0x96, 0x99, 0x00, 0x8f, 0x99, 0x01, 0x88, 0x99, - 0x00, 0x82, 0x99, 0x00, 0x83, 0x99, 0x06, 0x81, - 0x99, 0x00, 0x82, 0x99, 0x01, 0x80, 0x99, 0x01, - 0x83, 0x99, 0x01, 0x89, 0x99, 0x06, 0x88, 0x99, - 0x8c, 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x96, 0x3f, - 0x00, 0x89, 0x3f, 0x00, 0x84, 0x3f, 0x01, 0x88, - 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x83, 0x3f, 0x06, - 0x81, 0x3f, 0x05, 0x81, 0x3f, 0x00, 0x83, 0x3f, - 0x01, 0x89, 0x3f, 0x00, 0x82, 0x3f, 0x0b, 0x8c, - 0x54, 0x00, 0x82, 0x54, 0x00, 0xb2, 0x54, 0x00, - 0x82, 0x54, 0x00, 0x85, 0x54, 0x03, 0x8f, 0x54, - 0x01, 0x99, 0x54, 0x00, 0x82, 0x89, 0x00, 0x91, - 0x89, 0x02, 0x97, 0x89, 0x00, 0x88, 0x89, 0x00, - 0x80, 0x89, 0x01, 0x86, 0x89, 0x02, 0x80, 0x89, - 0x03, 0x85, 0x89, 0x00, 0x80, 0x89, 0x00, 0x87, - 0x89, 0x05, 0x89, 0x89, 0x01, 0x82, 0x89, 0x0b, - 0xb9, 0x9b, 0x03, 0x80, 0x19, 0x9b, 0x9b, 0x24, - 0x81, 0x49, 0x00, 0x80, 0x49, 0x00, 0x84, 0x49, - 0x00, 0x97, 0x49, 0x00, 0x80, 0x49, 0x00, 0x96, - 0x49, 0x01, 0x84, 0x49, 0x00, 0x80, 0x49, 0x00, - 0x86, 0x49, 0x00, 0x89, 0x49, 0x01, 0x83, 0x49, - 0x1f, 0xc7, 0x9c, 0x00, 0xa3, 0x9c, 0x03, 0xa6, - 0x9c, 0x00, 0xa3, 0x9c, 0x00, 0x8e, 0x9c, 0x00, - 0x86, 0x9c, 0x83, 0x19, 0x81, 0x9c, 0x24, 0xe0, - 0x3f, 0x63, 0xa5, 0x29, 0x00, 0x80, 0x29, 0x04, - 0x80, 0x29, 0x01, 0xaa, 0x29, 0x80, 0x19, 0x83, - 0x29, 0xe0, 0x9f, 0x33, 0xc8, 0x27, 0x00, 0x83, - 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, - 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, - 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, - 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, - 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27, - 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99, - 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, - 0xe2, 0x1f, 0x12, 0x9c, 0x6c, 0x02, 0xca, 0x82, - 0x82, 0x19, 0x8a, 0x82, 0x06, 0x95, 0x91, 0x08, - 0x80, 0x91, 0x94, 0x35, 0x81, 0x19, 0x08, 0x93, - 0x11, 0x0b, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00, - 0x81, 0x92, 0x0b, 0xdd, 0x44, 0x01, 0x89, 0x44, - 0x05, 0x89, 0x44, 0x05, 0x81, 0x60, 0x81, 0x19, - 0x80, 0x60, 0x80, 0x19, 0x93, 0x60, 0x05, 0xd8, - 0x60, 0x06, 0xaa, 0x60, 0x04, 0xc5, 0x12, 0x09, - 0x9e, 0x4c, 0x00, 0x8b, 0x4c, 0x03, 0x8b, 0x4c, - 0x03, 0x80, 0x4c, 0x02, 0x8b, 0x4c, 0x9d, 0x93, - 0x01, 0x84, 0x93, 0x0a, 0xab, 0x67, 0x03, 0x99, - 0x67, 0x05, 0x8a, 0x67, 0x02, 0x81, 0x67, 0x9f, - 0x44, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x94, - 0x00, 0x9c, 0x94, 0x01, 0x8a, 0x94, 0x05, 0x89, - 0x94, 0x05, 0x8d, 0x94, 0x01, 0x9e, 0x3a, 0x30, - 0xcc, 0x07, 0x00, 0xb1, 0x07, 0xbf, 0x8d, 0xb3, - 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x4b, 0x02, 0x8e, - 0x4b, 0x02, 0x82, 0x4b, 0xaf, 0x6d, 0x8a, 0x1d, - 0x04, 0xaa, 0x29, 0x01, 0x82, 0x29, 0x87, 0x8d, - 0x07, 0x82, 0x3a, 0x80, 0x19, 0x8c, 0x3a, 0x80, - 0x19, 0x86, 0x3a, 0x83, 0x19, 0x80, 0x3a, 0x85, - 0x19, 0x80, 0x3a, 0x82, 0x19, 0x81, 0x3a, 0x80, - 0x19, 0x04, 0xa5, 0x4a, 0x84, 0x2d, 0x80, 0x1d, - 0xb0, 0x4a, 0x84, 0x2d, 0x83, 0x4a, 0x84, 0x2d, - 0x8c, 0x4a, 0x80, 0x1d, 0xc5, 0x4a, 0x80, 0x2d, - 0xbf, 0x3a, 0xe0, 0x9f, 0x4a, 0x95, 0x2d, 0x01, - 0x85, 0x2d, 0x01, 0xa5, 0x2d, 0x01, 0x85, 0x2d, - 0x01, 0x87, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x80, - 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x9e, 0x2d, 0x01, - 0xb4, 0x2d, 0x00, 0x8e, 0x2d, 0x00, 0x8d, 0x2d, - 0x01, 0x85, 0x2d, 0x00, 0x92, 0x2d, 0x01, 0x82, - 0x2d, 0x00, 0x88, 0x2d, 0x00, 0x8b, 0x19, 0x81, - 0x3a, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, 0x4a, - 0x01, 0x8a, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x00, - 0x8c, 0x4a, 0x02, 0xa0, 0x19, 0x0e, 0xa0, 0x3a, - 0x0e, 0xa5, 0x19, 0x80, 0x2d, 0x82, 0x19, 0x81, - 0x4a, 0x85, 0x19, 0x80, 0x4a, 0x9a, 0x19, 0x80, - 0x4a, 0x90, 0x19, 0xa8, 0x4a, 0x82, 0x19, 0x03, - 0xe2, 0x39, 0x19, 0x15, 0x8a, 0x19, 0x14, 0xe3, - 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x19, - 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, 0xdf, - 0x2a, 0x9f, 0x4a, 0xe0, 0x13, 0x1a, 0x04, 0x86, - 0x1a, 0xa5, 0x29, 0x00, 0x80, 0x29, 0x04, 0x80, - 0x29, 0x01, 0xb7, 0x9d, 0x06, 0x81, 0x9d, 0x0d, - 0x80, 0x9d, 0x96, 0x27, 0x08, 0x86, 0x27, 0x00, - 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, - 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, - 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, 0xdd, - 0x19, 0x21, 0x99, 0x32, 0x00, 0xd8, 0x32, 0x0b, - 0xe0, 0x75, 0x32, 0x19, 0x94, 0x19, 0x80, 0x32, - 0x80, 0x19, 0x80, 0x32, 0x98, 0x19, 0x88, 0x32, - 0x83, 0x3a, 0x81, 0x33, 0x87, 0x19, 0x83, 0x32, - 0x83, 0x19, 0x00, 0xd5, 0x38, 0x01, 0x81, 0x3a, - 0x81, 0x19, 0x82, 0x38, 0x80, 0x19, 0xd9, 0x40, - 0x81, 0x19, 0x82, 0x40, 0x04, 0xaa, 0x0d, 0x00, - 0xdd, 0x33, 0x00, 0x8f, 0x19, 0x9f, 0x0d, 0xa5, - 0x19, 0x08, 0x80, 0x19, 0x8f, 0x40, 0x9e, 0x33, - 0x00, 0xbf, 0x19, 0x9e, 0x33, 0xd0, 0x19, 0xae, - 0x40, 0x80, 0x19, 0xd7, 0x40, 0xe0, 0x47, 0x19, - 0xf0, 0x09, 0x5f, 0x32, 0xbf, 0x19, 0xf0, 0x41, - 0x9f, 0x32, 0xe4, 0x2c, 0xa9, 0x02, 0xb6, 0xa9, - 0x08, 0xaf, 0x4f, 0xe0, 0xcb, 0xa4, 0x13, 0xdf, - 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, 0xe0, 0x05, - 0x4a, 0x82, 0x19, 0xc2, 0x4a, 0x01, 0x81, 0x4a, - 0x00, 0x80, 0x4a, 0x00, 0x87, 0x4a, 0x14, 0x8d, - 0x4a, 0xac, 0x8f, 0x02, 0x89, 0x19, 0x05, 0xb7, - 0x7e, 0x07, 0xc5, 0x84, 0x07, 0x8b, 0x84, 0x05, - 0x9f, 0x20, 0xad, 0x42, 0x80, 0x19, 0x80, 0x42, - 0xa3, 0x81, 0x0a, 0x80, 0x81, 0x9c, 0x33, 0x02, - 0xcd, 0x3d, 0x00, 0x80, 0x19, 0x89, 0x3d, 0x03, - 0x81, 0x3d, 0x9e, 0x63, 0x00, 0xb6, 0x16, 0x08, - 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, 0x16, - 0x9f, 0x63, 0xc2, 0x95, 0x17, 0x84, 0x95, 0x96, - 0x5a, 0x09, 0x85, 0x27, 0x01, 0x85, 0x27, 0x01, - 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, 0x86, 0x27, - 0x00, 0xaa, 0x4a, 0x80, 0x19, 0x88, 0x4a, 0x80, - 0x2d, 0x83, 0x4a, 0x81, 0x19, 0x03, 0xcf, 0x17, - 0xad, 0x5a, 0x01, 0x89, 0x5a, 0x05, 0xf0, 0x1b, - 0x43, 0x33, 0x0b, 0x96, 0x33, 0x03, 0xb0, 0x33, - 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x32, 0x01, 0xe0, - 0x09, 0x32, 0x25, 0x86, 0x4a, 0x0b, 0x84, 0x05, - 0x04, 0x99, 0x37, 0x00, 0x84, 0x37, 0x00, 0x80, - 0x37, 0x00, 0x81, 0x37, 0x00, 0x81, 0x37, 0x00, - 0x89, 0x37, 0xe0, 0x12, 0x04, 0x0f, 0xe1, 0x0a, - 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, 0xb5, 0x04, - 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, 0x8f, 0x3a, - 0x89, 0x19, 0x05, 0x8d, 0x3a, 0x81, 0x1d, 0xa2, - 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, 0x19, 0x03, - 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, - 0x19, 0x00, 0x9f, 0x19, 0x99, 0x4a, 0x85, 0x19, - 0x99, 0x4a, 0x8a, 0x19, 0x89, 0x40, 0x80, 0x19, - 0xac, 0x40, 0x81, 0x19, 0x9e, 0x33, 0x02, 0x85, - 0x33, 0x01, 0x85, 0x33, 0x01, 0x85, 0x33, 0x01, - 0x82, 0x33, 0x02, 0x86, 0x19, 0x00, 0x86, 0x19, - 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4e, 0x00, 0x99, - 0x4e, 0x00, 0x92, 0x4e, 0x00, 0x81, 0x4e, 0x00, - 0x8e, 0x4e, 0x01, 0x8d, 0x4e, 0x21, 0xe0, 0x1a, - 0x4e, 0x04, 0x82, 0x19, 0x03, 0xac, 0x19, 0x02, - 0x88, 0x19, 0xce, 0x2d, 0x00, 0x8c, 0x19, 0x02, - 0x80, 0x2d, 0x2e, 0xac, 0x19, 0x80, 0x3a, 0x60, - 0x21, 0x9c, 0x50, 0x02, 0xb0, 0x13, 0x0e, 0x80, - 0x3a, 0x9a, 0x19, 0x03, 0xa3, 0x70, 0x08, 0x82, - 0x70, 0x9a, 0x2b, 0x04, 0xaa, 0x72, 0x04, 0x9d, - 0xa3, 0x00, 0x80, 0xa3, 0xa3, 0x73, 0x03, 0x8d, - 0x73, 0x29, 0xcf, 0x1f, 0xaf, 0x86, 0x9d, 0x7a, - 0x01, 0x89, 0x7a, 0x05, 0xa3, 0x79, 0x03, 0xa3, - 0x79, 0x03, 0xa7, 0x25, 0x07, 0xb3, 0x14, 0x0a, - 0x80, 0x14, 0x8a, 0xa5, 0x00, 0x8e, 0xa5, 0x00, - 0x86, 0xa5, 0x00, 0x81, 0xa5, 0x00, 0x8a, 0xa5, - 0x00, 0x8e, 0xa5, 0x00, 0x86, 0xa5, 0x00, 0x81, - 0xa5, 0x02, 0xb3, 0xa0, 0x0b, 0xe0, 0xd6, 0x4d, - 0x08, 0x95, 0x4d, 0x09, 0x87, 0x4d, 0x17, 0x85, - 0x4a, 0x00, 0xa9, 0x4a, 0x00, 0x88, 0x4a, 0x44, - 0x85, 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, - 0x00, 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, - 0x1c, 0x95, 0x39, 0x00, 0x88, 0x39, 0x9f, 0x7c, - 0x9e, 0x64, 0x07, 0x88, 0x64, 0x2f, 0x92, 0x36, - 0x00, 0x81, 0x36, 0x04, 0x84, 0x36, 0x9b, 0x7f, - 0x02, 0x80, 0x7f, 0x99, 0x51, 0x04, 0x80, 0x51, - 0x3f, 0x9f, 0x5d, 0x97, 0x5c, 0x03, 0x93, 0x5c, - 0x01, 0xad, 0x5c, 0x83, 0x43, 0x00, 0x81, 0x43, - 0x04, 0x87, 0x43, 0x00, 0x82, 0x43, 0x00, 0x9c, - 0x43, 0x01, 0x82, 0x43, 0x03, 0x89, 0x43, 0x06, - 0x88, 0x43, 0x06, 0x9f, 0x75, 0x9f, 0x71, 0x1f, - 0xa6, 0x56, 0x03, 0x8b, 0x56, 0x08, 0xb5, 0x06, - 0x02, 0x86, 0x06, 0x95, 0x3c, 0x01, 0x87, 0x3c, - 0x92, 0x3b, 0x04, 0x87, 0x3b, 0x91, 0x80, 0x06, - 0x83, 0x80, 0x0b, 0x86, 0x80, 0x4f, 0xc8, 0x76, - 0x36, 0xb2, 0x6f, 0x0c, 0xb2, 0x6f, 0x06, 0x85, - 0x6f, 0xa7, 0x34, 0x07, 0x89, 0x34, 0x05, 0xa5, - 0x28, 0x02, 0x9c, 0x28, 0x07, 0x81, 0x28, 0x60, - 0x6f, 0x9e, 0x04, 0x00, 0xa9, 0xa8, 0x00, 0x82, - 0xa8, 0x01, 0x81, 0xa8, 0x0f, 0x82, 0x04, 0x36, - 0x83, 0x04, 0xa7, 0x74, 0x07, 0xa9, 0x8a, 0x15, - 0x99, 0x77, 0x25, 0x9b, 0x18, 0x13, 0x96, 0x26, - 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08, 0x80, - 0x0e, 0xc2, 0x3e, 0x09, 0x80, 0x3e, 0x01, 0x98, - 0x8b, 0x06, 0x89, 0x8b, 0x05, 0xb4, 0x15, 0x00, - 0x91, 0x15, 0x07, 0xa6, 0x53, 0x08, 0xdf, 0x85, - 0x00, 0x93, 0x89, 0x0a, 0x91, 0x45, 0x00, 0xae, - 0x45, 0x3d, 0x86, 0x62, 0x00, 0x80, 0x62, 0x00, - 0x83, 0x62, 0x00, 0x8e, 0x62, 0x00, 0x8a, 0x62, - 0x05, 0xba, 0x47, 0x04, 0x89, 0x47, 0x05, 0x83, - 0x2c, 0x00, 0x87, 0x2c, 0x01, 0x81, 0x2c, 0x01, - 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, - 0x00, 0x84, 0x2c, 0x00, 0x80, 0x3a, 0x88, 0x2c, - 0x01, 0x81, 0x2c, 0x01, 0x82, 0x2c, 0x01, 0x80, - 0x2c, 0x05, 0x80, 0x2c, 0x04, 0x86, 0x2c, 0x01, - 0x86, 0x2c, 0x02, 0x84, 0x2c, 0x0a, 0x89, 0xa2, - 0x00, 0x80, 0xa2, 0x01, 0x80, 0xa2, 0x00, 0xa5, - 0xa2, 0x00, 0x89, 0xa2, 0x00, 0x80, 0xa2, 0x01, - 0x80, 0xa2, 0x00, 0x83, 0xa2, 0x00, 0x89, 0xa2, - 0x00, 0x81, 0xa2, 0x07, 0x81, 0xa2, 0x1c, 0xdb, - 0x68, 0x00, 0x84, 0x68, 0x1d, 0xc7, 0x9e, 0x07, - 0x89, 0x9e, 0x60, 0x45, 0xb5, 0x87, 0x01, 0xa5, - 0x87, 0x21, 0xc4, 0x5f, 0x0a, 0x89, 0x5f, 0x05, - 0x8c, 0x60, 0x12, 0xb9, 0x96, 0x05, 0x89, 0x96, - 0x05, 0x93, 0x63, 0x1b, 0x9a, 0x02, 0x01, 0x8e, - 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, - 0x60, 0x03, 0xd2, 0xa7, 0x0b, 0x80, 0xa7, 0x86, - 0x21, 0x01, 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, - 0x81, 0x21, 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, - 0x01, 0x8b, 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, - 0x66, 0x01, 0xad, 0x66, 0x01, 0x8a, 0x66, 0x1a, - 0xc7, 0xaa, 0x07, 0xd2, 0x8c, 0x0c, 0x8f, 0x12, - 0xb8, 0x7d, 0x06, 0x89, 0x20, 0x60, 0x55, 0xa1, - 0x8e, 0x0d, 0x89, 0x8e, 0x05, 0x88, 0x0c, 0x00, - 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, - 0x02, 0x9f, 0x57, 0x01, 0x95, 0x57, 0x00, 0x8d, - 0x57, 0x48, 0x86, 0x58, 0x00, 0x81, 0x58, 0x00, - 0xab, 0x58, 0x02, 0x80, 0x58, 0x00, 0x81, 0x58, - 0x00, 0x88, 0x58, 0x07, 0x89, 0x58, 0x05, 0x85, - 0x2f, 0x00, 0x81, 0x2f, 0x00, 0xa4, 0x2f, 0x00, - 0x81, 0x2f, 0x00, 0x85, 0x2f, 0x06, 0x89, 0x2f, - 0x60, 0xd5, 0x98, 0x52, 0x06, 0x90, 0x41, 0x00, - 0xa8, 0x41, 0x02, 0x9c, 0x41, 0x54, 0x80, 0x4f, - 0x0e, 0xb1, 0x97, 0x0c, 0x80, 0x97, 0xe3, 0x39, - 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, 0x00, 0x84, - 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, 0xeb, 0xe0, - 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, 0x09, 0xef, - 0x3a, 0x24, 0x04, 0xe1, 0xe6, 0x03, 0x70, 0x0a, - 0x58, 0xb9, 0x31, 0x66, 0x65, 0xe1, 0xd8, 0x08, - 0x06, 0x9e, 0x61, 0x00, 0x89, 0x61, 0x03, 0x81, - 0x61, 0xce, 0x9f, 0x00, 0x89, 0x9f, 0x05, 0x9d, - 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x7b, 0x09, - 0x89, 0x7b, 0x00, 0x86, 0x7b, 0x00, 0x94, 0x7b, - 0x04, 0x92, 0x7b, 0x61, 0x4f, 0xb9, 0x48, 0x60, - 0x65, 0xda, 0x59, 0x60, 0x04, 0xca, 0x5e, 0x03, - 0xb8, 0x5e, 0x06, 0x90, 0x5e, 0x3f, 0x80, 0x98, - 0x80, 0x6a, 0x81, 0x32, 0x80, 0x46, 0x0a, 0x81, - 0x32, 0x0d, 0xf0, 0x07, 0x97, 0x98, 0x07, 0xe2, - 0x9f, 0x98, 0xe1, 0x75, 0x46, 0x28, 0x80, 0x46, - 0x88, 0x98, 0x70, 0x12, 0x86, 0x83, 0x40, 0x00, - 0x86, 0x40, 0x00, 0x81, 0x40, 0x00, 0x80, 0x40, - 0xe0, 0xbe, 0x38, 0x82, 0x40, 0x0e, 0x80, 0x38, - 0x1c, 0x82, 0x38, 0x01, 0x80, 0x40, 0x0d, 0x83, - 0x40, 0x07, 0xe1, 0x2b, 0x6a, 0x68, 0xa3, 0xe0, - 0x0a, 0x23, 0x04, 0x8c, 0x23, 0x02, 0x88, 0x23, - 0x06, 0x89, 0x23, 0x01, 0x83, 0x23, 0x83, 0x19, - 0x6e, 0xfb, 0xe0, 0x99, 0x19, 0x05, 0xe1, 0x53, - 0x19, 0x4b, 0xad, 0x3a, 0x01, 0x96, 0x3a, 0x08, - 0xe0, 0x13, 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, - 0xa6, 0x19, 0x01, 0xbd, 0x19, 0x82, 0x3a, 0x90, - 0x19, 0x87, 0x3a, 0x81, 0x19, 0x86, 0x3a, 0x9d, - 0x19, 0x83, 0x3a, 0xbc, 0x19, 0x14, 0xc5, 0x2d, - 0x60, 0x19, 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, - 0xd6, 0x19, 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, - 0x19, 0x00, 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, - 0x80, 0x19, 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, - 0x00, 0x8b, 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, - 0x19, 0x00, 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, - 0x87, 0x19, 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, - 0x00, 0x83, 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, - 0x19, 0x02, 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, - 0x01, 0xe0, 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, - 0x2b, 0x88, 0x0e, 0x84, 0x88, 0x00, 0x8e, 0x88, - 0x63, 0xef, 0x9e, 0x4a, 0x05, 0x85, 0x4a, 0x60, - 0x74, 0x86, 0x2a, 0x00, 0x90, 0x2a, 0x01, 0x86, - 0x2a, 0x00, 0x81, 0x2a, 0x00, 0x84, 0x2a, 0x04, - 0xbd, 0x1d, 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, - 0x6b, 0x02, 0x8d, 0x6b, 0x01, 0x89, 0x6b, 0x03, - 0x81, 0x6b, 0x60, 0xdf, 0x9e, 0xa1, 0x10, 0xb9, - 0xa6, 0x04, 0x80, 0xa6, 0x61, 0x6f, 0xa9, 0x65, - 0x60, 0x75, 0xaa, 0x6e, 0x03, 0x80, 0x6e, 0x61, - 0x7f, 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, - 0x27, 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x5b, - 0x01, 0x8f, 0x5b, 0x28, 0xcb, 0x01, 0x03, 0x89, - 0x01, 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, - 0x4b, 0xbc, 0x19, 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, 0x19, 0x03, 0xe0, - 0x03, 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, - 0x00, 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, - 0x4d, 0x19, 0x37, 0x99, 0x19, 0x80, 0x38, 0x81, - 0x19, 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, - 0x81, 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, - 0x77, 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, - 0x02, 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, - 0x8b, 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, - 0x03, 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, - 0x19, 0x07, 0x9d, 0x19, 0x01, 0x8b, 0x19, 0x03, - 0x81, 0x19, 0x3d, 0xe0, 0xf3, 0x19, 0x0b, 0x8d, - 0x19, 0x01, 0x8c, 0x19, 0x02, 0x89, 0x19, 0x04, - 0xb7, 0x19, 0x06, 0x8e, 0x19, 0x01, 0x8a, 0x19, - 0x05, 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, - 0xe0, 0x05, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, - 0x32, 0x1f, 0xef, 0xd9, 0x32, 0x05, 0xe0, 0x7d, - 0x32, 0x01, 0xf0, 0x06, 0x21, 0x32, 0x0d, 0xf0, - 0x0c, 0xd0, 0x32, 0x0e, 0xe2, 0x0d, 0x32, 0x69, - 0x41, 0xe1, 0xbd, 0x32, 0x65, 0x81, 0xf0, 0x02, - 0xea, 0x32, 0x04, 0xef, 0xff, 0x32, 0x7a, 0xcb, - 0xf0, 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, - 0xe0, 0x8f, 0x3a, -}; - -static const uint8_t unicode_script_ext_table[1253] = { - 0x80, 0x36, 0x00, 0x00, 0x10, 0x06, 0x13, 0x1a, - 0x23, 0x25, 0x29, 0x2a, 0x2f, 0x2b, 0x2d, 0x32, - 0x4a, 0x51, 0x53, 0x72, 0x86, 0x81, 0x83, 0x00, - 0x00, 0x07, 0x0b, 0x1d, 0x20, 0x4a, 0x4f, 0x9b, - 0xa1, 0x09, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x00, - 0x00, 0x02, 0x02, 0x0d, 0x4a, 0x00, 0x00, 0x00, - 0x02, 0x4a, 0x4f, 0x08, 0x00, 0x00, 0x02, 0x4a, - 0x9b, 0x00, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x25, - 0x00, 0x00, 0x08, 0x17, 0x1a, 0x1d, 0x2d, 0x4a, - 0x72, 0x8e, 0x93, 0x00, 0x08, 0x17, 0x1d, 0x2d, - 0x4a, 0x79, 0x8e, 0x93, 0xa0, 0x00, 0x04, 0x17, - 0x1d, 0x4a, 0x9d, 0x00, 0x05, 0x2a, 0x4a, 0x8e, - 0x90, 0x9b, 0x00, 0x0b, 0x14, 0x17, 0x1a, 0x1d, - 0x2b, 0x2d, 0x4a, 0x79, 0x90, 0x9d, 0xa0, 0x00, - 0x06, 0x1a, 0x25, 0x2a, 0x2b, 0x40, 0x4a, 0x00, - 0x04, 0x1d, 0x2d, 0x4a, 0x72, 0x00, 0x09, 0x1a, - 0x23, 0x37, 0x4a, 0x72, 0x90, 0x93, 0x9d, 0xa0, - 0x00, 0x0a, 0x05, 0x1d, 0x23, 0x2b, 0x2d, 0x37, - 0x4a, 0x72, 0x90, 0x93, 0x00, 0x02, 0x4a, 0x9d, - 0x00, 0x03, 0x23, 0x4a, 0x90, 0x00, 0x04, 0x17, - 0x1d, 0x4a, 0x79, 0x00, 0x03, 0x17, 0x4a, 0x93, - 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x02, 0x27, 0x4a, - 0x00, 0x00, 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x03, - 0x1d, 0x4a, 0xa0, 0x00, 0x00, 0x00, 0x04, 0x2d, - 0x4a, 0x72, 0xa0, 0x0b, 0x00, 0x00, 0x02, 0x4a, - 0x90, 0x01, 0x00, 0x00, 0x05, 0x17, 0x23, 0x40, - 0x4a, 0x90, 0x00, 0x04, 0x17, 0x23, 0x4a, 0x90, - 0x00, 0x02, 0x4a, 0x90, 0x06, 0x00, 0x00, 0x03, - 0x4a, 0x8e, 0x90, 0x00, 0x02, 0x4a, 0x90, 0x00, - 0x00, 0x00, 0x03, 0x17, 0x4a, 0x90, 0x00, 0x06, - 0x14, 0x17, 0x2b, 0x4a, 0x8e, 0x9b, 0x0f, 0x00, - 0x00, 0x01, 0x2d, 0x01, 0x00, 0x00, 0x01, 0x2d, - 0x11, 0x00, 0x00, 0x02, 0x4a, 0x79, 0x04, 0x00, - 0x00, 0x03, 0x14, 0x4a, 0xa0, 0x03, 0x00, 0x0c, - 0x01, 0x4a, 0x03, 0x00, 0x01, 0x02, 0x1a, 0x2d, - 0x80, 0x8c, 0x00, 0x00, 0x02, 0x1d, 0x72, 0x00, - 0x02, 0x1d, 0x2a, 0x01, 0x02, 0x1d, 0x4a, 0x00, - 0x02, 0x1d, 0x2a, 0x80, 0x80, 0x00, 0x00, 0x03, - 0x05, 0x29, 0x2a, 0x80, 0x01, 0x00, 0x00, 0x07, - 0x04, 0x28, 0x69, 0x34, 0x90, 0x9a, 0xa8, 0x0d, - 0x00, 0x00, 0x07, 0x04, 0x28, 0x69, 0x34, 0x90, - 0x9a, 0xa8, 0x00, 0x03, 0x04, 0x90, 0x9a, 0x01, - 0x00, 0x00, 0x08, 0x01, 0x04, 0x28, 0x69, 0x34, - 0x90, 0x9a, 0xa8, 0x1f, 0x00, 0x00, 0x09, 0x01, - 0x04, 0x55, 0x56, 0x77, 0x80, 0x34, 0x8a, 0x90, - 0x09, 0x00, 0x0a, 0x02, 0x04, 0x90, 0x09, 0x00, - 0x09, 0x03, 0x04, 0x9a, 0xa8, 0x05, 0x00, 0x00, - 0x02, 0x04, 0x90, 0x62, 0x00, 0x00, 0x02, 0x04, - 0x34, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x20, - 0x2c, 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x85, - 0x97, 0x99, 0x9e, 0x00, 0x0c, 0x0b, 0x20, 0x2c, - 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x97, 0x99, - 0x9e, 0x10, 0x00, 0x00, 0x15, 0x0b, 0x20, 0x22, - 0x2f, 0x58, 0x2c, 0x2e, 0x30, 0x3f, 0x53, 0x54, - 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, 0x97, - 0x99, 0x9e, 0x00, 0x17, 0x0b, 0x20, 0x22, 0x2f, - 0x58, 0x2c, 0x2e, 0x31, 0x30, 0x3f, 0x4c, 0x53, - 0x54, 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, - 0x97, 0x99, 0x9e, 0x09, 0x04, 0x20, 0x22, 0x3e, - 0x53, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x8f, - 0x75, 0x00, 0x09, 0x02, 0x30, 0x62, 0x75, 0x00, - 0x09, 0x02, 0x2e, 0x45, 0x80, 0x75, 0x00, 0x0d, - 0x02, 0x2c, 0x97, 0x80, 0x71, 0x00, 0x09, 0x03, - 0x3f, 0x66, 0xa2, 0x82, 0xcf, 0x00, 0x09, 0x03, - 0x15, 0x63, 0x93, 0x80, 0x30, 0x00, 0x00, 0x03, - 0x29, 0x2a, 0x4a, 0x85, 0x6e, 0x00, 0x02, 0x01, - 0x82, 0x46, 0x00, 0x01, 0x04, 0x11, 0x35, 0x92, - 0x91, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x60, 0x7e, - 0x00, 0x00, 0x00, 0x02, 0x60, 0x7e, 0x84, 0x49, - 0x00, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, 0x00, - 0x01, 0x20, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, - 0x00, 0x03, 0x20, 0x2c, 0x3f, 0x00, 0x01, 0x20, - 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, - 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, - 0x00, 0x06, 0x20, 0x3f, 0x54, 0x78, 0x97, 0x99, - 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, 0x85, 0x01, - 0x01, 0x20, 0x00, 0x02, 0x20, 0x85, 0x00, 0x02, - 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, 0x02, 0x20, - 0x66, 0x00, 0x02, 0x0b, 0x20, 0x01, 0x01, 0x20, - 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, 0x20, 0x00, - 0x0b, 0x0b, 0x20, 0x2c, 0x3f, 0x54, 0x66, 0x78, - 0x89, 0x99, 0x9e, 0xa2, 0x00, 0x02, 0x20, 0x2c, - 0x00, 0x04, 0x20, 0x2c, 0x3f, 0xa2, 0x01, 0x02, - 0x0b, 0x20, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, - 0x2c, 0x00, 0x01, 0x66, 0x80, 0x44, 0x00, 0x01, - 0x01, 0x2d, 0x35, 0x00, 0x00, 0x03, 0x1d, 0x4a, - 0x90, 0x00, 0x00, 0x00, 0x01, 0x90, 0x81, 0xb3, - 0x00, 0x00, 0x03, 0x4a, 0x60, 0x7e, 0x1e, 0x00, - 0x00, 0x02, 0x01, 0x04, 0x09, 0x00, 0x00, 0x06, - 0x13, 0x29, 0x2a, 0x6f, 0x50, 0x76, 0x01, 0x00, - 0x00, 0x04, 0x13, 0x2d, 0x6f, 0x5d, 0x80, 0x11, - 0x00, 0x00, 0x03, 0x20, 0x2c, 0x4a, 0x8c, 0xa5, - 0x00, 0x00, 0x02, 0x1a, 0x4a, 0x17, 0x00, 0x00, - 0x02, 0x06, 0x76, 0x00, 0x07, 0x06, 0x13, 0x29, - 0x6f, 0x3e, 0x51, 0x83, 0x09, 0x00, 0x00, 0x01, - 0x23, 0x03, 0x00, 0x00, 0x03, 0x01, 0x04, 0x6f, - 0x00, 0x00, 0x00, 0x02, 0x1d, 0x2a, 0x81, 0x2b, - 0x00, 0x0f, 0x02, 0x32, 0x98, 0x00, 0x00, 0x00, - 0x07, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, 0xa9, - 0x00, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, - 0x7e, 0xa9, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, - 0x40, 0x01, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, - 0x01, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, - 0x9c, 0xa9, 0x01, 0x09, 0x0d, 0x33, 0x32, 0x38, - 0x40, 0x4f, 0x60, 0x9c, 0xa9, 0x05, 0x06, 0x0d, - 0x33, 0x32, 0x38, 0x40, 0xa9, 0x00, 0x00, 0x00, - 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x07, 0x06, - 0x0d, 0x33, 0x32, 0x38, 0x40, 0xa9, 0x03, 0x05, - 0x0d, 0x33, 0x32, 0x38, 0x40, 0x09, 0x00, 0x03, - 0x02, 0x0d, 0x32, 0x01, 0x00, 0x00, 0x05, 0x0d, - 0x33, 0x32, 0x38, 0x40, 0x04, 0x02, 0x38, 0x40, - 0x00, 0x00, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, - 0x40, 0x03, 0x00, 0x01, 0x03, 0x32, 0x38, 0x40, - 0x01, 0x01, 0x32, 0x58, 0x00, 0x03, 0x02, 0x38, - 0x40, 0x02, 0x00, 0x00, 0x02, 0x38, 0x40, 0x59, - 0x00, 0x00, 0x06, 0x0d, 0x33, 0x32, 0x38, 0x40, - 0xa9, 0x00, 0x02, 0x38, 0x40, 0x80, 0x12, 0x00, - 0x0f, 0x01, 0x32, 0x1f, 0x00, 0x25, 0x01, 0x32, - 0x08, 0x00, 0x00, 0x02, 0x32, 0x98, 0x2f, 0x00, - 0x27, 0x01, 0x32, 0x37, 0x00, 0x30, 0x01, 0x32, - 0x0e, 0x00, 0x0b, 0x01, 0x32, 0x32, 0x00, 0x00, - 0x01, 0x32, 0x57, 0x00, 0x18, 0x01, 0x32, 0x09, - 0x00, 0x04, 0x01, 0x32, 0x5f, 0x00, 0x1e, 0x01, - 0x32, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, - 0x2a, 0x80, 0x0f, 0x00, 0x07, 0x02, 0x32, 0x4a, - 0x80, 0xa7, 0x00, 0x02, 0x10, 0x20, 0x22, 0x2e, - 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x54, 0x5f, 0x66, - 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x02, 0x0f, 0x20, - 0x22, 0x2e, 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x5f, - 0x66, 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x01, 0x0b, - 0x20, 0x22, 0x2e, 0x30, 0x45, 0x3e, 0x53, 0x5f, - 0x47, 0x96, 0x9e, 0x00, 0x0c, 0x20, 0x22, 0x2e, - 0x30, 0x45, 0x3e, 0x53, 0x5f, 0x85, 0x47, 0x96, - 0x9e, 0x00, 0x0b, 0x20, 0x22, 0x2e, 0x30, 0x45, - 0x3e, 0x53, 0x5f, 0x47, 0x96, 0x9e, 0x80, 0x36, - 0x00, 0x00, 0x03, 0x0b, 0x20, 0xa2, 0x00, 0x00, - 0x00, 0x02, 0x20, 0x97, 0x39, 0x00, 0x00, 0x03, - 0x42, 0x4a, 0x63, 0x80, 0x1f, 0x00, 0x00, 0x02, - 0x10, 0x3d, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, - 0x04, 0x69, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, - 0x9a, 0x09, 0x00, 0x00, 0x02, 0x04, 0x9a, 0x46, - 0x00, 0x01, 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, - 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x33, 0x32, - 0x38, 0x40, 0xa9, 0x09, 0x00, 0x00, 0x02, 0x38, - 0x40, 0x2c, 0x00, 0x01, 0x02, 0x38, 0x40, 0x80, - 0xdf, 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4e, 0x00, - 0x02, 0x1c, 0x4e, 0x03, 0x00, 0x2c, 0x03, 0x1c, - 0x4d, 0x4e, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4e, - 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, - 0x75, 0x00, 0x00, 0x02, 0x56, 0x77, 0x87, 0x8d, - 0x00, 0x00, 0x02, 0x2c, 0x97, 0x00, 0x00, 0x00, - 0x02, 0x2c, 0x97, 0x36, 0x00, 0x01, 0x02, 0x2c, - 0x97, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2c, 0x97, - 0x00, 0x00, 0x00, 0x02, 0x2c, 0x97, 0xc0, 0x5c, - 0x4b, 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, - 0x11, 0x01, 0x32, 0x9e, 0x5d, 0x00, 0x01, 0x01, - 0x32, 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[443] = { - 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, 0xce, - 0x80, 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, 0x41, 0x96, 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, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, 0x85, - 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Other_Lowercase_table[69] = { - 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, - 0x40, 0x80, 0x12, 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[450] = { - 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, 0x40, 0x80, - 0x12, 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_Basic_Emoji1_table[143] = { - 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, 0xe5, 0x80, 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, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, 0x40, - 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, - 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, - 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, -}; - -static const uint8_t unicode_prop_Basic_Emoji2_table[183] = { - 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, - 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, - 0x8e, 0x81, 0x41, 0x7c, 0x80, 0x40, 0xa5, 0x80, - 0x9c, 0x10, 0x0c, 0x82, 0x40, 0xc6, 0x80, 0x40, - 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, 0xb9, 0x0a, - 0x84, 0x88, 0x01, 0x05, 0x03, 0x01, 0x00, 0x09, - 0x02, 0x02, 0x0f, 0x14, 0x00, 0x80, 0x9b, 0x09, - 0x00, 0x08, 0x80, 0x91, 0x01, 0x80, 0x92, 0x00, - 0x18, 0x00, 0x0a, 0x05, 0x07, 0x81, 0x95, 0x05, - 0x00, 0x00, 0x80, 0x94, 0x05, 0x09, 0x01, 0x17, - 0x04, 0x09, 0x08, 0x01, 0x00, 0x00, 0x05, 0x02, - 0x80, 0x90, 0x81, 0x8e, 0x01, 0x80, 0x9a, 0x81, - 0xbb, 0x80, 0x41, 0x91, 0x81, 0x41, 0xce, 0x82, - 0x45, 0x27, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, - 0x80, 0x61, 0xbe, 0xd5, 0x81, 0x8b, 0x81, 0x40, - 0x81, 0x80, 0xb3, 0x80, 0x40, 0xe8, 0x01, 0x88, - 0x88, 0x80, 0xc5, 0x80, 0x97, 0x08, 0x11, 0x81, - 0xaa, 0x1c, 0x8b, 0x92, 0x00, 0x00, 0x80, 0xc6, - 0x00, 0x80, 0x40, 0xba, 0x80, 0xca, 0x81, 0xa3, - 0x09, 0x86, 0x8c, 0x01, 0x19, 0x80, 0x93, 0x01, - 0x07, 0x81, 0x88, 0x04, 0x82, 0x8b, 0x17, 0x11, - 0x00, 0x03, 0x05, 0x02, 0x05, 0x80, 0x40, 0xcf, - 0x00, 0x82, 0x8f, 0x2a, 0x05, 0x01, 0x80, -}; - -static const uint8_t unicode_prop_RGI_Emoji_Modifier_Sequence_table[73] = { - 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, - 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, - 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x18, 0x18, 0x88, - 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, 0x11, 0xb7, 0x80, 0xbc, 0x08, 0x08, - 0x80, 0x90, 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, - 0x88, -}; - -static const uint8_t unicode_prop_RGI_Emoji_Flag_Sequence_table[128] = { - 0x0c, 0x00, 0x09, 0x00, 0x04, 0x01, 0x02, 0x06, - 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x07, 0x0d, - 0x18, 0x00, 0x09, 0x00, 0x00, 0x89, 0x08, 0x00, - 0x00, 0x81, 0x88, 0x83, 0x8c, 0x10, 0x00, 0x01, - 0x07, 0x08, 0x29, 0x10, 0x28, 0x00, 0x80, 0x8a, - 0x00, 0x0a, 0x00, 0x0e, 0x15, 0x18, 0x83, 0x89, - 0x06, 0x00, 0x81, 0x8d, 0x00, 0x12, 0x08, 0x00, - 0x03, 0x00, 0x24, 0x00, 0x05, 0x21, 0x00, 0x00, - 0x29, 0x90, 0x00, 0x02, 0x00, 0x08, 0x09, 0x00, - 0x08, 0x18, 0x8b, 0x80, 0x8c, 0x02, 0x19, 0x1a, - 0x11, 0x00, 0x00, 0x80, 0x9c, 0x80, 0x88, 0x02, - 0x00, 0x00, 0x02, 0x20, 0x88, 0x0a, 0x00, 0x03, - 0x01, 0x02, 0x05, 0x08, 0x00, 0x01, 0x09, 0x20, - 0x21, 0x18, 0x22, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x28, 0x89, 0x80, 0x8b, 0x80, 0x90, 0x80, 0x92, - 0x80, 0x8d, 0x05, 0x80, 0x8a, 0x80, 0x88, 0x80, -}; - -static const uint8_t unicode_prop_Emoji_Keycap_Sequence_table[4] = { - 0xa2, 0x05, 0x04, 0x89, -}; - -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[438] = { - 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, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, - 0x40, 0x85, 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, 0xe7, 0x80, 0x8e, 0x80, 0xa5, - 0x88, 0xb5, 0x81, 0xb9, 0x80, 0x8a, 0x81, 0xc1, - 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, - 0xb1, 0xbe, 0xd8, 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, 0xec, 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, 0x8e, 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, 0x41, 0xa8, - 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[111] = { - 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, 0x60, 0x50, 0xa8, - 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, - 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[72] = { - 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, 0x9a, 0x57, 0xf7, 0x87, 0x44, - 0xd5, 0xa8, 0x89, 0x60, 0x24, 0x66, 0x41, 0x8b, - 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50, - 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, - 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x49, 0xa1, 0x42, - 0x1d, 0x45, 0xe1, 0x53, 0x4a, 0x84, 0x50, 0x5f, -}; - -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[48] = { - 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, 0x50, 0x39, 0x85, 0x40, 0xdd, - 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, 0x42, - 0x6d, 0x51, 0xa1, 0x53, 0x4a, 0x84, 0x50, 0x5f, -}; - -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_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_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[238] = { - 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, 0x0a, 0x83, - 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, - 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, - 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, 0xb7, - 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, -}; - -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[144] = { - 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, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, - 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, - 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, - 0x84, 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, -}; - -static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { - 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, - 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, - 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, - 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, - 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, - 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, - 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, - 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, - 0x8a, 0x84, 0xac, 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, - 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, - 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, - 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, - 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, - 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, - 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, - 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, - 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_Basic_Emoji1, - UNICODE_PROP_Basic_Emoji2, - UNICODE_PROP_RGI_Emoji_Modifier_Sequence, - UNICODE_PROP_RGI_Emoji_Flag_Sequence, - UNICODE_PROP_Emoji_Keycap_Sequence, - 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_InCB, - UNICODE_PROP_Lowercase, - UNICODE_PROP_Math, - UNICODE_PROP_Uppercase, - UNICODE_PROP_XID_Continue, - UNICODE_PROP_XID_Start, - UNICODE_PROP_Cased1, - 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" - "InCB" "\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_Basic_Emoji1_table, - unicode_prop_Basic_Emoji2_table, - unicode_prop_RGI_Emoji_Modifier_Sequence_table, - unicode_prop_RGI_Emoji_Flag_Sequence_table, - unicode_prop_Emoji_Keycap_Sequence_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_Basic_Emoji1_table), - countof(unicode_prop_Basic_Emoji2_table), - countof(unicode_prop_RGI_Emoji_Modifier_Sequence_table), - countof(unicode_prop_RGI_Emoji_Flag_Sequence_table), - countof(unicode_prop_Emoji_Keycap_Sequence_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), -}; - -typedef enum { - UNICODE_SEQUENCE_PROP_Basic_Emoji, - UNICODE_SEQUENCE_PROP_Emoji_Keycap_Sequence, - UNICODE_SEQUENCE_PROP_RGI_Emoji_Modifier_Sequence, - UNICODE_SEQUENCE_PROP_RGI_Emoji_Flag_Sequence, - UNICODE_SEQUENCE_PROP_RGI_Emoji_Tag_Sequence, - UNICODE_SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence, - UNICODE_SEQUENCE_PROP_RGI_Emoji, - UNICODE_SEQUENCE_PROP_COUNT, -} UnicodeSequencePropertyEnum; - -static const char unicode_sequence_prop_name_table[] = - "Basic_Emoji" "\0" - "Emoji_Keycap_Sequence" "\0" - "RGI_Emoji_Modifier_Sequence" "\0" - "RGI_Emoji_Flag_Sequence" "\0" - "RGI_Emoji_Tag_Sequence" "\0" - "RGI_Emoji_ZWJ_Sequence" "\0" - "RGI_Emoji" "\0" -; - -static const uint8_t unicode_rgi_emoji_tag_sequence[18] = { - 0x67, 0x62, 0x65, 0x6e, 0x67, 0x00, 0x67, 0x62, - 0x73, 0x63, 0x74, 0x00, 0x67, 0x62, 0x77, 0x6c, - 0x73, 0x00, -}; - -static const uint8_t unicode_rgi_emoji_zwj_sequence[2320] = { - 0x02, 0xb8, 0x19, 0x40, 0x86, 0x02, 0xd1, 0x39, - 0xb0, 0x19, 0x02, 0x26, 0x39, 0x42, 0x86, 0x02, - 0xb4, 0x36, 0x42, 0x86, 0x03, 0x68, 0x54, 0x64, - 0x87, 0x68, 0x54, 0x02, 0xdc, 0x39, 0x42, 0x86, - 0x02, 0xd1, 0x39, 0x73, 0x13, 0x02, 0x39, 0x39, - 0x40, 0x86, 0x02, 0x69, 0x34, 0xbd, 0x19, 0x03, - 0xb6, 0x36, 0x40, 0x86, 0xa1, 0x87, 0x03, 0x68, - 0x74, 0x1d, 0x19, 0x68, 0x74, 0x03, 0x68, 0x34, - 0xbd, 0x19, 0xa1, 0x87, 0x02, 0xf1, 0x7a, 0xf2, - 0x7a, 0x02, 0xca, 0x33, 0x42, 0x86, 0x02, 0x69, - 0x34, 0xb0, 0x19, 0x04, 0x68, 0x14, 0x68, 0x14, - 0x67, 0x14, 0x66, 0x14, 0x02, 0xf9, 0x26, 0x42, - 0x86, 0x03, 0x69, 0x74, 0x1d, 0x19, 0x69, 0x74, - 0x03, 0xd1, 0x19, 0xbc, 0x19, 0xa1, 0x87, 0x02, - 0x3c, 0x19, 0x40, 0x86, 0x02, 0x68, 0x34, 0xeb, - 0x13, 0x02, 0xc3, 0x33, 0xa1, 0x87, 0x02, 0x70, - 0x34, 0x40, 0x86, 0x02, 0xd4, 0x39, 0x42, 0x86, - 0x02, 0xcf, 0x39, 0x42, 0x86, 0x02, 0x47, 0x36, - 0x40, 0x86, 0x02, 0x39, 0x39, 0x42, 0x86, 0x04, - 0xd1, 0x79, 0x64, 0x87, 0x8b, 0x14, 0xd1, 0x79, - 0x02, 0xd1, 0x39, 0x95, 0x86, 0x02, 0x68, 0x34, - 0x93, 0x13, 0x02, 0x69, 0x34, 0xed, 0x13, 0x02, - 0xda, 0x39, 0x40, 0x86, 0x03, 0x69, 0x34, 0xaf, - 0x19, 0xa1, 0x87, 0x02, 0xd1, 0x39, 0x93, 0x13, - 0x03, 0xce, 0x39, 0x42, 0x86, 0xa1, 0x87, 0x03, - 0xd1, 0x79, 0x64, 0x87, 0xd1, 0x79, 0x03, 0xc3, - 0x33, 0x42, 0x86, 0xa1, 0x87, 0x03, 0x69, 0x74, - 0x1d, 0x19, 0x68, 0x74, 0x02, 0x69, 0x34, 0x92, - 0x16, 0x02, 0xd1, 0x39, 0x96, 0x86, 0x04, 0x69, - 0x14, 0x64, 0x87, 0x8b, 0x14, 0x68, 0x14, 0x02, - 0x68, 0x34, 0x7c, 0x13, 0x02, 0x47, 0x36, 0x42, - 0x86, 0x02, 0x86, 0x34, 0x42, 0x86, 0x02, 0xd1, - 0x39, 0x7c, 0x13, 0x02, 0x69, 0x14, 0xa4, 0x13, - 0x02, 0xda, 0x39, 0x42, 0x86, 0x02, 0x37, 0x39, - 0x40, 0x86, 0x02, 0xd1, 0x39, 0x08, 0x87, 0x04, - 0x68, 0x54, 0x64, 0x87, 0x8b, 0x14, 0x68, 0x54, - 0x02, 0x4d, 0x36, 0x40, 0x86, 0x02, 0x68, 0x34, - 0x2c, 0x15, 0x02, 0x69, 0x34, 0xaf, 0x19, 0x02, - 0x6e, 0x34, 0x40, 0x86, 0x02, 0xcd, 0x39, 0x42, - 0x86, 0x02, 0xd1, 0x39, 0x2c, 0x15, 0x02, 0x6f, - 0x14, 0x40, 0x86, 0x03, 0xd1, 0x39, 0xbc, 0x19, - 0xa1, 0x87, 0x02, 0x68, 0x34, 0xa8, 0x13, 0x02, - 0x69, 0x34, 0x73, 0x13, 0x04, 0x69, 0x54, 0x64, - 0x87, 0x8b, 0x14, 0x68, 0x54, 0x02, 0x71, 0x34, - 0x42, 0x86, 0x02, 0xd1, 0x39, 0xa8, 0x13, 0x02, - 0x45, 0x36, 0x40, 0x86, 0x03, 0x69, 0x54, 0x64, - 0x87, 0x68, 0x54, 0x03, 0x69, 0x54, 0x64, 0x87, - 0x69, 0x54, 0x03, 0xce, 0x39, 0x40, 0x86, 0xa1, - 0x87, 0x02, 0xd8, 0x39, 0x40, 0x86, 0x03, 0xc3, - 0x33, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x4d, 0x36, - 0x42, 0x86, 0x02, 0xd1, 0x19, 0x92, 0x16, 0x02, - 0xd1, 0x39, 0xeb, 0x13, 0x02, 0x68, 0x34, 0xbc, - 0x14, 0x02, 0xd1, 0x39, 0xbc, 0x14, 0x02, 0x3d, - 0x39, 0x40, 0x86, 0x02, 0xb8, 0x39, 0x42, 0x86, - 0x02, 0xa3, 0x36, 0x40, 0x86, 0x02, 0x75, 0x35, - 0x40, 0x86, 0x02, 0xd8, 0x39, 0x42, 0x86, 0x02, - 0x69, 0x34, 0x93, 0x13, 0x02, 0x35, 0x39, 0x40, - 0x86, 0x02, 0x4b, 0x36, 0x40, 0x86, 0x02, 0x3d, - 0x39, 0x42, 0x86, 0x02, 0x38, 0x39, 0x42, 0x86, - 0x02, 0xa3, 0x36, 0x42, 0x86, 0x03, 0x69, 0x14, - 0x67, 0x14, 0x67, 0x14, 0x02, 0xb6, 0x36, 0x40, - 0x86, 0x02, 0x69, 0x34, 0x7c, 0x13, 0x02, 0x75, - 0x35, 0x42, 0x86, 0x02, 0xcc, 0x93, 0x40, 0x86, - 0x02, 0xcc, 0x33, 0x40, 0x86, 0x03, 0xd1, 0x39, - 0xbd, 0x19, 0xa1, 0x87, 0x02, 0x82, 0x34, 0x40, - 0x86, 0x02, 0x87, 0x34, 0x40, 0x86, 0x02, 0x69, - 0x14, 0x3e, 0x13, 0x02, 0xd6, 0x39, 0x40, 0x86, - 0x02, 0x68, 0x14, 0xbd, 0x19, 0x02, 0x46, 0x36, - 0x42, 0x86, 0x02, 0x4b, 0x36, 0x42, 0x86, 0x02, - 0x69, 0x34, 0x2c, 0x15, 0x03, 0xb6, 0x36, 0x42, - 0x86, 0xa1, 0x87, 0x02, 0xc4, 0x33, 0x40, 0x86, - 0x02, 0x26, 0x19, 0x40, 0x86, 0x02, 0x69, 0x14, - 0xb0, 0x19, 0x02, 0xde, 0x19, 0x42, 0x86, 0x02, - 0x69, 0x34, 0xa8, 0x13, 0x02, 0xcc, 0x33, 0x42, - 0x86, 0x02, 0x82, 0x34, 0x42, 0x86, 0x02, 0xd1, - 0x19, 0x93, 0x13, 0x02, 0x81, 0x14, 0x42, 0x86, - 0x02, 0x69, 0x34, 0x95, 0x86, 0x02, 0x68, 0x34, - 0xbb, 0x14, 0x02, 0xd1, 0x39, 0xbb, 0x14, 0x02, - 0x69, 0x34, 0xeb, 0x13, 0x02, 0xd1, 0x39, 0x84, - 0x13, 0x02, 0x69, 0x34, 0xbc, 0x14, 0x04, 0x69, - 0x54, 0x64, 0x87, 0x8b, 0x14, 0x69, 0x54, 0x02, - 0x26, 0x39, 0x40, 0x86, 0x02, 0xb4, 0x36, 0x40, - 0x86, 0x02, 0x47, 0x16, 0x42, 0x86, 0x02, 0xdc, - 0x39, 0x40, 0x86, 0x02, 0xca, 0x33, 0x40, 0x86, - 0x02, 0xf9, 0x26, 0x40, 0x86, 0x02, 0x69, 0x34, - 0x08, 0x87, 0x03, 0x69, 0x14, 0x69, 0x14, 0x66, - 0x14, 0x03, 0xd1, 0x59, 0x1d, 0x19, 0xd1, 0x59, - 0x02, 0xd4, 0x39, 0x40, 0x86, 0x02, 0xcf, 0x39, - 0x40, 0x86, 0x02, 0x68, 0x34, 0xa4, 0x13, 0x02, - 0xd1, 0x39, 0xa4, 0x13, 0x02, 0xd1, 0x19, 0xa8, - 0x13, 0x02, 0xd7, 0x39, 0x42, 0x86, 0x03, 0x69, - 0x34, 0xbc, 0x19, 0xa1, 0x87, 0x02, 0x68, 0x14, - 0xb0, 0x19, 0x02, 0x68, 0x14, 0x73, 0x13, 0x04, - 0x69, 0x14, 0x69, 0x14, 0x66, 0x14, 0x66, 0x14, - 0x03, 0x68, 0x34, 0xaf, 0x19, 0xa1, 0x87, 0x02, - 0x68, 0x34, 0x80, 0x16, 0x02, 0x73, 0x34, 0x42, - 0x86, 0x02, 0xd1, 0x39, 0x80, 0x16, 0x02, 0x68, - 0x34, 0xb0, 0x19, 0x02, 0x86, 0x34, 0x40, 0x86, - 0x02, 0x38, 0x19, 0x42, 0x86, 0x02, 0x69, 0x34, - 0xbb, 0x14, 0x02, 0xb5, 0x36, 0x42, 0x86, 0x02, - 0xcd, 0x39, 0x40, 0x86, 0x02, 0x68, 0x34, 0x95, - 0x86, 0x02, 0x68, 0x34, 0x27, 0x15, 0x03, 0x68, - 0x14, 0x68, 0x14, 0x66, 0x14, 0x02, 0x71, 0x34, - 0x40, 0x86, 0x02, 0xd1, 0x39, 0x27, 0x15, 0x02, - 0x2e, 0x16, 0xa8, 0x14, 0x02, 0xc3, 0x33, 0x42, - 0x86, 0x02, 0x69, 0x14, 0x66, 0x14, 0x02, 0x68, - 0x34, 0x96, 0x86, 0x02, 0x69, 0x34, 0xa4, 0x13, - 0x03, 0x69, 0x14, 0x64, 0x87, 0x68, 0x14, 0x02, - 0xb8, 0x39, 0x40, 0x86, 0x02, 0x68, 0x34, 0x3e, - 0x13, 0x03, 0xd1, 0x19, 0xaf, 0x19, 0xa1, 0x87, - 0x02, 0xd1, 0x39, 0x3e, 0x13, 0x02, 0x68, 0x34, - 0xbd, 0x19, 0x02, 0xd1, 0x19, 0xbb, 0x14, 0x02, - 0xd1, 0x19, 0x95, 0x86, 0x02, 0xdb, 0x39, 0x42, - 0x86, 0x02, 0x38, 0x39, 0x40, 0x86, 0x02, 0x69, - 0x34, 0x80, 0x16, 0x02, 0x69, 0x14, 0xeb, 0x13, - 0x04, 0x68, 0x14, 0x69, 0x14, 0x67, 0x14, 0x67, - 0x14, 0x02, 0x77, 0x34, 0x42, 0x86, 0x02, 0x46, - 0x36, 0x40, 0x86, 0x02, 0x68, 0x34, 0x92, 0x16, - 0x02, 0x4e, 0x36, 0x42, 0x86, 0x03, 0x69, 0x14, - 0xbd, 0x19, 0xa1, 0x87, 0x02, 0xde, 0x19, 0x40, - 0x86, 0x02, 0x69, 0x34, 0x27, 0x15, 0x03, 0xc3, - 0x13, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x81, 0x14, - 0x40, 0x86, 0x03, 0xd1, 0x39, 0xaf, 0x19, 0xa1, - 0x87, 0x02, 0x68, 0x34, 0xbc, 0x19, 0x02, 0xd1, - 0x19, 0x80, 0x16, 0x02, 0xd9, 0x39, 0x42, 0x86, - 0x02, 0xd1, 0x39, 0xbc, 0x19, 0x02, 0xdc, 0x19, - 0x42, 0x86, 0x02, 0x68, 0x34, 0x73, 0x13, 0x02, - 0x69, 0x34, 0x3e, 0x13, 0x02, 0x47, 0x16, 0x40, - 0x86, 0x02, 0xd1, 0x39, 0xbd, 0x19, 0x02, 0x3e, - 0x39, 0x42, 0x86, 0x02, 0x69, 0x14, 0x95, 0x86, - 0x02, 0x68, 0x14, 0x96, 0x86, 0x03, 0x69, 0x34, - 0xbd, 0x19, 0xa1, 0x87, 0x02, 0xd7, 0x39, 0x40, - 0x86, 0x02, 0x45, 0x16, 0x42, 0x86, 0x02, 0x68, - 0x34, 0xed, 0x13, 0x03, 0x68, 0x34, 0xbc, 0x19, - 0xa1, 0x87, 0x02, 0xd1, 0x39, 0xed, 0x13, 0x02, - 0xd1, 0x39, 0x92, 0x16, 0x02, 0x73, 0x34, 0x40, - 0x86, 0x02, 0x38, 0x19, 0x40, 0x86, 0x02, 0xb5, - 0x36, 0x40, 0x86, 0x02, 0x68, 0x34, 0xaf, 0x19, - 0x02, 0xd1, 0x39, 0xaf, 0x19, 0x02, 0x69, 0x34, - 0xbc, 0x19, 0x02, 0xb6, 0x16, 0x42, 0x86, 0x02, - 0x26, 0x14, 0x25, 0x15, 0x02, 0xc3, 0x33, 0x40, - 0x86, 0x02, 0xdd, 0x39, 0x42, 0x86, 0x02, 0xcb, - 0x93, 0x42, 0x86, 0x02, 0xcb, 0x33, 0x42, 0x86, - 0x02, 0x81, 0x34, 0x42, 0x86, 0x02, 0xce, 0x39, - 0xa1, 0x87, 0x02, 0xdb, 0x39, 0x40, 0x86, 0x02, - 0x68, 0x34, 0x08, 0x87, 0x02, 0xd1, 0x19, 0xb0, - 0x19, 0x02, 0x77, 0x34, 0x40, 0x86, 0x02, 0x4e, - 0x36, 0x40, 0x86, 0x02, 0xce, 0x39, 0x42, 0x86, - 0x02, 0x4e, 0x16, 0x42, 0x86, 0x02, 0xd9, 0x39, - 0x40, 0x86, 0x02, 0xdc, 0x19, 0x40, 0x86, 0x02, - 0x3e, 0x39, 0x40, 0x86, 0x02, 0xb9, 0x39, 0x42, - 0x86, 0x02, 0xda, 0x19, 0x42, 0x86, 0x02, 0x42, - 0x16, 0x94, 0x81, 0x02, 0x45, 0x16, 0x40, 0x86, - 0x02, 0x69, 0x14, 0xbd, 0x19, 0x02, 0x70, 0x34, - 0x42, 0x86, 0x02, 0xce, 0x19, 0xa1, 0x87, 0x02, - 0xc3, 0x13, 0x42, 0x86, 0x02, 0x68, 0x14, 0x08, - 0x87, 0x02, 0xd1, 0x19, 0x7c, 0x13, 0x02, 0x68, - 0x14, 0x92, 0x16, 0x02, 0xb6, 0x16, 0x40, 0x86, - 0x02, 0x37, 0x39, 0x42, 0x86, 0x03, 0xce, 0x19, - 0x42, 0x86, 0xa1, 0x87, 0x03, 0x68, 0x14, 0x67, - 0x14, 0x67, 0x14, 0x02, 0xdd, 0x39, 0x40, 0x86, - 0x02, 0xcf, 0x19, 0x42, 0x86, 0x02, 0xd1, 0x19, - 0x2c, 0x15, 0x02, 0x4b, 0x13, 0xe9, 0x17, 0x02, - 0x68, 0x14, 0x67, 0x14, 0x02, 0xcb, 0x93, 0x40, - 0x86, 0x02, 0x6e, 0x34, 0x42, 0x86, 0x02, 0xcb, - 0x33, 0x40, 0x86, 0x02, 0x81, 0x34, 0x40, 0x86, - 0x02, 0xb6, 0x36, 0xa1, 0x87, 0x02, 0x45, 0x36, - 0x42, 0x86, 0x02, 0xb4, 0x16, 0x42, 0x86, 0x02, - 0x69, 0x14, 0x73, 0x13, 0x04, 0x69, 0x14, 0x69, - 0x14, 0x67, 0x14, 0x66, 0x14, 0x02, 0x35, 0x39, - 0x42, 0x86, 0x02, 0x68, 0x14, 0x93, 0x13, 0x02, - 0xb6, 0x36, 0x42, 0x86, 0x03, 0x68, 0x14, 0x69, - 0x14, 0x66, 0x14, 0x02, 0xce, 0x39, 0x40, 0x86, - 0x02, 0x4e, 0x16, 0x40, 0x86, 0x02, 0x87, 0x34, - 0x42, 0x86, 0x02, 0x86, 0x14, 0x42, 0x86, 0x02, - 0xd6, 0x39, 0x42, 0x86, 0x02, 0xc4, 0x33, 0x42, - 0x86, 0x02, 0x69, 0x34, 0x96, 0x86, 0x02, 0xb9, - 0x39, 0x40, 0x86, 0x02, 0x68, 0x14, 0xa8, 0x13, - 0x02, 0xd1, 0x19, 0x84, 0x13, 0x02, 0xda, 0x19, - 0x40, 0x86, 0x02, 0xd8, 0x19, 0x42, 0x86, 0x02, - 0xc3, 0x13, 0x40, 0x86, 0x02, 0xb9, 0x19, 0x42, - 0x86, 0x02, 0x3d, 0x19, 0x42, 0x86, 0x02, 0xcf, - 0x19, 0x40, 0x86, 0x04, 0x68, 0x14, 0x68, 0x14, - 0x67, 0x14, 0x67, 0x14, 0x03, 0xd1, 0x19, 0xd1, - 0x19, 0xd2, 0x19, 0x02, 0x68, 0x14, 0xbb, 0x14, - 0x02, 0x3b, 0x14, 0x44, 0x87, 0x02, 0xd1, 0x19, - 0x27, 0x15, 0x02, 0xb4, 0x16, 0x40, 0x86, 0x02, - 0xcd, 0x19, 0x42, 0x86, 0x02, 0xd3, 0x86, 0xa5, - 0x14, 0x02, 0x70, 0x14, 0x42, 0x86, 0x03, 0xb6, - 0x16, 0x42, 0x86, 0xa1, 0x87, 0x04, 0x69, 0x14, - 0x64, 0x87, 0x8b, 0x14, 0x69, 0x14, 0x02, 0x36, - 0x16, 0x2b, 0x93, 0x02, 0x68, 0x14, 0x80, 0x16, - 0x02, 0x86, 0x14, 0x40, 0x86, 0x02, 0x08, 0x14, - 0x1b, 0x0b, 0x02, 0xd1, 0x19, 0xbc, 0x19, 0x02, - 0xca, 0x13, 0x42, 0x86, 0x02, 0x41, 0x94, 0xe8, - 0x95, 0x02, 0xd8, 0x19, 0x40, 0x86, 0x02, 0xb9, - 0x19, 0x40, 0x86, 0x02, 0xd1, 0x19, 0xed, 0x13, - 0x02, 0xf9, 0x86, 0x42, 0x86, 0x03, 0xd1, 0x19, - 0xbd, 0x19, 0xa1, 0x87, 0x02, 0x3d, 0x19, 0x40, - 0x86, 0x02, 0xd6, 0x19, 0x42, 0x86, 0x03, 0x69, - 0x14, 0x66, 0x14, 0x66, 0x14, 0x02, 0xd1, 0x19, - 0xaf, 0x19, 0x03, 0x69, 0x14, 0x69, 0x14, 0x67, - 0x14, 0x02, 0xcd, 0x19, 0x40, 0x86, 0x02, 0x70, - 0x14, 0x40, 0x86, 0x03, 0x68, 0x14, 0xbc, 0x19, - 0xa1, 0x87, 0x02, 0x6e, 0x14, 0x42, 0x86, 0x02, - 0x69, 0x14, 0x92, 0x16, 0x03, 0x68, 0x14, 0x68, - 0x14, 0x67, 0x14, 0x02, 0x69, 0x14, 0x67, 0x14, - 0x02, 0x75, 0x95, 0x42, 0x86, 0x03, 0x69, 0x14, - 0x64, 0x87, 0x69, 0x14, 0x02, 0xd1, 0x19, 0xbc, - 0x14, 0x02, 0xdf, 0x19, 0x42, 0x86, 0x02, 0xca, - 0x13, 0x40, 0x86, 0x02, 0x82, 0x14, 0x42, 0x86, - 0x02, 0x69, 0x14, 0x93, 0x13, 0x02, 0x68, 0x14, - 0x7c, 0x13, 0x02, 0xf9, 0x86, 0x40, 0x86, 0x02, - 0xd6, 0x19, 0x40, 0x86, 0x02, 0x68, 0x14, 0x2c, - 0x15, 0x02, 0x69, 0x14, 0xa8, 0x13, 0x02, 0xd4, - 0x19, 0x42, 0x86, 0x04, 0x68, 0x14, 0x69, 0x14, - 0x66, 0x14, 0x66, 0x14, 0x02, 0x77, 0x14, 0x42, - 0x86, 0x02, 0x39, 0x19, 0x42, 0x86, 0x02, 0xd1, - 0x19, 0xa4, 0x13, 0x02, 0x6e, 0x14, 0x40, 0x86, - 0x03, 0xd1, 0x19, 0xd2, 0x19, 0xd2, 0x19, 0x02, - 0x69, 0x14, 0xbb, 0x14, 0x02, 0xd1, 0x19, 0x96, - 0x86, 0x02, 0x75, 0x95, 0x40, 0x86, 0x04, 0x68, - 0x14, 0x64, 0x87, 0x8b, 0x14, 0x68, 0x14, 0x02, - 0xd1, 0x19, 0x3e, 0x13, 0x02, 0xdf, 0x19, 0x40, - 0x86, 0x02, 0x82, 0x14, 0x40, 0x86, 0x02, 0x44, - 0x13, 0xeb, 0x17, 0x02, 0xdd, 0x19, 0x42, 0x86, - 0x02, 0x69, 0x14, 0x80, 0x16, 0x03, 0x68, 0x14, - 0xaf, 0x19, 0xa1, 0x87, 0x02, 0xa3, 0x16, 0x42, - 0x86, 0x02, 0x69, 0x14, 0x96, 0x86, 0x02, 0x46, - 0x16, 0x42, 0x86, 0x02, 0xb6, 0x16, 0xa1, 0x87, - 0x02, 0x68, 0x14, 0x27, 0x15, 0x02, 0x26, 0x14, - 0x1b, 0x0b, 0x02, 0xd4, 0x19, 0x40, 0x86, 0x02, - 0x77, 0x14, 0x40, 0x86, 0x02, 0x39, 0x19, 0x40, - 0x86, 0x02, 0x37, 0x19, 0x42, 0x86, 0x03, 0x69, - 0x14, 0x67, 0x14, 0x66, 0x14, 0x03, 0xc3, 0x13, - 0x42, 0x86, 0xa1, 0x87, 0x02, 0x68, 0x14, 0xbc, - 0x19, 0x02, 0xd1, 0x19, 0xeb, 0x13, 0x04, 0x69, - 0x14, 0x69, 0x14, 0x67, 0x14, 0x67, 0x14, 0x02, - 0xd1, 0x19, 0x08, 0x87, 0x02, 0x68, 0x14, 0xed, - 0x13, 0x03, 0x69, 0x14, 0xbc, 0x19, 0xa1, 0x87, - 0x02, 0xdd, 0x19, 0x40, 0x86, 0x02, 0xc3, 0x13, - 0xa1, 0x87, 0x03, 0x68, 0x14, 0x66, 0x14, 0x66, - 0x14, 0x03, 0x68, 0x14, 0x69, 0x14, 0x67, 0x14, - 0x02, 0xa3, 0x16, 0x40, 0x86, 0x02, 0xdb, 0x19, - 0x42, 0x86, 0x02, 0x68, 0x14, 0xaf, 0x19, 0x02, - 0x46, 0x16, 0x40, 0x86, 0x02, 0x35, 0x16, 0xab, - 0x14, 0x02, 0x68, 0x14, 0x95, 0x86, 0x02, 0x42, - 0x16, 0x95, 0x81, 0x02, 0xc4, 0x13, 0x42, 0x86, - 0x02, 0x15, 0x14, 0xba, 0x19, 0x02, 0x69, 0x14, - 0x08, 0x87, 0x03, 0xd1, 0x19, 0x1d, 0x19, 0xd1, - 0x19, 0x02, 0x69, 0x14, 0x7c, 0x13, 0x02, 0x37, - 0x19, 0x40, 0x86, 0x02, 0x73, 0x14, 0x42, 0x86, - 0x02, 0x69, 0x14, 0x2c, 0x15, 0x02, 0xb5, 0x16, - 0x42, 0x86, 0x02, 0x35, 0x19, 0x42, 0x86, 0x04, - 0x68, 0x14, 0x69, 0x14, 0x67, 0x14, 0x66, 0x14, - 0x02, 0x64, 0x87, 0x25, 0x15, 0x02, 0x64, 0x87, - 0x79, 0x1a, 0x02, 0x68, 0x14, 0xbc, 0x14, 0x03, - 0xce, 0x19, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x87, - 0x14, 0x42, 0x86, 0x02, 0x4d, 0x16, 0x42, 0x86, - 0x04, 0x68, 0x14, 0x68, 0x14, 0x66, 0x14, 0x66, - 0x14, 0x02, 0xdb, 0x19, 0x40, 0x86, 0x02, 0xd9, - 0x19, 0x42, 0x86, 0x02, 0xc4, 0x13, 0x40, 0x86, - 0x02, 0xd1, 0x19, 0xbd, 0x19, 0x02, 0x68, 0x14, - 0xa4, 0x13, 0x02, 0x3e, 0x19, 0x42, 0x86, 0x02, - 0xf3, 0x93, 0xa7, 0x86, 0x03, 0x69, 0x14, 0xaf, - 0x19, 0xa1, 0x87, 0x02, 0xf3, 0x93, 0x08, 0x13, - 0x02, 0xd1, 0x19, 0xd2, 0x19, 0x02, 0x73, 0x14, - 0x40, 0x86, 0x02, 0xb5, 0x16, 0x40, 0x86, 0x02, - 0x35, 0x19, 0x40, 0x86, 0x02, 0x69, 0x14, 0x27, - 0x15, 0x02, 0xce, 0x19, 0x42, 0x86, 0x02, 0x71, - 0x14, 0x42, 0x86, 0x02, 0xd1, 0x19, 0x73, 0x13, - 0x02, 0x68, 0x14, 0x3e, 0x13, 0x02, 0xf4, 0x13, - 0x20, 0x86, 0x02, 0x87, 0x14, 0x40, 0x86, 0x03, - 0xb6, 0x16, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x4d, - 0x16, 0x40, 0x86, 0x02, 0x69, 0x14, 0xbc, 0x19, - 0x02, 0x4b, 0x16, 0x42, 0x86, 0x02, 0xd9, 0x19, - 0x40, 0x86, 0x02, 0x3e, 0x19, 0x40, 0x86, 0x02, - 0x69, 0x14, 0xed, 0x13, 0x02, 0xd7, 0x19, 0x42, - 0x86, 0x02, 0xb8, 0x19, 0x42, 0x86, 0x03, 0x68, - 0x14, 0x67, 0x14, 0x66, 0x14, 0x02, 0x3c, 0x19, - 0x42, 0x86, 0x02, 0x68, 0x14, 0x66, 0x14, 0x03, - 0x68, 0x14, 0x64, 0x87, 0x68, 0x14, 0x02, 0x69, - 0x14, 0xaf, 0x19, 0x02, 0xce, 0x19, 0x40, 0x86, - 0x02, 0x71, 0x14, 0x40, 0x86, 0x02, 0x68, 0x14, - 0xeb, 0x13, 0x03, 0x68, 0x14, 0xbd, 0x19, 0xa1, - 0x87, 0x02, 0x6f, 0x14, 0x42, 0x86, 0x04, 0xd1, - 0x19, 0xd1, 0x19, 0xd2, 0x19, 0xd2, 0x19, 0x02, - 0x69, 0x14, 0xbc, 0x14, 0x02, 0xcc, 0x93, 0x42, - 0x86, 0x02, 0x4b, 0x16, 0x40, 0x86, 0x02, 0x26, - 0x19, 0x42, 0x86, 0x02, 0xd7, 0x19, 0x40, 0x86, -}; - -#endif /* CONFIG_ALL_UNICODE */ -/* 71 tables / 36311 bytes, 5 index / 351 bytes */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.c deleted file mode 100755 index 0c510ccb..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.c +++ /dev/null @@ -1,2123 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "libunicode.h" -#include "libunicode-table.h" - -enum { - RUN_TYPE_U, - RUN_TYPE_L, - RUN_TYPE_UF, - RUN_TYPE_LF, - RUN_TYPE_UL, - RUN_TYPE_LSU, - RUN_TYPE_U2L_399_EXT2, - RUN_TYPE_UF_D20, - RUN_TYPE_UF_D1_EXT, - RUN_TYPE_U_EXT, - RUN_TYPE_LF_EXT, - RUN_TYPE_UF_EXT2, - RUN_TYPE_LF_EXT2, - RUN_TYPE_UF_EXT3, -}; - -static int lre_case_conv1(uint32_t c, int conv_type) -{ - uint32_t res[LRE_CC_RES_LEN_MAX]; - lre_case_conv(res, c, conv_type); - return res[0]; -} - -/* case conversion using the table entry 'idx' with value 'v' */ -static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v) -{ - uint32_t code, data, type, a, is_lower; - is_lower = (conv_type != 0); - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - data = ((v & 0xf) << 8) | case_conv_table2[idx]; - code = v >> (32 - 17); - switch(type) { - case RUN_TYPE_U: - case RUN_TYPE_L: - case RUN_TYPE_UF: - case RUN_TYPE_LF: - if (conv_type == (type & 1) || - (type >= RUN_TYPE_UF && conv_type == 2)) { - c = c - code + (case_conv_table1[data] >> (32 - 17)); - } - break; - case RUN_TYPE_UL: - a = c - code; - if ((a & 1) != (1 - is_lower)) - break; - c = (a ^ 1) + code; - break; - case RUN_TYPE_LSU: - a = c - code; - if (a == 1) { - c += 2 * is_lower - 1; - } else if (a == (1 - is_lower) * 2) { - c += (2 * is_lower - 1) * 2; - } - break; - case RUN_TYPE_U2L_399_EXT2: - if (!is_lower) { - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = 0x399; - return 2; - } else { - c = c - code + case_conv_ext[data & 0x3f]; - } - break; - case RUN_TYPE_UF_D20: - if (conv_type == 1) - break; - c = data + (conv_type == 2) * 0x20; - break; - case RUN_TYPE_UF_D1_EXT: - if (conv_type == 1) - break; - c = case_conv_ext[data] + (conv_type == 2); - break; - case RUN_TYPE_U_EXT: - case RUN_TYPE_LF_EXT: - if (is_lower != (type - RUN_TYPE_U_EXT)) - break; - c = case_conv_ext[data]; - break; - case RUN_TYPE_LF_EXT2: - if (!is_lower) - break; - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = case_conv_ext[data & 0x3f]; - return 2; - case RUN_TYPE_UF_EXT2: - if (conv_type == 1) - break; - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = case_conv_ext[data & 0x3f]; - if (conv_type == 2) { - /* convert to lower */ - res[0] = lre_case_conv1(res[0], 1); - res[1] = lre_case_conv1(res[1], 1); - } - return 2; - default: - case RUN_TYPE_UF_EXT3: - if (conv_type == 1) - break; - res[0] = case_conv_ext[data >> 8]; - res[1] = case_conv_ext[(data >> 4) & 0xf]; - res[2] = case_conv_ext[data & 0xf]; - if (conv_type == 2) { - /* convert to lower */ - res[0] = lre_case_conv1(res[0], 1); - res[1] = lre_case_conv1(res[1], 1); - res[2] = lre_case_conv1(res[2], 1); - } - return 3; - } - res[0] = c; - return 1; -} - -/* conv_type: - 0 = to upper - 1 = to lower - 2 = case folding (= to lower with modifications) -*/ -int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) -{ - if (c < 128) { - if (conv_type) { - if (c >= 'A' && c <= 'Z') { - c = c - 'A' + 'a'; - } - } else { - if (c >= 'a' && c <= 'z') { - c = c - 'a' + 'A'; - } - } - } else { - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return lre_case_conv_entry(res, c, conv_type, idx, v); - } - } - } - res[0] = c; - return 1; -} - -static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, BOOL is_unicode) -{ - uint32_t res[LRE_CC_RES_LEN_MAX]; - int len; - - if (is_unicode) { - len = lre_case_conv_entry(res, c, 2, idx, v); - if (len == 1) { - c = res[0]; - } else { - /* handle the few specific multi-character cases (see - unicode_gen.c:dump_case_folding_special_cases()) */ - if (c == 0xfb06) { - c = 0xfb05; - } else if (c == 0x01fd3) { - c = 0x390; - } else if (c == 0x01fe3) { - c = 0x3b0; - } - } - } else { - if (likely(c < 128)) { - if (c >= 'a' && c <= 'z') - c = c - 'a' + 'A'; - } else { - /* legacy regexp: to upper case if single char >= 128 */ - len = lre_case_conv_entry(res, c, FALSE, idx, v); - if (len == 1 && res[0] >= 128) - c = res[0]; - } - } - return c; -} - -/* JS regexp specific rules for case folding */ -int lre_canonicalize(uint32_t c, BOOL is_unicode) -{ - if (c < 128) { - /* fast case */ - if (is_unicode) { - if (c >= 'A' && c <= 'Z') { - c = c - 'A' + 'a'; - } - } else { - if (c >= 'a' && c <= 'z') { - c = c - 'a' + 'A'; - } - } - } else { - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return lre_case_folding_entry(c, idx, v, is_unicode); - } - } - } - return c; -} - -static uint32_t get_le24(const uint8_t *ptr) -{ - return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); -} - -#define UNICODE_INDEX_BLOCK_LEN 32 - -/* return -1 if not in table, otherwise the offset in the block */ -static int get_index_pos(uint32_t *pcode, uint32_t c, - const uint8_t *index_table, int index_table_len) -{ - uint32_t code, v; - int idx_min, idx_max, idx; - - idx_min = 0; - v = get_le24(index_table); - code = v & ((1 << 21) - 1); - if (c < code) { - *pcode = 0; - return 0; - } - idx_max = index_table_len - 1; - code = get_le24(index_table + idx_max * 3); - if (c >= code) - return -1; - /* invariant: tab[idx_min] <= c < tab2[idx_max] */ - while ((idx_max - idx_min) > 1) { - idx = (idx_max + idx_min) / 2; - v = get_le24(index_table + idx * 3); - code = v & ((1 << 21) - 1); - if (c < code) { - idx_max = idx; - } else { - idx_min = idx; - } - } - v = get_le24(index_table + idx_min * 3); - *pcode = v & ((1 << 21) - 1); - return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); -} - -static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, - const uint8_t *index_table, int index_table_len) -{ - uint32_t code, b, bit; - int pos; - const uint8_t *p; - - pos = get_index_pos(&code, c, index_table, index_table_len); - if (pos < 0) - return FALSE; /* outside the table */ - p = table + pos; - bit = 0; - /* Compressed run length encoding: - 00..3F: 2 packed lengths: 3-bit + 3-bit - 40..5F: 5-bits plus extra byte for length - 60..7F: 5-bits plus 2 extra bytes for length - 80..FF: 7-bit length - lengths must be incremented to get character count - Ranges alternate between false and true return value. - */ - for(;;) { - b = *p++; - if (b < 64) { - code += (b >> 3) + 1; - if (c < code) - return bit; - bit ^= 1; - code += (b & 7) + 1; - } else if (b >= 0x80) { - code += b - 0x80 + 1; - } else if (b < 0x60) { - code += (((b - 0x40) << 8) | p[0]) + 1; - p++; - } else { - code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; - p += 2; - } - if (c < code) - return bit; - bit ^= 1; - } -} - -BOOL lre_is_cased(uint32_t c) -{ - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return TRUE; - } - } - return lre_is_in_table(c, unicode_prop_Cased1_table, - unicode_prop_Cased1_index, - sizeof(unicode_prop_Cased1_index) / 3); -} - -BOOL lre_is_case_ignorable(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, - unicode_prop_Case_Ignorable_index, - sizeof(unicode_prop_Case_Ignorable_index) / 3); -} - -/* character range */ - -static __maybe_unused void cr_dump(CharRange *cr) -{ - int i; - for(i = 0; i < cr->len; i++) - printf("%d: 0x%04x\n", i, cr->points[i]); -} - -static void *cr_default_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) -{ - cr->len = cr->size = 0; - cr->points = NULL; - cr->mem_opaque = mem_opaque; - cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; -} - -void cr_free(CharRange *cr) -{ - cr->realloc_func(cr->mem_opaque, cr->points, 0); -} - -int cr_realloc(CharRange *cr, int size) -{ - int new_size; - uint32_t *new_buf; - - if (size > cr->size) { - new_size = max_int(size, cr->size * 3 / 2); - new_buf = cr->realloc_func(cr->mem_opaque, cr->points, - new_size * sizeof(cr->points[0])); - if (!new_buf) - return -1; - cr->points = new_buf; - cr->size = new_size; - } - return 0; -} - -int cr_copy(CharRange *cr, const CharRange *cr1) -{ - if (cr_realloc(cr, cr1->len)) - return -1; - memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); - cr->len = cr1->len; - return 0; -} - -/* merge consecutive intervals and remove empty intervals */ -static void cr_compress(CharRange *cr) -{ - int i, j, k, len; - uint32_t *pt; - - pt = cr->points; - len = cr->len; - i = 0; - j = 0; - k = 0; - while ((i + 1) < len) { - if (pt[i] == pt[i + 1]) { - /* empty interval */ - i += 2; - } else { - j = i; - while ((j + 3) < len && pt[j + 1] == pt[j + 2]) - j += 2; - /* just copy */ - pt[k] = pt[i]; - pt[k + 1] = pt[j + 1]; - k += 2; - i = j + 2; - } - } - cr->len = k; -} - -/* union or intersection */ -int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, - const uint32_t *b_pt, int b_len, int op) -{ - int a_idx, b_idx, is_in; - uint32_t v; - - a_idx = 0; - b_idx = 0; - for(;;) { - /* get one more point from a or b in increasing order */ - if (a_idx < a_len && b_idx < b_len) { - if (a_pt[a_idx] < b_pt[b_idx]) { - goto a_add; - } else if (a_pt[a_idx] == b_pt[b_idx]) { - v = a_pt[a_idx]; - a_idx++; - b_idx++; - } else { - goto b_add; - } - } else if (a_idx < a_len) { - a_add: - v = a_pt[a_idx++]; - } else if (b_idx < b_len) { - b_add: - v = b_pt[b_idx++]; - } else { - break; - } - /* add the point if the in/out status changes */ - switch(op) { - case CR_OP_UNION: - is_in = (a_idx & 1) | (b_idx & 1); - break; - case CR_OP_INTER: - is_in = (a_idx & 1) & (b_idx & 1); - break; - case CR_OP_XOR: - is_in = (a_idx & 1) ^ (b_idx & 1); - break; - case CR_OP_SUB: - is_in = (a_idx & 1) & ((b_idx & 1) ^ 1); - break; - default: - abort(); - } - if (is_in != (cr->len & 1)) { - if (cr_add_point(cr, v)) - return -1; - } - } - cr_compress(cr); - return 0; -} - -int cr_op1(CharRange *cr, const uint32_t *b_pt, int b_len, int op) -{ - CharRange a = *cr; - int ret; - cr->len = 0; - cr->size = 0; - cr->points = NULL; - ret = cr_op(cr, a.points, a.len, b_pt, b_len, op); - cr_free(&a); - return ret; -} - -int cr_invert(CharRange *cr) -{ - int len; - len = cr->len; - if (cr_realloc(cr, len + 2)) - return -1; - memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); - cr->points[0] = 0; - cr->points[len + 1] = UINT32_MAX; - cr->len = len + 2; - cr_compress(cr); - return 0; -} - -#define CASE_U (1 << 0) -#define CASE_L (1 << 1) -#define CASE_F (1 << 2) - -/* use the case conversion table to generate range of characters. - CASE_U: set char if modified by uppercasing, - CASE_L: set char if modified by lowercasing, - CASE_F: set char if modified by case folding, - */ -static int unicode_case1(CharRange *cr, int case_mask) -{ -#define MR(x) (1 << RUN_TYPE_ ## x) - const uint32_t tab_run_mask[3] = { - MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | - MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3), - - MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2), - - MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3), - }; -#undef MR - uint32_t mask, v, code, type, len, i, idx; - - if (case_mask == 0) - return 0; - mask = 0; - for(i = 0; i < 3; i++) { - if ((case_mask >> i) & 1) - mask |= tab_run_mask[i]; - } - for(idx = 0; idx < countof(case_conv_table1); idx++) { - v = case_conv_table1[idx]; - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if ((mask >> type) & 1) { - // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); - switch(type) { - case RUN_TYPE_UL: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - code += ((case_mask & CASE_U) != 0); - for(i = 0; i < len; i += 2) { - if (cr_add_interval(cr, code + i, code + i + 1)) - return -1; - } - break; - case RUN_TYPE_LSU: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - if (!(case_mask & CASE_U)) { - if (cr_add_interval(cr, code, code + 1)) - return -1; - } - if (cr_add_interval(cr, code + 1, code + 2)) - return -1; - if (case_mask & CASE_U) { - if (cr_add_interval(cr, code + 2, code + 3)) - return -1; - } - break; - default: - def_case: - if (cr_add_interval(cr, code, code + len)) - return -1; - break; - } - } - } - return 0; -} - -static int point_cmp(const void *p1, const void *p2, void *arg) -{ - uint32_t v1 = *(uint32_t *)p1; - uint32_t v2 = *(uint32_t *)p2; - return (v1 > v2) - (v1 < v2); -} - -static void cr_sort_and_remove_overlap(CharRange *cr) -{ - uint32_t start, end, start1, end1, i, j; - - /* the resulting ranges are not necessarily sorted and may overlap */ - rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); - j = 0; - for(i = 0; i < cr->len; ) { - start = cr->points[i]; - end = cr->points[i + 1]; - i += 2; - while (i < cr->len) { - start1 = cr->points[i]; - end1 = cr->points[i + 1]; - if (start1 > end) { - /* |------| - * |-------| */ - break; - } else if (end1 <= end) { - /* |------| - * |--| */ - i += 2; - } else { - /* |------| - * |-------| */ - end = end1; - i += 2; - } - } - cr->points[j] = start; - cr->points[j + 1] = end; - j += 2; - } - cr->len = j; -} - -/* canonicalize a character set using the JS regex case folding rules - (see lre_canonicalize()) */ -int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode) -{ - CharRange cr_inter, cr_mask, cr_result, cr_sub; - uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; - - cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func); - cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func); - cr_init(&cr_result, cr->mem_opaque, cr->realloc_func); - cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func); - - if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U)) - goto fail; - if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) - goto fail; - - if (cr_invert(&cr_mask)) - goto fail; - if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) - goto fail; - - /* cr_inter = cr & cr_mask */ - /* cr_sub = cr & ~cr_mask */ - - /* use the case conversion table to compute the result */ - d_start = -1; - d_end = -1; - idx = 0; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - for(i = 0; i < cr_inter.len; i += 2) { - start = cr_inter.points[i]; - end = cr_inter.points[i + 1]; - - for(c = start; c < end; c++) { - for(;;) { - if (c >= code && c < code + len) - break; - idx++; - assert(idx < countof(case_conv_table1)); - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - } - d = lre_case_folding_entry(c, idx, v, is_unicode); - /* try to merge with the current interval */ - if (d_start == -1) { - d_start = d; - d_end = d + 1; - } else if (d_end == d) { - d_end++; - } else { - cr_add_interval(&cr_result, d_start, d_end); - d_start = d; - d_end = d + 1; - } - } - } - if (d_start != -1) { - if (cr_add_interval(&cr_result, d_start, d_end)) - goto fail; - } - - /* the resulting ranges are not necessarily sorted and may overlap */ - cr_sort_and_remove_overlap(&cr_result); - - /* or with the character not affected by the case folding */ - cr->len = 0; - if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION)) - goto fail; - - cr_free(&cr_inter); - cr_free(&cr_mask); - cr_free(&cr_result); - cr_free(&cr_sub); - return 0; - fail: - cr_free(&cr_inter); - cr_free(&cr_mask); - cr_free(&cr_result); - cr_free(&cr_sub); - return -1; -} - -#ifdef CONFIG_ALL_UNICODE - -BOOL lre_is_id_start(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_ID_Start_table, - unicode_prop_ID_Start_index, - sizeof(unicode_prop_ID_Start_index) / 3); -} - -BOOL lre_is_id_continue(uint32_t c) -{ - return lre_is_id_start(c) || - lre_is_in_table(c, unicode_prop_ID_Continue1_table, - unicode_prop_ID_Continue1_index, - sizeof(unicode_prop_ID_Continue1_index) / 3); -} - -#define UNICODE_DECOMP_LEN_MAX 18 - -typedef enum { - DECOMP_TYPE_C1, /* 16 bit char */ - DECOMP_TYPE_L1, /* 16 bit char table */ - DECOMP_TYPE_L2, - DECOMP_TYPE_L3, - DECOMP_TYPE_L4, - DECOMP_TYPE_L5, /* XXX: not used */ - DECOMP_TYPE_L6, /* XXX: could remove */ - DECOMP_TYPE_L7, /* XXX: could remove */ - DECOMP_TYPE_LL1, /* 18 bit char table */ - DECOMP_TYPE_LL2, - DECOMP_TYPE_S1, /* 8 bit char table */ - DECOMP_TYPE_S2, - DECOMP_TYPE_S3, - DECOMP_TYPE_S4, - DECOMP_TYPE_S5, - DECOMP_TYPE_I1, /* increment 16 bit char value */ - DECOMP_TYPE_I2_0, - DECOMP_TYPE_I2_1, - DECOMP_TYPE_I3_1, - DECOMP_TYPE_I3_2, - DECOMP_TYPE_I4_1, - DECOMP_TYPE_I4_2, - DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ - DECOMP_TYPE_B2, - DECOMP_TYPE_B3, - DECOMP_TYPE_B4, - DECOMP_TYPE_B5, - DECOMP_TYPE_B6, - DECOMP_TYPE_B7, - DECOMP_TYPE_B8, - DECOMP_TYPE_B18, - DECOMP_TYPE_LS2, - DECOMP_TYPE_PAT3, - DECOMP_TYPE_S2_UL, - DECOMP_TYPE_LS2_UL, -} DecompTypeEnum; - -static uint32_t unicode_get_short_code(uint32_t c) -{ - static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; - - if (c < 0x80) - return c; - else if (c < 0x80 + 0x50) - return c - 0x80 + 0x300; - else - return unicode_short_table[c - 0x80 - 0x50]; -} - -static uint32_t unicode_get_lower_simple(uint32_t c) -{ - if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) - c += 0x20; - else - c++; - return c; -} - -static uint16_t unicode_get16(const uint8_t *p) -{ - return p[0] | (p[1] << 8); -} - -static int unicode_decomp_entry(uint32_t *res, uint32_t c, - int idx, uint32_t code, uint32_t len, - uint32_t type) -{ - uint32_t c1; - int l, i, p; - const uint8_t *d; - - if (type == DECOMP_TYPE_C1) { - res[0] = unicode_decomp_table2[idx]; - return 1; - } else { - d = unicode_decomp_data + unicode_decomp_table2[idx]; - switch(type) { - case DECOMP_TYPE_L1: - case DECOMP_TYPE_L2: - case DECOMP_TYPE_L3: - case DECOMP_TYPE_L4: - case DECOMP_TYPE_L5: - case DECOMP_TYPE_L6: - case DECOMP_TYPE_L7: - l = type - DECOMP_TYPE_L1 + 1; - d += (c - code) * l * 2; - for(i = 0; i < l; i++) { - if ((res[i] = unicode_get16(d + 2 * i)) == 0) - return 0; - } - return l; - case DECOMP_TYPE_LL1: - case DECOMP_TYPE_LL2: - { - uint32_t k, p; - l = type - DECOMP_TYPE_LL1 + 1; - k = (c - code) * l; - p = len * l * 2; - for(i = 0; i < l; i++) { - c1 = unicode_get16(d + 2 * k) | - (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); - if (!c1) - return 0; - res[i] = c1; - k++; - } - } - return l; - case DECOMP_TYPE_S1: - case DECOMP_TYPE_S2: - case DECOMP_TYPE_S3: - case DECOMP_TYPE_S4: - case DECOMP_TYPE_S5: - l = type - DECOMP_TYPE_S1 + 1; - d += (c - code) * l; - for(i = 0; i < l; i++) { - if ((res[i] = unicode_get_short_code(d[i])) == 0) - return 0; - } - return l; - case DECOMP_TYPE_I1: - l = 1; - p = 0; - goto decomp_type_i; - case DECOMP_TYPE_I2_0: - case DECOMP_TYPE_I2_1: - case DECOMP_TYPE_I3_1: - case DECOMP_TYPE_I3_2: - case DECOMP_TYPE_I4_1: - case DECOMP_TYPE_I4_2: - l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); - p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); - decomp_type_i: - for(i = 0; i < l; i++) { - c1 = unicode_get16(d + 2 * i); - if (i == p) - c1 += c - code; - res[i] = c1; - } - return l; - case DECOMP_TYPE_B18: - l = 18; - goto decomp_type_b; - case DECOMP_TYPE_B1: - case DECOMP_TYPE_B2: - case DECOMP_TYPE_B3: - case DECOMP_TYPE_B4: - case DECOMP_TYPE_B5: - case DECOMP_TYPE_B6: - case DECOMP_TYPE_B7: - case DECOMP_TYPE_B8: - l = type - DECOMP_TYPE_B1 + 1; - decomp_type_b: - { - uint32_t c_min; - c_min = unicode_get16(d); - d += 2 + (c - code) * l; - for(i = 0; i < l; i++) { - c1 = d[i]; - if (c1 == 0xff) - c1 = 0x20; - else - c1 += c_min; - res[i] = c1; - } - } - return l; - case DECOMP_TYPE_LS2: - d += (c - code) * 3; - if (!(res[0] = unicode_get16(d))) - return 0; - res[1] = unicode_get_short_code(d[2]); - return 2; - case DECOMP_TYPE_PAT3: - res[0] = unicode_get16(d); - res[2] = unicode_get16(d + 2); - d += 4 + (c - code) * 2; - res[1] = unicode_get16(d); - return 3; - case DECOMP_TYPE_S2_UL: - case DECOMP_TYPE_LS2_UL: - c1 = c - code; - if (type == DECOMP_TYPE_S2_UL) { - d += c1 & ~1; - c = unicode_get_short_code(*d); - d++; - } else { - d += (c1 >> 1) * 3; - c = unicode_get16(d); - d += 2; - } - if (c1 & 1) - c = unicode_get_lower_simple(c); - res[0] = c; - res[1] = unicode_get_short_code(*d); - return 2; - } - } - return 0; -} - - -/* return the length of the decomposition (length <= - UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ -static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) -{ - uint32_t v, type, is_compat, code, len; - int idx_min, idx_max, idx; - - idx_min = 0; - idx_max = countof(unicode_decomp_table1) - 1; - while (idx_min <= idx_max) { - idx = (idx_max + idx_min) / 2; - v = unicode_decomp_table1[idx]; - code = v >> (32 - 18); - len = (v >> (32 - 18 - 7)) & 0x7f; - // printf("idx=%d code=%05x len=%d\n", idx, code, len); - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - is_compat = v & 1; - if (is_compat1 < is_compat) - break; - type = (v >> (32 - 18 - 7 - 6)) & 0x3f; - return unicode_decomp_entry(res, c, idx, code, len, type); - } - } - return 0; -} - -/* return 0 if no pair found */ -static int unicode_compose_pair(uint32_t c0, uint32_t c1) -{ - uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; - int idx_min, idx_max, idx, d; - uint32_t pair[2]; - - idx_min = 0; - idx_max = countof(unicode_comp_table) - 1; - while (idx_min <= idx_max) { - idx = (idx_max + idx_min) / 2; - idx1 = unicode_comp_table[idx]; - - /* idx1 represent an entry of the decomposition table */ - d_idx = idx1 >> 6; - d_offset = idx1 & 0x3f; - v = unicode_decomp_table1[d_idx]; - code = v >> (32 - 18); - len = (v >> (32 - 18 - 7)) & 0x7f; - type = (v >> (32 - 18 - 7 - 6)) & 0x3f; - ch = code + d_offset; - unicode_decomp_entry(pair, ch, d_idx, code, len, type); - d = c0 - pair[0]; - if (d == 0) - d = c1 - pair[1]; - if (d < 0) { - idx_max = idx - 1; - } else if (d > 0) { - idx_min = idx + 1; - } else { - return ch; - } - } - return 0; -} - -/* return the combining class of character c (between 0 and 255) */ -static int unicode_get_cc(uint32_t c) -{ - uint32_t code, n, type, cc, c1, b; - int pos; - const uint8_t *p; - - pos = get_index_pos(&code, c, - unicode_cc_index, sizeof(unicode_cc_index) / 3); - if (pos < 0) - return 0; - p = unicode_cc_table + pos; - /* Compressed run length encoding: - - 2 high order bits are combining class type - - 0:0, 1:230, 2:extra byte linear progression, 3:extra byte - - 00..2F: range length (add 1) - - 30..37: 3-bit range-length + 1 extra byte - - 38..3F: 3-bit range-length + 2 extra byte - */ - for(;;) { - b = *p++; - type = b >> 6; - n = b & 0x3f; - if (n < 48) { - } else if (n < 56) { - n = (n - 48) << 8; - n |= *p++; - n += 48; - } else { - n = (n - 56) << 8; - n |= *p++ << 8; - n |= *p++; - n += 48 + (1 << 11); - } - if (type <= 1) - p++; - c1 = code + n + 1; - if (c < c1) { - switch(type) { - case 0: - cc = p[-1]; - break; - case 1: - cc = p[-1] + c - code; - break; - case 2: - cc = 0; - break; - default: - case 3: - cc = 230; - break; - } - return cc; - } - code = c1; - } -} - -static void sort_cc(int *buf, int len) -{ - int i, j, k, cc, cc1, start, ch1; - - for(i = 0; i < len; i++) { - cc = unicode_get_cc(buf[i]); - if (cc != 0) { - start = i; - j = i + 1; - while (j < len) { - ch1 = buf[j]; - cc1 = unicode_get_cc(ch1); - if (cc1 == 0) - break; - k = j - 1; - while (k >= start) { - if (unicode_get_cc(buf[k]) <= cc1) - break; - buf[k + 1] = buf[k]; - k--; - } - buf[k + 1] = ch1; - j++; - } -#if 0 - printf("cc:"); - for(k = start; k < j; k++) { - printf(" %3d", unicode_get_cc(buf[k])); - } - printf("\n"); -#endif - i = j; - } - } -} - -static void to_nfd_rec(DynBuf *dbuf, - const int *src, int src_len, int is_compat) -{ - uint32_t c, v; - int i, l; - uint32_t res[UNICODE_DECOMP_LEN_MAX]; - - for(i = 0; i < src_len; i++) { - c = src[i]; - if (c >= 0xac00 && c < 0xd7a4) { - /* Hangul decomposition */ - c -= 0xac00; - dbuf_put_u32(dbuf, 0x1100 + c / 588); - dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); - v = c % 28; - if (v != 0) - dbuf_put_u32(dbuf, 0x11a7 + v); - } else { - l = unicode_decomp_char(res, c, is_compat); - if (l) { - to_nfd_rec(dbuf, (int *)res, l, is_compat); - } else { - dbuf_put_u32(dbuf, c); - } - } - } -} - -/* return 0 if not found */ -static int compose_pair(uint32_t c0, uint32_t c1) -{ - /* Hangul composition */ - if (c0 >= 0x1100 && c0 < 0x1100 + 19 && - c1 >= 0x1161 && c1 < 0x1161 + 21) { - return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; - } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && - (c0 - 0xac00) % 28 == 0 && - c1 >= 0x11a7 && c1 < 0x11a7 + 28) { - return c0 + c1 - 0x11a7; - } else { - return unicode_compose_pair(c0, c1); - } -} - -int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, - UnicodeNormalizationEnum n_type, - void *opaque, DynBufReallocFunc *realloc_func) -{ - int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; - BOOL is_compat; - DynBuf dbuf_s, *dbuf = &dbuf_s; - - is_compat = n_type >> 1; - - dbuf_init2(dbuf, opaque, realloc_func); - if (dbuf_claim(dbuf, sizeof(int) * src_len)) - goto fail; - - /* common case: latin1 is unaffected by NFC */ - if (n_type == UNICODE_NFC) { - for(i = 0; i < src_len; i++) { - if (src[i] >= 0x100) - goto not_latin1; - } - buf = (int *)dbuf->buf; - memcpy(buf, src, src_len * sizeof(int)); - *pdst = (uint32_t *)buf; - return src_len; - not_latin1: ; - } - - to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); - if (dbuf_error(dbuf)) { - fail: - *pdst = NULL; - return -1; - } - buf = (int *)dbuf->buf; - buf_len = dbuf->size / sizeof(int); - - sort_cc(buf, buf_len); - - if (buf_len <= 1 || (n_type & 1) != 0) { - /* NFD / NFKD */ - *pdst = (uint32_t *)buf; - return buf_len; - } - - i = 1; - out_len = 1; - while (i < buf_len) { - /* find the starter character and test if it is blocked from - the character at 'i' */ - last_cc = unicode_get_cc(buf[i]); - starter_pos = out_len - 1; - while (starter_pos >= 0) { - cc = unicode_get_cc(buf[starter_pos]); - if (cc == 0) - break; - if (cc >= last_cc) - goto next; - last_cc = 256; - starter_pos--; - } - if (starter_pos >= 0 && - (p = compose_pair(buf[starter_pos], buf[i])) != 0) { - buf[starter_pos] = p; - i++; - } else { - next: - buf[out_len++] = buf[i++]; - } - } - *pdst = (uint32_t *)buf; - return out_len; -} - -/* char ranges for various unicode properties */ - -static int unicode_find_name(const char *name_table, const char *name) -{ - const char *p, *r; - int pos; - size_t name_len, len; - - p = name_table; - pos = 0; - name_len = strlen(name); - while (*p) { - for(;;) { - r = strchr(p, ','); - if (!r) - len = strlen(p); - else - len = r - p; - if (len == name_len && !memcmp(p, name, name_len)) - return pos; - p += len + 1; - if (!r) - break; - } - pos++; - } - return -1; -} - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_script(CharRange *cr, - const char *script_name, BOOL is_ext) -{ - int script_idx; - const uint8_t *p, *p_end; - uint32_t c, c1, b, n, v, v_len, i, type; - CharRange cr1_s, *cr1; - CharRange cr2_s, *cr2 = &cr2_s; - BOOL is_common; - - script_idx = unicode_find_name(unicode_script_name_table, script_name); - if (script_idx < 0) - return -2; - - is_common = (script_idx == UNICODE_SCRIPT_Common || - script_idx == UNICODE_SCRIPT_Inherited); - if (is_ext) { - cr1 = &cr1_s; - cr_init(cr1, cr->mem_opaque, cr->realloc_func); - cr_init(cr2, cr->mem_opaque, cr->realloc_func); - } else { - cr1 = cr; - } - - p = unicode_script_table; - p_end = unicode_script_table + countof(unicode_script_table); - c = 0; - while (p < p_end) { - b = *p++; - type = b >> 7; - n = b & 0x7f; - if (n < 96) { - } else if (n < 112) { - n = (n - 96) << 8; - n |= *p++; - n += 96; - } else { - n = (n - 112) << 16; - n |= *p++ << 8; - n |= *p++; - n += 96 + (1 << 12); - } - c1 = c + n + 1; - if (type != 0) { - v = *p++; - if (v == script_idx || script_idx == UNICODE_SCRIPT_Unknown) { - if (cr_add_interval(cr1, c, c1)) - goto fail; - } - } - c = c1; - } - if (script_idx == UNICODE_SCRIPT_Unknown) { - /* Unknown is all the characters outside scripts */ - if (cr_invert(cr1)) - goto fail; - } - - if (is_ext) { - /* add the script extensions */ - p = unicode_script_ext_table; - p_end = unicode_script_ext_table + countof(unicode_script_ext_table); - c = 0; - while (p < p_end) { - b = *p++; - if (b < 128) { - n = b; - } else if (b < 128 + 64) { - n = (b - 128) << 8; - n |= *p++; - n += 128; - } else { - n = (b - 128 - 64) << 16; - n |= *p++ << 8; - n |= *p++; - n += 128 + (1 << 14); - } - c1 = c + n + 1; - v_len = *p++; - if (is_common) { - if (v_len != 0) { - if (cr_add_interval(cr2, c, c1)) - goto fail; - } - } else { - for(i = 0; i < v_len; i++) { - if (p[i] == script_idx) { - if (cr_add_interval(cr2, c, c1)) - goto fail; - break; - } - } - } - p += v_len; - c = c1; - } - if (is_common) { - /* remove all the characters with script extensions */ - if (cr_invert(cr2)) - goto fail; - if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, - CR_OP_INTER)) - goto fail; - } else { - if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, - CR_OP_UNION)) - goto fail; - } - cr_free(cr1); - cr_free(cr2); - } - return 0; - fail: - if (is_ext) { - cr_free(cr1); - cr_free(cr2); - } - goto fail; -} - -#define M(id) (1U << UNICODE_GC_ ## id) - -static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) -{ - const uint8_t *p, *p_end; - uint32_t c, c0, b, n, v; - - p = unicode_gc_table; - p_end = unicode_gc_table + countof(unicode_gc_table); - c = 0; - /* Compressed range encoding: - initial byte: - bits 0..4: category number (special case 31) - bits 5..7: range length (add 1) - special case bits 5..7 == 7: read an extra byte - - 00..7F: range length (add 7 + 1) - - 80..BF: 6-bits plus extra byte for range length (add 7 + 128) - - C0..FF: 6-bits plus 2 extra bytes for range length (add 7 + 128 + 16384) - */ - while (p < p_end) { - b = *p++; - n = b >> 5; - v = b & 0x1f; - if (n == 7) { - n = *p++; - if (n < 128) { - n += 7; - } else if (n < 128 + 64) { - n = (n - 128) << 8; - n |= *p++; - n += 7 + 128; - } else { - n = (n - 128 - 64) << 16; - n |= *p++ << 8; - n |= *p++; - n += 7 + 128 + (1 << 14); - } - } - c0 = c; - c += n + 1; - if (v == 31) { - /* run of Lu / Ll */ - b = gc_mask & (M(Lu) | M(Ll)); - if (b != 0) { - if (b == (M(Lu) | M(Ll))) { - goto add_range; - } else { - c0 += ((gc_mask & M(Ll)) != 0); - for(; c0 < c; c0 += 2) { - if (cr_add_interval(cr, c0, c0 + 1)) - return -1; - } - } - } - } else if ((gc_mask >> v) & 1) { - add_range: - if (cr_add_interval(cr, c0, c)) - return -1; - } - } - return 0; -} - -static int unicode_prop1(CharRange *cr, int prop_idx) -{ - const uint8_t *p, *p_end; - uint32_t c, c0, b, bit; - - p = unicode_prop_table[prop_idx]; - p_end = p + unicode_prop_len_table[prop_idx]; - c = 0; - bit = 0; - /* Compressed range encoding: - 00..3F: 2 packed lengths: 3-bit + 3-bit - 40..5F: 5-bits plus extra byte for length - 60..7F: 5-bits plus 2 extra bytes for length - 80..FF: 7-bit length - lengths must be incremented to get character count - Ranges alternate between false and true return value. - */ - while (p < p_end) { - c0 = c; - b = *p++; - if (b < 64) { - c += (b >> 3) + 1; - if (bit) { - if (cr_add_interval(cr, c0, c)) - return -1; - } - bit ^= 1; - c0 = c; - c += (b & 7) + 1; - } else if (b >= 0x80) { - c += b - 0x80 + 1; - } else if (b < 0x60) { - c += (((b - 0x40) << 8) | p[0]) + 1; - p++; - } else { - c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; - p += 2; - } - if (bit) { - if (cr_add_interval(cr, c0, c)) - return -1; - } - bit ^= 1; - } - return 0; -} - -typedef enum { - POP_GC, - POP_PROP, - POP_CASE, - POP_UNION, - POP_INTER, - POP_XOR, - POP_INVERT, - POP_END, -} PropOPEnum; - -#define POP_STACK_LEN_MAX 4 - -static int unicode_prop_ops(CharRange *cr, ...) -{ - va_list ap; - CharRange stack[POP_STACK_LEN_MAX]; - int stack_len, op, ret, i; - uint32_t a; - - va_start(ap, cr); - stack_len = 0; - for(;;) { - op = va_arg(ap, int); - switch(op) { - case POP_GC: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_general_category1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_PROP: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_prop1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_CASE: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_case1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_UNION: - case POP_INTER: - case POP_XOR: - { - CharRange *cr1, *cr2, *cr3; - assert(stack_len >= 2); - assert(stack_len < POP_STACK_LEN_MAX); - cr1 = &stack[stack_len - 2]; - cr2 = &stack[stack_len - 1]; - cr3 = &stack[stack_len++]; - cr_init(cr3, cr->mem_opaque, cr->realloc_func); - /* CR_OP_XOR may be used here */ - if (cr_op(cr3, cr1->points, cr1->len, - cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) - goto fail; - cr_free(cr1); - cr_free(cr2); - *cr1 = *cr3; - stack_len -= 2; - } - break; - case POP_INVERT: - assert(stack_len >= 1); - if (cr_invert(&stack[stack_len - 1])) - goto fail; - break; - case POP_END: - goto done; - default: - abort(); - } - } - done: - assert(stack_len == 1); - ret = cr_copy(cr, &stack[0]); - cr_free(&stack[0]); - return ret; - fail: - for(i = 0; i < stack_len; i++) - cr_free(&stack[i]); - return -1; -} - -static const uint32_t unicode_gc_mask_table[] = { - M(Lu) | M(Ll) | M(Lt), /* LC */ - M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ - M(Mn) | M(Mc) | M(Me), /* M */ - M(Nd) | M(Nl) | M(No), /* N */ - M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ - M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ - M(Zs) | M(Zl) | M(Zp), /* Z */ - M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ -}; - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_general_category(CharRange *cr, const char *gc_name) -{ - int gc_idx; - uint32_t gc_mask; - - gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); - if (gc_idx < 0) - return -2; - if (gc_idx <= UNICODE_GC_Co) { - gc_mask = (uint64_t)1 << gc_idx; - } else { - gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; - } - return unicode_general_category1(cr, gc_mask); -} - - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_prop(CharRange *cr, const char *prop_name) -{ - int prop_idx, ret; - - prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); - if (prop_idx < 0) - return -2; - prop_idx += UNICODE_PROP_ASCII_Hex_Digit; - - ret = 0; - switch(prop_idx) { - case UNICODE_PROP_ASCII: - if (cr_add_interval(cr, 0x00, 0x7f + 1)) - return -1; - break; - case UNICODE_PROP_Any: - if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) - return -1; - break; - case UNICODE_PROP_Assigned: - ret = unicode_prop_ops(cr, - POP_GC, M(Cn), - POP_INVERT, - POP_END); - break; - case UNICODE_PROP_Math: - ret = unicode_prop_ops(cr, - POP_GC, M(Sm), - POP_PROP, UNICODE_PROP_Other_Math, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Lowercase: - ret = unicode_prop_ops(cr, - POP_GC, M(Ll), - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Uppercase: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Cased: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Alphabetic: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Alphabetic, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Grapheme_Base: - ret = unicode_prop_ops(cr, - POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), - POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, - POP_UNION, - POP_INVERT, - POP_END); - break; - case UNICODE_PROP_Grapheme_Extend: - ret = unicode_prop_ops(cr, - POP_GC, M(Me) | M(Mn), - POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_XID_Start: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_PROP, UNICODE_PROP_XID_Start1, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_XID_Continue: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | - M(Mn) | M(Mc) | M(Nd) | M(Pc), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_ID_Continue, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_PROP, UNICODE_PROP_XID_Continue1, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_Changes_When_Uppercased: - ret = unicode_case1(cr, CASE_U); - break; - case UNICODE_PROP_Changes_When_Lowercased: - ret = unicode_case1(cr, CASE_L); - break; - case UNICODE_PROP_Changes_When_Casemapped: - ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); - break; - case UNICODE_PROP_Changes_When_Titlecased: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_U, - POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, - POP_XOR, - POP_END); - break; - case UNICODE_PROP_Changes_When_Casefolded: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_F, - POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, - POP_XOR, - POP_END); - break; - case UNICODE_PROP_Changes_When_NFKC_Casefolded: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_F, - POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, - POP_XOR, - POP_END); - break; -#if 0 - case UNICODE_PROP_ID_Start: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_ID_Continue: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | - M(Mn) | M(Mc) | M(Nd) | M(Pc), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_ID_Continue, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_Case_Ignorable: - ret = unicode_prop_ops(cr, - POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk), - POP_PROP, UNICODE_PROP_Case_Ignorable1, - POP_XOR, - POP_END); - break; -#else - /* we use the existing tables */ - case UNICODE_PROP_ID_Continue: - ret = unicode_prop_ops(cr, - POP_PROP, UNICODE_PROP_ID_Start, - POP_PROP, UNICODE_PROP_ID_Continue1, - POP_XOR, - POP_END); - break; -#endif - default: - if (prop_idx >= countof(unicode_prop_table)) - return -2; - ret = unicode_prop1(cr, prop_idx); - break; - } - return ret; -} - -#endif /* CONFIG_ALL_UNICODE */ - -/*---- lre codepoint categorizing functions ----*/ - -#define S UNICODE_C_SPACE -#define D UNICODE_C_DIGIT -#define X UNICODE_C_XDIGIT -#define U UNICODE_C_UPPER -#define L UNICODE_C_LOWER -#define _ UNICODE_C_UNDER -#define d UNICODE_C_DOLLAR - -uint8_t const lre_ctype_bits[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, S, S, S, S, S, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - S, 0, 0, 0, d, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - X|D, X|D, X|D, X|D, X|D, X|D, X|D, X|D, - X|D, X|D, 0, 0, 0, 0, 0, 0, - - 0, X|U, X|U, X|U, X|U, X|U, X|U, U, - U, U, U, U, U, U, U, U, - U, U, U, U, U, U, U, U, - U, U, U, 0, 0, 0, 0, _, - - 0, X|L, X|L, X|L, X|L, X|L, X|L, L, - L, L, L, L, L, L, L, L, - L, L, L, L, L, L, L, L, - L, L, L, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - S, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -#undef S -#undef D -#undef X -#undef U -#undef L -#undef _ -#undef d - -/* code point ranges for Zs,Zl or Zp property */ -static const uint16_t char_range_s[] = { - 10, - 0x0009, 0x000D + 1, - 0x0020, 0x0020 + 1, - 0x00A0, 0x00A0 + 1, - 0x1680, 0x1680 + 1, - 0x2000, 0x200A + 1, - /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ - /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ - 0x2028, 0x2029 + 1, - 0x202F, 0x202F + 1, - 0x205F, 0x205F + 1, - 0x3000, 0x3000 + 1, - /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ - 0xFEFF, 0xFEFF + 1, -}; - -BOOL lre_is_space_non_ascii(uint32_t c) -{ - size_t i, n; - - n = countof(char_range_s); - for(i = 5; i < n; i += 2) { - uint32_t low = char_range_s[i]; - uint32_t high = char_range_s[i + 1]; - if (c < low) - return FALSE; - if (c < high) - return TRUE; - } - return FALSE; -} - -#define SEQ_MAX_LEN 16 - -static int unicode_sequence_prop1(int seq_prop_idx, UnicodeSequencePropCB *cb, void *opaque, - CharRange *cr) -{ - int i, c, j; - uint32_t seq[SEQ_MAX_LEN]; - - switch(seq_prop_idx) { - case UNICODE_SEQUENCE_PROP_Basic_Emoji: - if (unicode_prop1(cr, UNICODE_PROP_Basic_Emoji1) < 0) - return -1; - for(i = 0; i < cr->len; i += 2) { - for(c = cr->points[i]; c < cr->points[i + 1]; c++) { - seq[0] = c; - cb(opaque, seq, 1); - } - } - - cr->len = 0; - - if (unicode_prop1(cr, UNICODE_PROP_Basic_Emoji2) < 0) - return -1; - for(i = 0; i < cr->len; i += 2) { - for(c = cr->points[i]; c < cr->points[i + 1]; c++) { - seq[0] = c; - seq[1] = 0xfe0f; - cb(opaque, seq, 2); - } - } - - break; - case UNICODE_SEQUENCE_PROP_RGI_Emoji_Modifier_Sequence: - if (unicode_prop1(cr, UNICODE_PROP_Emoji_Modifier_Base) < 0) - return -1; - for(i = 0; i < cr->len; i += 2) { - for(c = cr->points[i]; c < cr->points[i + 1]; c++) { - for(j = 0; j < 5; j++) { - seq[0] = c; - seq[1] = 0x1f3fb + j; - cb(opaque, seq, 2); - } - } - } - break; - case UNICODE_SEQUENCE_PROP_RGI_Emoji_Flag_Sequence: - if (unicode_prop1(cr, UNICODE_PROP_RGI_Emoji_Flag_Sequence) < 0) - return -1; - for(i = 0; i < cr->len; i += 2) { - for(c = cr->points[i]; c < cr->points[i + 1]; c++) { - int c0, c1; - c0 = c / 26; - c1 = c % 26; - seq[0] = 0x1F1E6 + c0; - seq[1] = 0x1F1E6 + c1; - cb(opaque, seq, 2); - } - } - break; - case UNICODE_SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence: - { - int len, code, pres, k, mod, mod_count, mod_pos[2], hc_pos, n_mod, n_hc, mod1; - int mod_idx, hc_idx, i0, i1; - const uint8_t *tab = unicode_rgi_emoji_zwj_sequence; - - for(i = 0; i < countof(unicode_rgi_emoji_zwj_sequence);) { - len = tab[i++]; - k = 0; - mod = 0; - mod_count = 0; - hc_pos = -1; - for(j = 0; j < len; j++) { - code = tab[i++]; - code |= tab[i++] << 8; - pres = code >> 15; - mod1 = (code >> 13) & 3; - code &= 0x1fff; - if (code < 0x1000) { - c = code + 0x2000; - } else { - c = 0x1f000 + (code - 0x1000); - } - if (c == 0x1f9b0) - hc_pos = k; - seq[k++] = c; - if (mod1 != 0) { - assert(mod_count < 2); - mod = mod1; - mod_pos[mod_count++] = k; - seq[k++] = 0; /* will be filled later */ - } - if (pres) { - seq[k++] = 0xfe0f; - } - if (j < len - 1) { - seq[k++] = 0x200d; - } - } - - /* genrate all the variants */ - switch(mod) { - case 1: - n_mod = 5; - break; - case 2: - n_mod = 25; - break; - case 3: - n_mod = 20; - break; - default: - n_mod = 1; - break; - } - if (hc_pos >= 0) - n_hc = 4; - else - n_hc = 1; - for(hc_idx = 0; hc_idx < n_hc; hc_idx++) { - for(mod_idx = 0; mod_idx < n_mod; mod_idx++) { - if (hc_pos >= 0) - seq[hc_pos] = 0x1f9b0 + hc_idx; - - switch(mod) { - case 1: - seq[mod_pos[0]] = 0x1f3fb + mod_idx; - break; - case 2: - case 3: - i0 = mod_idx / 5; - i1 = mod_idx % 5; - /* avoid identical values */ - if (mod == 3 && i0 >= i1) - i0++; - seq[mod_pos[0]] = 0x1f3fb + i0; - seq[mod_pos[1]] = 0x1f3fb + i1; - break; - default: - break; - } -#if 0 - for(j = 0; j < k; j++) - printf(" %04x", seq[j]); - printf("\n"); -#endif - cb(opaque, seq, k); - } - } - } - } - break; - case UNICODE_SEQUENCE_PROP_RGI_Emoji_Tag_Sequence: - { - for(i = 0; i < countof(unicode_rgi_emoji_tag_sequence);) { - j = 0; - seq[j++] = 0x1F3F4; - for(;;) { - c = unicode_rgi_emoji_tag_sequence[i++]; - if (c == 0x00) - break; - seq[j++] = 0xe0000 + c; - } - seq[j++] = 0xe007f; - cb(opaque, seq, j); - } - } - break; - case UNICODE_SEQUENCE_PROP_Emoji_Keycap_Sequence: - if (unicode_prop1(cr, UNICODE_PROP_Emoji_Keycap_Sequence) < 0) - return -1; - for(i = 0; i < cr->len; i += 2) { - for(c = cr->points[i]; c < cr->points[i + 1]; c++) { - seq[0] = c; - seq[1] = 0xfe0f; - seq[2] = 0x20e3; - cb(opaque, seq, 3); - } - } - break; - case UNICODE_SEQUENCE_PROP_RGI_Emoji: - /* all prevous sequences */ - for(i = UNICODE_SEQUENCE_PROP_Basic_Emoji; i <= UNICODE_SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence; i++) { - int ret; - ret = unicode_sequence_prop1(i, cb, opaque, cr); - if (ret < 0) - return ret; - cr->len = 0; - } - break; - default: - return -2; - } - return 0; -} - -/* build a unicode sequence property */ -/* return -2 if not found, -1 if other error. 'cr' is used as temporary memory. */ -int unicode_sequence_prop(const char *prop_name, UnicodeSequencePropCB *cb, void *opaque, - CharRange *cr) -{ - int seq_prop_idx; - seq_prop_idx = unicode_find_name(unicode_sequence_prop_name_table, prop_name); - if (seq_prop_idx < 0) - return -2; - return unicode_sequence_prop1(seq_prop_idx, cb, opaque, cr); -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.h deleted file mode 100755 index 5d964e40..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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 - -/* define it to include all the unicode tables (40KB larger) */ -#define CONFIG_ALL_UNICODE - -#define LRE_CC_RES_LEN_MAX 3 - -/* 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, - CR_OP_SUB, -} 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_op(CharRange *cr, const uint32_t *a_pt, int a_len, - const uint32_t *b_pt, int b_len, int op); -int cr_op1(CharRange *cr, const uint32_t *b_pt, int b_len, int op); - -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_op1(cr, b_pt, 2, CR_OP_UNION); -} - -int cr_invert(CharRange *cr); - -int cr_regexp_canonicalize(CharRange *cr, int is_unicode); - -typedef enum { - UNICODE_NFC, - UNICODE_NFD, - UNICODE_NFKC, - UNICODE_NFKD, -} UnicodeNormalizationEnum; - -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, int is_ext); -int unicode_general_category(CharRange *cr, const char *gc_name); -int unicode_prop(CharRange *cr, const char *prop_name); - -typedef void UnicodeSequencePropCB(void *opaque, const uint32_t *buf, int len); -int unicode_sequence_prop(const char *prop_name, UnicodeSequencePropCB *cb, void *opaque, - CharRange *cr); - -int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); -int lre_canonicalize(uint32_t c, int is_unicode); - -/* Code point type categories */ -enum { - UNICODE_C_SPACE = (1 << 0), - UNICODE_C_DIGIT = (1 << 1), - UNICODE_C_UPPER = (1 << 2), - UNICODE_C_LOWER = (1 << 3), - UNICODE_C_UNDER = (1 << 4), - UNICODE_C_DOLLAR = (1 << 5), - UNICODE_C_XDIGIT = (1 << 6), -}; -extern uint8_t const lre_ctype_bits[256]; - -/* zero or non-zero return value */ -int lre_is_cased(uint32_t c); -int lre_is_case_ignorable(uint32_t c); -int lre_is_id_start(uint32_t c); -int lre_is_id_continue(uint32_t c); - -static inline int lre_is_space_byte(uint8_t c) { - return lre_ctype_bits[c] & UNICODE_C_SPACE; -} - -static inline int lre_is_id_start_byte(uint8_t c) { - return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER | - UNICODE_C_UNDER | UNICODE_C_DOLLAR); -} - -static inline int lre_is_id_continue_byte(uint8_t c) { - return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER | - UNICODE_C_UNDER | UNICODE_C_DOLLAR | - UNICODE_C_DIGIT); -} - -int lre_is_space_non_ascii(uint32_t c); - -static inline int lre_is_space(uint32_t c) { - if (c < 256) - return lre_is_space_byte(c); - else - return lre_is_space_non_ascii(c); -} - -static inline int lre_js_is_ident_first(uint32_t c) { - if (c < 128) { - return lre_is_id_start_byte(c); - } else { -#ifdef CONFIG_ALL_UNICODE - return lre_is_id_start(c); -#else - return !lre_is_space_non_ascii(c); -#endif - } -} - -static inline int lre_js_is_ident_next(uint32_t c) { - if (c < 128) { - return lre_is_id_continue_byte(c); - } else { - /* ZWNJ and ZWJ are accepted in identifiers */ - if (c >= 0x200C && c <= 0x200D) - return TRUE; -#ifdef CONFIG_ALL_UNICODE - return lre_is_id_continue(c); -#else - return !lre_is_space_non_ascii(c); -#endif - } -} - -#endif /* LIBUNICODE_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/list.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/list.h deleted file mode 100755 index 80983111..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/list.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 - -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) - -#endif /* LIST_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c deleted file mode 100755 index 0224f7cf..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * QuickJS stand alone interpreter - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#elif defined(__linux__) || defined(__GLIBC__) -#include -#elif defined(__FreeBSD__) -#include -#endif - -#include "cutils.h" -#include "quickjs-libc.h" - -extern const uint8_t qjsc_repl[]; -extern const uint32_t qjsc_repl_size; - -static int eval_buf(JSContext *ctx, const void *buf, int buf_len, - const char *filename, int eval_flags) -{ - JSValue val; - int ret; - - if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { - /* for the modules, we compile then run to be able to set - import.meta */ - val = JS_Eval(ctx, buf, buf_len, filename, - eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); - if (!JS_IsException(val)) { - js_module_set_import_meta(ctx, val, TRUE, TRUE); - val = JS_EvalFunction(ctx, val); - } - val = js_std_await(ctx, val); - } else { - val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); - } - if (JS_IsException(val)) { - js_std_dump_error(ctx); - ret = -1; - } else { - ret = 0; - } - JS_FreeValue(ctx, val); - return ret; -} - -static int eval_file(JSContext *ctx, const char *filename, int module, int strict) -{ - uint8_t *buf; - int ret, eval_flags; - size_t buf_len; - - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - perror(filename); - exit(1); - } - - if (module < 0) { - module = (has_suffix(filename, ".mjs") || - JS_DetectModule((const char *)buf, buf_len)); - } - if (module) { - eval_flags = JS_EVAL_TYPE_MODULE; - } else { - eval_flags = JS_EVAL_TYPE_GLOBAL; - if (strict) - eval_flags |= JS_EVAL_FLAG_STRICT; - } - ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); - js_free(ctx, buf); - return ret; -} - -/* also used to initialize the worker context */ -static JSContext *JS_NewCustomContext(JSRuntime *rt) -{ - JSContext *ctx; - ctx = JS_NewContext(rt); - if (!ctx) - return NULL; - /* system modules */ - js_init_module_std(ctx, "std"); - js_init_module_os(ctx, "os"); - return ctx; -} - -#if defined(__APPLE__) -#define MALLOC_OVERHEAD 0 -#else -#define MALLOC_OVERHEAD 8 -#endif - -struct trace_malloc_data { - uint8_t *base; -}; - -static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, - struct trace_malloc_data *dp) -{ - return ptr - dp->base; -} - -/* default memory allocation functions with memory limitation */ -static size_t js_trace_malloc_usable_size(const void *ptr) -{ -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize((void *)ptr); -#elif defined(EMSCRIPTEN) - return 0; -#elif defined(__linux__) || defined(__GLIBC__) - return malloc_usable_size((void *)ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size((void *)ptr); -#endif -} - -static void -#ifdef _WIN32 -/* mingw printf is used */ -__attribute__((format(gnu_printf, 2, 3))) -#else -__attribute__((format(printf, 2, 3))) -#endif - js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) -{ - va_list ap; - int c; - - va_start(ap, fmt); - while ((c = *fmt++) != '\0') { - if (c == '%') { - /* only handle %p and %zd */ - if (*fmt == 'p') { - uint8_t *ptr = va_arg(ap, void *); - if (ptr == NULL) { - printf("NULL"); - } else { - printf("H%+06lld.%zd", - js_trace_malloc_ptr_offset(ptr, s->opaque), - js_trace_malloc_usable_size(ptr)); - } - fmt++; - continue; - } - if (fmt[0] == 'z' && fmt[1] == 'd') { - size_t sz = va_arg(ap, size_t); - printf("%zd", sz); - fmt += 2; - continue; - } - } - putc(c, stdout); - } - va_end(ap); -} - -static void js_trace_malloc_init(struct trace_malloc_data *s) -{ - free(s->base = malloc(8)); -} - -static void *js_trace_malloc(JSMallocState *s, size_t size) -{ - void *ptr; - - /* Do not allocate zero bytes: behavior is platform dependent */ - assert(size != 0); - - if (unlikely(s->malloc_size + size > s->malloc_limit)) - return NULL; - ptr = malloc(size); - js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr); - if (ptr) { - s->malloc_count++; - s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - } - return ptr; -} - -static void js_trace_free(JSMallocState *s, void *ptr) -{ - if (!ptr) - return; - - js_trace_malloc_printf(s, "F %p\n", ptr); - s->malloc_count--; - s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); -} - -static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) -{ - size_t old_size; - - if (!ptr) { - if (size == 0) - return NULL; - return js_trace_malloc(s, size); - } - old_size = js_trace_malloc_usable_size(ptr); - if (size == 0) { - js_trace_malloc_printf(s, "R %zd %p\n", size, ptr); - s->malloc_count--; - s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); - return NULL; - } - if (s->malloc_size + size - old_size > s->malloc_limit) - return NULL; - - js_trace_malloc_printf(s, "R %zd %p", size, ptr); - - ptr = realloc(ptr, size); - js_trace_malloc_printf(s, " -> %p\n", ptr); - if (ptr) { - s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size; - } - return ptr; -} - -static const JSMallocFunctions trace_mf = { - js_trace_malloc, - js_trace_free, - js_trace_realloc, - js_trace_malloc_usable_size, -}; - -static size_t get_suffixed_size(const char *str) -{ - char *p; - size_t v; - v = (size_t)strtod(str, &p); - switch(*p) { - case 'G': - v <<= 30; - break; - case 'M': - v <<= 20; - break; - case 'k': - case 'K': - v <<= 10; - break; - default: - if (*p != '\0') { - fprintf(stderr, "qjs: invalid suffix: %s\n", p); - exit(1); - } - break; - } - return v; -} - -#define PROG_NAME "qjs" - -void help(void) -{ - printf("QuickJS version " CONFIG_VERSION "\n" - "usage: " PROG_NAME " [options] [file [args]]\n" - "-h --help list options\n" - "-e --eval EXPR evaluate EXPR\n" - "-i --interactive go to interactive mode\n" - "-m --module load as ES6 module (default=autodetect)\n" - " --script load as ES6 script (default=autodetect)\n" - " --strict force strict mode\n" - "-I --include file include an additional file\n" - " --std make 'std' and 'os' available to the loaded script\n" - "-T --trace trace memory allocation\n" - "-d --dump dump the memory usage stats\n" - " --memory-limit n limit the memory usage to 'n' bytes (SI suffixes allowed)\n" - " --stack-size n limit the stack size to 'n' bytes (SI suffixes allowed)\n" - " --no-unhandled-rejection ignore unhandled promise rejections\n" - "-s strip all the debug info\n" - " --strip-source strip the source code\n" - "-q --quit just instantiate the interpreter and quit\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - JSRuntime *rt; - JSContext *ctx; - struct trace_malloc_data trace_data = { NULL }; - int optind; - char *expr = NULL; - int interactive = 0; - int dump_memory = 0; - int trace_memory = 0; - int empty_run = 0; - int module = -1; - int strict = 0; - int load_std = 0; - int dump_unhandled_promise_rejection = 1; - size_t memory_limit = 0; - char *include_list[32]; - int i, include_count = 0; - int strip_flags = 0; - size_t stack_size = 0; - - /* cannot use getopt because we want to pass the command line to - the script */ - optind = 1; - while (optind < argc && *argv[optind] == '-') { - char *arg = argv[optind] + 1; - const char *longopt = ""; - /* a single - is not an option, it also stops argument scanning */ - if (!*arg) - break; - optind++; - if (*arg == '-') { - longopt = arg + 1; - arg += strlen(arg); - /* -- stops argument scanning */ - if (!*longopt) - break; - } - for (; *arg || *longopt; longopt = "") { - char opt = *arg; - if (opt) - arg++; - if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { - help(); - continue; - } - if (opt == 'e' || !strcmp(longopt, "eval")) { - if (*arg) { - expr = arg; - break; - } - if (optind < argc) { - expr = argv[optind++]; - break; - } - fprintf(stderr, "qjs: missing expression for -e\n"); - exit(2); - } - if (opt == 'I' || !strcmp(longopt, "include")) { - if (optind >= argc) { - fprintf(stderr, "expecting filename"); - exit(1); - } - if (include_count >= countof(include_list)) { - fprintf(stderr, "too many included files"); - exit(1); - } - include_list[include_count++] = argv[optind++]; - continue; - } - if (opt == 'i' || !strcmp(longopt, "interactive")) { - interactive++; - continue; - } - if (opt == 'm' || !strcmp(longopt, "module")) { - module = 1; - continue; - } - if (!strcmp(longopt, "script")) { - module = 0; - continue; - } - if (!strcmp(longopt, "strict")) { - strict = 1; - continue; - } - if (opt == 'd' || !strcmp(longopt, "dump")) { - dump_memory++; - continue; - } - if (opt == 'T' || !strcmp(longopt, "trace")) { - trace_memory++; - continue; - } - if (!strcmp(longopt, "std")) { - load_std = 1; - continue; - } - if (!strcmp(longopt, "no-unhandled-rejection")) { - dump_unhandled_promise_rejection = 0; - continue; - } - if (opt == 'q' || !strcmp(longopt, "quit")) { - empty_run++; - continue; - } - if (!strcmp(longopt, "memory-limit")) { - if (optind >= argc) { - fprintf(stderr, "expecting memory limit"); - exit(1); - } - memory_limit = get_suffixed_size(argv[optind++]); - continue; - } - if (!strcmp(longopt, "stack-size")) { - if (optind >= argc) { - fprintf(stderr, "expecting stack size"); - exit(1); - } - stack_size = get_suffixed_size(argv[optind++]); - continue; - } - if (opt == 's') { - strip_flags = JS_STRIP_DEBUG; - continue; - } - if (!strcmp(longopt, "strip-source")) { - strip_flags = JS_STRIP_SOURCE; - continue; - } - if (opt) { - fprintf(stderr, "qjs: unknown option '-%c'\n", opt); - } else { - fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); - } - help(); - } - } - - if (trace_memory) { - js_trace_malloc_init(&trace_data); - rt = JS_NewRuntime2(&trace_mf, &trace_data); - } else { - rt = JS_NewRuntime(); - } - if (!rt) { - fprintf(stderr, "qjs: cannot allocate JS runtime\n"); - exit(2); - } - if (memory_limit != 0) - JS_SetMemoryLimit(rt, memory_limit); - if (stack_size != 0) - JS_SetMaxStackSize(rt, stack_size); - JS_SetStripInfo(rt, strip_flags); - js_std_set_worker_new_context_func(JS_NewCustomContext); - js_std_init_handlers(rt); - ctx = JS_NewCustomContext(rt); - if (!ctx) { - fprintf(stderr, "qjs: cannot allocate JS context\n"); - exit(2); - } - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader, js_module_check_attributes, NULL); - - if (dump_unhandled_promise_rejection) { - JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, - NULL); - } - - if (!empty_run) { - js_std_add_helpers(ctx, argc - optind, argv + optind); - - /* make 'std' and 'os' visible to non module code */ - if (load_std) { - const char *str = "import * as std from 'std';\n" - "import * as os from 'os';\n" - "globalThis.std = std;\n" - "globalThis.os = os;\n"; - eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); - } - - for(i = 0; i < include_count; i++) { - if (eval_file(ctx, include_list[i], 0, strict)) - goto fail; - } - - if (expr) { - int eval_flags; - if (module > 0) { - eval_flags = JS_EVAL_TYPE_MODULE; - } else { - eval_flags = JS_EVAL_TYPE_GLOBAL; - if (strict) - eval_flags |= JS_EVAL_FLAG_STRICT; - } - if (eval_buf(ctx, expr, strlen(expr), "", eval_flags)) - goto fail; - } else - if (optind >= argc) { - /* interactive mode */ - interactive = 1; - } else { - const char *filename; - filename = argv[optind]; - if (eval_file(ctx, filename, module, strict)) - goto fail; - } - if (interactive) { - JS_SetHostPromiseRejectionTracker(rt, NULL, NULL); - js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); - } - js_std_loop(ctx); - } - - if (dump_memory) { - JSMemoryUsage stats; - JS_ComputeMemoryUsage(rt, &stats); - JS_DumpMemoryUsage(stdout, &stats, rt); - } - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - if (empty_run && dump_memory) { - clock_t t[5]; - double best[5]; - int i, j; - for (i = 0; i < 100; i++) { - t[0] = clock(); - rt = JS_NewRuntime(); - t[1] = clock(); - ctx = JS_NewContext(rt); - t[2] = clock(); - JS_FreeContext(ctx); - t[3] = clock(); - JS_FreeRuntime(rt); - t[4] = clock(); - for (j = 4; j > 0; j--) { - double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; - if (i == 0 || best[j] > ms) - best[j] = ms; - } - } - printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", - best[1] + best[2] + best[3] + best[4], - best[1], best[2], best[3], best[4]); - } - return 0; - fail: - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 1; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c deleted file mode 100755 index e55ca61c..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c +++ /dev/null @@ -1,876 +0,0 @@ -/* - * QuickJS command line compiler - * - * Copyright (c) 2018-2021 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#if !defined(_WIN32) -#include -#endif - -#include "cutils.h" -#include "quickjs-libc.h" - -typedef struct { - char *name; - char *short_name; - int flags; -} namelist_entry_t; - -typedef struct namelist_t { - namelist_entry_t *array; - int count; - int size; -} namelist_t; - -typedef struct { - const char *option_name; - const char *init_name; -} FeatureEntry; - -static namelist_t cname_list; -static namelist_t cmodule_list; -static namelist_t init_module_list; -static uint64_t feature_bitmap; -static FILE *outfile; -static BOOL byte_swap; -static BOOL dynamic_export; -static const char *c_ident_prefix = "qjsc_"; - -#define FE_ALL (-1) - -static const FeatureEntry feature_list[] = { - { "date", "Date" }, - { "eval", "Eval" }, - { "string-normalize", "StringNormalize" }, - { "regexp", "RegExp" }, - { "json", "JSON" }, - { "proxy", "Proxy" }, - { "map", "MapSet" }, - { "typedarray", "TypedArrays" }, - { "promise", "Promise" }, -#define FE_MODULE_LOADER 9 - { "module-loader", NULL }, - { "weakref", "WeakRef" }, -}; - -void namelist_add(namelist_t *lp, const char *name, const char *short_name, - int flags) -{ - namelist_entry_t *e; - if (lp->count == lp->size) { - size_t newsize = lp->size + (lp->size >> 1) + 4; - namelist_entry_t *a = - realloc(lp->array, sizeof(lp->array[0]) * newsize); - /* XXX: check for realloc failure */ - lp->array = a; - lp->size = newsize; - } - e = &lp->array[lp->count++]; - e->name = strdup(name); - if (short_name) - e->short_name = strdup(short_name); - else - e->short_name = NULL; - e->flags = flags; -} - -void namelist_free(namelist_t *lp) -{ - while (lp->count > 0) { - namelist_entry_t *e = &lp->array[--lp->count]; - free(e->name); - free(e->short_name); - } - free(lp->array); - lp->array = NULL; - lp->size = 0; -} - -namelist_entry_t *namelist_find(namelist_t *lp, const char *name) -{ - int i; - for(i = 0; i < lp->count; i++) { - namelist_entry_t *e = &lp->array[i]; - if (!strcmp(e->name, name)) - return e; - } - return NULL; -} - -static void get_c_name(char *buf, size_t buf_size, const char *file) -{ - const char *p, *r; - size_t len, i; - int c; - char *q; - - p = strrchr(file, '/'); - if (!p) - p = file; - else - p++; - r = strrchr(p, '.'); - if (!r) - len = strlen(p); - else - len = r - p; - pstrcpy(buf, buf_size, c_ident_prefix); - q = buf + strlen(buf); - for(i = 0; i < len; i++) { - c = p[i]; - if (!((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z'))) { - c = '_'; - } - if ((q - buf) < buf_size - 1) - *q++ = c; - } - *q = '\0'; -} - -static void dump_hex(FILE *f, const uint8_t *buf, size_t len) -{ - size_t i, col; - col = 0; - for(i = 0; i < len; i++) { - fprintf(f, " 0x%02x,", buf[i]); - if (++col == 8) { - fprintf(f, "\n"); - col = 0; - } - } - if (col != 0) - fprintf(f, "\n"); -} - -typedef enum { - CNAME_TYPE_SCRIPT, - CNAME_TYPE_MODULE, - CNAME_TYPE_JSON_MODULE, -} CNameTypeEnum; - -static void output_object_code(JSContext *ctx, - FILE *fo, JSValueConst obj, const char *c_name, - CNameTypeEnum c_name_type) -{ - uint8_t *out_buf; - size_t out_buf_len; - int flags; - - if (c_name_type == CNAME_TYPE_JSON_MODULE) - flags = 0; - else - flags = JS_WRITE_OBJ_BYTECODE; - if (byte_swap) - flags |= JS_WRITE_OBJ_BSWAP; - out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); - if (!out_buf) { - js_std_dump_error(ctx); - exit(1); - } - - namelist_add(&cname_list, c_name, NULL, c_name_type); - - fprintf(fo, "const uint32_t %s_size = %u;\n\n", - c_name, (unsigned int)out_buf_len); - fprintf(fo, "const uint8_t %s[%u] = {\n", - c_name, (unsigned int)out_buf_len); - dump_hex(fo, out_buf, out_buf_len); - fprintf(fo, "};\n\n"); - - js_free(ctx, out_buf); -} - -static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) -{ - /* should never be called when compiling JS code */ - abort(); -} - -static void find_unique_cname(char *cname, size_t cname_size) -{ - char cname1[1024]; - int suffix_num; - size_t len, max_len; - assert(cname_size >= 32); - /* find a C name not matching an existing module C name by - adding a numeric suffix */ - len = strlen(cname); - max_len = cname_size - 16; - if (len > max_len) - cname[max_len] = '\0'; - suffix_num = 1; - for(;;) { - snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); - if (!namelist_find(&cname_list, cname1)) - break; - suffix_num++; - } - pstrcpy(cname, cname_size, cname1); -} - -JSModuleDef *jsc_module_loader(JSContext *ctx, - const char *module_name, void *opaque, - JSValueConst attributes) -{ - JSModuleDef *m; - namelist_entry_t *e; - - /* check if it is a declared C or system module */ - e = namelist_find(&cmodule_list, module_name); - if (e) { - /* add in the static init module list */ - namelist_add(&init_module_list, e->name, e->short_name, 0); - /* create a dummy module */ - m = JS_NewCModule(ctx, module_name, js_module_dummy_init); - } else if (has_suffix(module_name, ".so")) { - fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name); - /* create a dummy module */ - m = JS_NewCModule(ctx, module_name, js_module_dummy_init); - /* the resulting executable will export its symbols for the - dynamic library */ - dynamic_export = TRUE; - } else { - size_t buf_len; - uint8_t *buf; - char cname[1024]; - int res; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - - res = js_module_test_json(ctx, attributes); - if (has_suffix(module_name, ".json") || res > 0) { - /* compile as JSON or JSON5 depending on "type" */ - JSValue val; - int flags; - - if (res == 2) - flags = JS_PARSE_JSON_EXT; - else - flags = 0; - val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags); - js_free(ctx, buf); - if (JS_IsException(val)) - return NULL; - /* create a dummy module */ - m = JS_NewCModule(ctx, module_name, js_module_dummy_init); - if (!m) { - JS_FreeValue(ctx, val); - return NULL; - } - - get_c_name(cname, sizeof(cname), module_name); - if (namelist_find(&cname_list, cname)) { - find_unique_cname(cname, sizeof(cname)); - } - - /* output the module name */ - fprintf(outfile, "static const uint8_t %s_module_name[] = {\n", - cname); - dump_hex(outfile, (const uint8_t *)module_name, strlen(module_name) + 1); - fprintf(outfile, "};\n\n"); - - output_object_code(ctx, outfile, val, cname, CNAME_TYPE_JSON_MODULE); - JS_FreeValue(ctx, val); - } else { - JSValue func_val; - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - get_c_name(cname, sizeof(cname), module_name); - if (namelist_find(&cname_list, cname)) { - find_unique_cname(cname, sizeof(cname)); - } - output_object_code(ctx, outfile, func_val, cname, CNAME_TYPE_MODULE); - - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - } - } - return m; -} - -static void compile_file(JSContext *ctx, FILE *fo, - const char *filename, - const char *c_name1, - int module) -{ - uint8_t *buf; - char c_name[1024]; - int eval_flags; - JSValue obj; - size_t buf_len; - - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - fprintf(stderr, "Could not load '%s'\n", filename); - exit(1); - } - eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; - if (module < 0) { - module = (has_suffix(filename, ".mjs") || - JS_DetectModule((const char *)buf, buf_len)); - } - if (module) - eval_flags |= JS_EVAL_TYPE_MODULE; - else - eval_flags |= JS_EVAL_TYPE_GLOBAL; - obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags); - if (JS_IsException(obj)) { - js_std_dump_error(ctx); - exit(1); - } - js_free(ctx, buf); - if (c_name1) { - pstrcpy(c_name, sizeof(c_name), c_name1); - } else { - get_c_name(c_name, sizeof(c_name), filename); - if (namelist_find(&cname_list, c_name)) { - find_unique_cname(c_name, sizeof(c_name)); - } - } - output_object_code(ctx, fo, obj, c_name, CNAME_TYPE_SCRIPT); - JS_FreeValue(ctx, obj); -} - -static const char main_c_template1[] = - "int main(int argc, char **argv)\n" - "{\n" - " JSRuntime *rt;\n" - " JSContext *ctx;\n" - " rt = JS_NewRuntime();\n" - " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" - " js_std_init_handlers(rt);\n" - ; - -static const char main_c_template2[] = - " js_std_loop(ctx);\n" - " js_std_free_handlers(rt);\n" - " JS_FreeContext(ctx);\n" - " JS_FreeRuntime(rt);\n" - " return 0;\n" - "}\n"; - -#define PROG_NAME "qjsc" - -void help(void) -{ - printf("QuickJS Compiler version " CONFIG_VERSION "\n" - "usage: " PROG_NAME " [options] [files]\n" - "\n" - "options are:\n" - "-c only output bytecode to a C file\n" - "-e output main() and bytecode to a C file (default = executable output)\n" - "-o output set the output filename\n" - "-N cname set the C name of the generated data\n" - "-m compile as Javascript module (default=autodetect)\n" - "-D module_name compile a dynamically loaded module or worker\n" - "-M module_name[,cname] add initialization code for an external C module\n" - "-x byte swapped output\n" - "-p prefix set the prefix of the generated C names\n" - "-S n set the maximum stack size to 'n' bytes (default=%d)\n" - "-s strip all the debug info\n" - "--keep-source keep the source code\n", - JS_DEFAULT_STACK_SIZE); -#ifdef CONFIG_LTO - { - int i; - printf("-flto use link time optimization\n"); - printf("-fno-["); - for(i = 0; i < countof(feature_list); i++) { - if (i != 0) - printf("|"); - printf("%s", feature_list[i].option_name); - } - printf("]\n" - " disable selected language features (smaller code size)\n"); - } -#endif - exit(1); -} - -#if defined(CONFIG_CC) && !defined(_WIN32) - -int exec_cmd(char **argv) -{ - int pid, status, ret; - - pid = fork(); - if (pid == 0) { - execvp(argv[0], argv); - exit(1); - } - - for(;;) { - ret = waitpid(pid, &status, 0); - if (ret == pid && WIFEXITED(status)) - break; - } - return WEXITSTATUS(status); -} - -static int output_executable(const char *out_filename, const char *cfilename, - BOOL use_lto, BOOL verbose, const char *exename) -{ - const char *argv[64]; - const char **arg, *bn_suffix, *lto_suffix; - char libjsname[1024]; - char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p; - int ret; - - /* get the directory of the executable */ - pstrcpy(exe_dir, sizeof(exe_dir), exename); - p = strrchr(exe_dir, '/'); - if (p) { - *p = '\0'; - } else { - pstrcpy(exe_dir, sizeof(exe_dir), "."); - } - - /* if 'quickjs.h' is present at the same path as the executable, we - use it as include and lib directory */ - snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir); - if (access(buf, R_OK) == 0) { - pstrcpy(inc_dir, sizeof(inc_dir), exe_dir); - pstrcpy(lib_dir, sizeof(lib_dir), exe_dir); - } else { - snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX); - snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX); - } - - lto_suffix = ""; - bn_suffix = ""; - - arg = argv; - *arg++ = CONFIG_CC; - *arg++ = "-O2"; -#ifdef CONFIG_LTO - if (use_lto) { - *arg++ = "-flto"; - lto_suffix = ".lto"; - } -#endif - /* XXX: use the executable path to find the includes files and - libraries */ - *arg++ = "-D"; - *arg++ = "_GNU_SOURCE"; - *arg++ = "-I"; - *arg++ = inc_dir; - *arg++ = "-o"; - *arg++ = out_filename; - if (dynamic_export) - *arg++ = "-rdynamic"; - *arg++ = cfilename; - snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a", - lib_dir, bn_suffix, lto_suffix); - *arg++ = libjsname; - *arg++ = "-lm"; - *arg++ = "-ldl"; - *arg++ = "-lpthread"; - *arg = NULL; - - if (verbose) { - for(arg = argv; *arg != NULL; arg++) - printf("%s ", *arg); - printf("\n"); - } - - ret = exec_cmd((char **)argv); - unlink(cfilename); - return ret; -} -#else -static int output_executable(const char *out_filename, const char *cfilename, - BOOL use_lto, BOOL verbose, const char *exename) -{ - fprintf(stderr, "Executable output is not supported for this target\n"); - exit(1); - return 0; -} -#endif - -static size_t get_suffixed_size(const char *str) -{ - char *p; - size_t v; - v = (size_t)strtod(str, &p); - switch(*p) { - case 'G': - v <<= 30; - break; - case 'M': - v <<= 20; - break; - case 'k': - case 'K': - v <<= 10; - break; - default: - if (*p != '\0') { - fprintf(stderr, "qjs: invalid suffix: %s\n", p); - exit(1); - } - break; - } - return v; -} - -typedef enum { - OUTPUT_C, - OUTPUT_C_MAIN, - OUTPUT_EXECUTABLE, -} OutputTypeEnum; - -static const char *get_short_optarg(int *poptind, int opt, - const char *arg, int argc, char **argv) -{ - const char *optarg; - if (*arg) { - optarg = arg; - } else if (*poptind < argc) { - optarg = argv[(*poptind)++]; - } else { - fprintf(stderr, "qjsc: expecting parameter for -%c\n", opt); - exit(1); - } - return optarg; -} - -int main(int argc, char **argv) -{ - int i, verbose, strip_flags; - const char *out_filename, *cname; - char cfilename[1024]; - FILE *fo; - JSRuntime *rt; - JSContext *ctx; - BOOL use_lto; - int module; - OutputTypeEnum output_type; - size_t stack_size; - namelist_t dynamic_module_list; - - out_filename = NULL; - output_type = OUTPUT_EXECUTABLE; - cname = NULL; - feature_bitmap = FE_ALL; - module = -1; - byte_swap = FALSE; - verbose = 0; - strip_flags = JS_STRIP_SOURCE; - use_lto = FALSE; - stack_size = 0; - memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); - - /* add system modules */ - namelist_add(&cmodule_list, "std", "std", 0); - namelist_add(&cmodule_list, "os", "os", 0); - - optind = 1; - while (optind < argc && *argv[optind] == '-') { - char *arg = argv[optind] + 1; - const char *longopt = ""; - const char *optarg; - /* a single - is not an option, it also stops argument scanning */ - if (!*arg) - break; - optind++; - if (*arg == '-') { - longopt = arg + 1; - arg += strlen(arg); - /* -- stops argument scanning */ - if (!*longopt) - break; - } - for (; *arg || *longopt; longopt = "") { - char opt = *arg; - if (opt) - arg++; - if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { - help(); - continue; - } - if (opt == 'o') { - out_filename = get_short_optarg(&optind, opt, arg, argc, argv); - break; - } - if (opt == 'c') { - output_type = OUTPUT_C; - continue; - } - if (opt == 'e') { - output_type = OUTPUT_C_MAIN; - continue; - } - if (opt == 'N') { - cname = get_short_optarg(&optind, opt, arg, argc, argv); - break; - } - if (opt == 'f') { - const char *p; - optarg = get_short_optarg(&optind, opt, arg, argc, argv); - p = optarg; - if (!strcmp(p, "lto")) { - use_lto = TRUE; - } else if (strstart(p, "no-", &p)) { - use_lto = TRUE; - for(i = 0; i < countof(feature_list); i++) { - if (!strcmp(p, feature_list[i].option_name)) { - feature_bitmap &= ~((uint64_t)1 << i); - break; - } - } - if (i == countof(feature_list)) - goto bad_feature; - } else { - bad_feature: - fprintf(stderr, "unsupported feature: %s\n", optarg); - exit(1); - } - break; - } - if (opt == 'm') { - module = 1; - continue; - } - if (opt == 'M') { - char *p; - char path[1024]; - char cname[1024]; - - optarg = get_short_optarg(&optind, opt, arg, argc, argv); - pstrcpy(path, sizeof(path), optarg); - p = strchr(path, ','); - if (p) { - *p = '\0'; - pstrcpy(cname, sizeof(cname), p + 1); - } else { - get_c_name(cname, sizeof(cname), path); - } - namelist_add(&cmodule_list, path, cname, 0); - break; - } - if (opt == 'D') { - optarg = get_short_optarg(&optind, opt, arg, argc, argv); - namelist_add(&dynamic_module_list, optarg, NULL, 0); - break; - } - if (opt == 'x') { - byte_swap = 1; - continue; - } - if (opt == 'v') { - verbose++; - continue; - } - if (opt == 'p') { - c_ident_prefix = get_short_optarg(&optind, opt, arg, argc, argv); - break; - } - if (opt == 'S') { - optarg = get_short_optarg(&optind, opt, arg, argc, argv); - stack_size = get_suffixed_size(optarg); - break; - } - if (opt == 's') { - strip_flags = JS_STRIP_DEBUG; - continue; - } - if (!strcmp(longopt, "keep-source")) { - strip_flags = 0; - continue; - } - if (opt) { - fprintf(stderr, "qjsc: unknown option '-%c'\n", opt); - } else { - fprintf(stderr, "qjsc: unknown option '--%s'\n", longopt); - } - help(); - } - } - - if (optind >= argc) - help(); - - if (!out_filename) { - if (output_type == OUTPUT_EXECUTABLE) { - out_filename = "a.out"; - } else { - out_filename = "out.c"; - } - } - - if (output_type == OUTPUT_EXECUTABLE) { -#if defined(_WIN32) || defined(__ANDROID__) - /* XXX: find a /tmp directory ? */ - snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid()); -#else - snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid()); -#endif - } else { - pstrcpy(cfilename, sizeof(cfilename), out_filename); - } - - fo = fopen(cfilename, "w"); - if (!fo) { - perror(cfilename); - exit(1); - } - outfile = fo; - - rt = JS_NewRuntime(); - ctx = JS_NewContext(rt); - - JS_SetStripInfo(rt, strip_flags); - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc2(rt, NULL, jsc_module_loader, NULL, NULL); - - fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" - "\n" - ); - - if (output_type != OUTPUT_C) { - fprintf(fo, "#include \"quickjs-libc.h\"\n" - "\n" - ); - } else { - fprintf(fo, "#include \n" - "\n" - ); - } - - for(i = optind; i < argc; i++) { - const char *filename = argv[i]; - compile_file(ctx, fo, filename, cname, module); - cname = NULL; - } - - for(i = 0; i < dynamic_module_list.count; i++) { - if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL, JS_UNDEFINED)) { - fprintf(stderr, "Could not load dynamic module '%s'\n", - dynamic_module_list.array[i].name); - exit(1); - } - } - - if (output_type != OUTPUT_C) { - fprintf(fo, - "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" - "{\n" - " JSContext *ctx = JS_NewContextRaw(rt);\n" - " if (!ctx)\n" - " return NULL;\n"); - /* add the basic objects */ - fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n"); - for(i = 0; i < countof(feature_list); i++) { - if ((feature_bitmap & ((uint64_t)1 << i)) && - feature_list[i].init_name) { - fprintf(fo, " JS_AddIntrinsic%s(ctx);\n", - feature_list[i].init_name); - } - } - /* add the precompiled modules (XXX: could modify the module - loader instead) */ - for(i = 0; i < init_module_list.count; i++) { - namelist_entry_t *e = &init_module_list.array[i]; - /* initialize the static C modules */ - - fprintf(fo, - " {\n" - " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" - " js_init_module_%s(ctx, \"%s\");\n" - " }\n", - e->short_name, e->short_name, e->name); - } - for(i = 0; i < cname_list.count; i++) { - namelist_entry_t *e = &cname_list.array[i]; - if (e->flags == CNAME_TYPE_MODULE) { - fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n", - e->name, e->name); - } else if (e->flags == CNAME_TYPE_JSON_MODULE) { - fprintf(fo, " js_std_eval_binary_json_module(ctx, %s, %s_size, (const char *)%s_module_name);\n", - e->name, e->name, e->name); - } - } - fprintf(fo, - " return ctx;\n" - "}\n\n"); - - fputs(main_c_template1, fo); - - if (stack_size != 0) { - fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n", - (unsigned int)stack_size); - } - - /* add the module loader if necessary */ - if (feature_bitmap & (1 << FE_MODULE_LOADER)) { - fprintf(fo, " JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader, js_module_check_attributes, NULL);\n"); - } - - fprintf(fo, - " ctx = JS_NewCustomContext(rt);\n" - " js_std_add_helpers(ctx, argc, argv);\n"); - - for(i = 0; i < cname_list.count; i++) { - namelist_entry_t *e = &cname_list.array[i]; - if (e->flags == CNAME_TYPE_SCRIPT) { - fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", - e->name, e->name); - } - } - fputs(main_c_template2, fo); - } - - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - fclose(fo); - - if (output_type == OUTPUT_EXECUTABLE) { - return output_executable(out_filename, cfilename, use_lto, verbose, - argv[0]); - } - namelist_free(&cname_list); - namelist_free(&cmodule_list); - namelist_free(&init_module_list); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h deleted file mode 100755 index dac2df67..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h +++ /dev/null @@ -1,278 +0,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(fileName, "fileName") -DEF(lineNumber, "lineNumber") -DEF(columnNumber, "columnNumber") -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(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(minus_zero, "-0") -DEF(Infinity, "Infinity") -DEF(minus_Infinity, "-Infinity") -DEF(NaN, "NaN") -DEF(hasIndices, "hasIndices") -DEF(ignoreCase, "ignoreCase") -DEF(multiline, "multiline") -DEF(dotAll, "dotAll") -DEF(sticky, "sticky") -DEF(unicodeSets, "unicodeSets") -/* the following 3 atoms are only used with CONFIG_ATOMICS */ -DEF(not_equal, "not-equal") -DEF(timed_out, "timed-out") -DEF(ok, "ok") -/* */ -DEF(toJSON, "toJSON") -DEF(maxByteLength, "maxByteLength") -/* 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(IteratorHelper, "Iterator Helper") -DEF(IteratorConcat, "Iterator Concat") -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(AggregateError, "AggregateError") -/* 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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c deleted file mode 100755 index c24b6d53..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c +++ /dev/null @@ -1,4362 +0,0 @@ -/* - * QuickJS C library - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(_WIN32) -#include -#include -#include -#else -#include -#include -#include -#include - -#if defined(__FreeBSD__) -extern char **environ; -#endif - -#if defined(__APPLE__) || defined(__FreeBSD__) -typedef sig_t sighandler_t; -#endif - -#if defined(__APPLE__) -#if !defined(environ) -#include -#define environ (*_NSGetEnviron()) -#endif -#endif /* __APPLE__ */ - -#endif - -/* enable the os.Worker API. It relies on POSIX threads */ -#define USE_WORKER - -#ifdef USE_WORKER -#include -#include -#endif - -#include "cutils.h" -#include "list.h" -#include "quickjs-libc.h" - -#if !defined(PATH_MAX) -#define PATH_MAX 4096 -#endif - -/* TODO: - - add socket calls -*/ - -typedef struct { - struct list_head link; - int fd; - JSValue rw_func[2]; -} JSOSRWHandler; - -typedef struct { - struct list_head link; - int sig_num; - JSValue func; -} JSOSSignalHandler; - -typedef struct { - struct list_head link; - int timer_id; - int64_t timeout; - JSValue func; -} JSOSTimer; - -typedef struct { - struct list_head link; - uint8_t *data; - size_t data_len; - /* list of SharedArrayBuffers, necessary to free the message */ - uint8_t **sab_tab; - size_t sab_tab_len; -} JSWorkerMessage; - -typedef struct JSWaker { -#ifdef _WIN32 - HANDLE handle; -#else - int read_fd; - int write_fd; -#endif -} JSWaker; - -typedef struct { - int ref_count; -#ifdef USE_WORKER - pthread_mutex_t mutex; -#endif - struct list_head msg_queue; /* list of JSWorkerMessage.link */ - JSWaker waker; -} JSWorkerMessagePipe; - -typedef struct { - struct list_head link; - JSWorkerMessagePipe *recv_pipe; - JSValue on_message_func; -} JSWorkerMessageHandler; - -typedef struct { - struct list_head link; - JSValue promise; - JSValue reason; -} JSRejectedPromiseEntry; - -typedef struct JSThreadState { - struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ - struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ - struct list_head os_timers; /* list of JSOSTimer.link */ - struct list_head port_list; /* list of JSWorkerMessageHandler.link */ - struct list_head rejected_promise_list; /* list of JSRejectedPromiseEntry.link */ - int eval_script_recurse; /* only used in the main thread */ - int next_timer_id; /* for setTimeout() */ - /* not used in the main thread */ - JSWorkerMessagePipe *recv_pipe, *send_pipe; -} JSThreadState; - -static uint64_t os_pending_signals; -static int (*os_poll_func)(JSContext *ctx); - -static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) -{ - dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt); -} - -static BOOL my_isdigit(int c) -{ - return (c >= '0' && c <= '9'); -} - -/* XXX: use 'o' and 'O' for object using JS_PrintValue() ? */ -static JSValue js_printf_internal(JSContext *ctx, - int argc, JSValueConst *argv, FILE *fp) -{ - char fmtbuf[32]; - uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; - JSValue res; - DynBuf dbuf; - const char *fmt_str = NULL; - const uint8_t *fmt, *fmt_end; - const uint8_t *p; - char *q; - int i, c, len, mod; - size_t fmt_len; - int32_t int32_arg; - int64_t int64_arg; - double double_arg; - const char *string_arg; - /* Use indirect call to dbuf_printf to prevent gcc warning */ - int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf; - - js_std_dbuf_init(ctx, &dbuf); - - if (argc > 0) { - fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); - if (!fmt_str) - goto fail; - - i = 1; - fmt = (const uint8_t *)fmt_str; - fmt_end = fmt + fmt_len; - while (fmt < fmt_end) { - for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) - continue; - dbuf_put(&dbuf, p, fmt - p); - if (fmt >= fmt_end) - break; - q = fmtbuf; - *q++ = *fmt++; /* copy '%' */ - - /* flags */ - for(;;) { - c = *fmt; - if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' || - c == '\'') { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = c; - fmt++; - } else { - break; - } - } - /* width */ - if (*fmt == '*') { - if (i >= argc) - goto missing; - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); - fmt++; - } else { - while (my_isdigit(*fmt)) { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - } - } - if (*fmt == '.') { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - if (*fmt == '*') { - if (i >= argc) - goto missing; - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); - fmt++; - } else { - while (my_isdigit(*fmt)) { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - } - } - } - - /* we only support the "l" modifier for 64 bit numbers */ - mod = ' '; - if (*fmt == 'l') { - mod = *fmt++; - } - - /* type */ - c = *fmt++; - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = c; - *q = '\0'; - - switch (c) { - case 'c': - if (i >= argc) - goto missing; - if (JS_IsString(argv[i])) { - string_arg = JS_ToCString(ctx, argv[i++]); - if (!string_arg) - goto fail; - int32_arg = unicode_from_utf8((const uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); - JS_FreeCString(ctx, string_arg); - } else { - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - } - /* handle utf-8 encoding explicitly */ - if ((unsigned)int32_arg > 0x10FFFF) - int32_arg = 0xFFFD; - /* ignore conversion flags, width and precision */ - len = unicode_to_utf8(cbuf, int32_arg); - dbuf_put(&dbuf, cbuf, len); - break; - - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - if (i >= argc) - goto missing; - if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++])) - goto fail; - if (mod == 'l') { - /* 64 bit number */ -#if defined(_WIN32) - if (q >= fmtbuf + sizeof(fmtbuf) - 3) - goto invalid; - q[2] = q[-1]; - q[-1] = 'I'; - q[0] = '6'; - q[1] = '4'; - q[3] = '\0'; - dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg); -#else - if (q >= fmtbuf + sizeof(fmtbuf) - 2) - goto invalid; - q[1] = q[-1]; - q[-1] = q[0] = 'l'; - q[2] = '\0'; - dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); -#endif - } else { - dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg); - } - break; - - case 's': - if (i >= argc) - goto missing; - /* XXX: handle strings containing null characters */ - string_arg = JS_ToCString(ctx, argv[i++]); - if (!string_arg) - goto fail; - dbuf_printf_fun(&dbuf, fmtbuf, string_arg); - JS_FreeCString(ctx, string_arg); - break; - - case 'e': - case 'f': - case 'g': - case 'a': - case 'E': - case 'F': - case 'G': - case 'A': - if (i >= argc) - goto missing; - if (JS_ToFloat64(ctx, &double_arg, argv[i++])) - goto fail; - dbuf_printf_fun(&dbuf, fmtbuf, double_arg); - break; - - case '%': - dbuf_putc(&dbuf, '%'); - break; - - default: - /* XXX: should support an extension mechanism */ - invalid: - JS_ThrowTypeError(ctx, "invalid conversion specifier in format string"); - goto fail; - missing: - JS_ThrowReferenceError(ctx, "missing argument for conversion specifier"); - goto fail; - } - } - JS_FreeCString(ctx, fmt_str); - } - if (dbuf.error) { - res = JS_ThrowOutOfMemory(ctx); - } else { - if (fp) { - len = fwrite(dbuf.buf, 1, dbuf.size, fp); - res = JS_NewInt32(ctx, len); - } else { - res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); - } - } - dbuf_free(&dbuf); - return res; - -fail: - JS_FreeCString(ctx, fmt_str); - dbuf_free(&dbuf); - return JS_EXCEPTION; -} - -uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) -{ - FILE *f; - uint8_t *buf; - size_t buf_len; - long lret; - - f = fopen(filename, "rb"); - if (!f) - return NULL; - if (fseek(f, 0, SEEK_END) < 0) - goto fail; - lret = ftell(f); - if (lret < 0) - goto fail; - /* XXX: on Linux, ftell() return LONG_MAX for directories */ - if (lret == LONG_MAX) { - errno = EISDIR; - goto fail; - } - buf_len = lret; - if (fseek(f, 0, SEEK_SET) < 0) - goto fail; - if (ctx) - buf = js_malloc(ctx, buf_len + 1); - else - buf = malloc(buf_len + 1); - if (!buf) - goto fail; - if (fread(buf, 1, buf_len, f) != buf_len) { - errno = EIO; - if (ctx) - js_free(ctx, buf); - else - free(buf); - fail: - fclose(f); - return NULL; - } - buf[buf_len] = '\0'; - fclose(f); - *pbuf_len = buf_len; - return buf; -} - -/* load and evaluate a file */ -static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint8_t *buf; - const char *filename; - JSValue ret; - size_t buf_len; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load '%s'", filename); - JS_FreeCString(ctx, filename); - return JS_EXCEPTION; - } - ret = JS_Eval(ctx, (char *)buf, buf_len, filename, - JS_EVAL_TYPE_GLOBAL); - js_free(ctx, buf); - JS_FreeCString(ctx, filename); - return ret; -} - -/* load a file as a UTF-8 encoded string */ -static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint8_t *buf; - const char *filename; - JSValue ret; - size_t buf_len; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - buf = js_load_file(ctx, &buf_len, filename); - JS_FreeCString(ctx, filename); - if (!buf) - return JS_NULL; - ret = JS_NewStringLen(ctx, (char *)buf, buf_len); - js_free(ctx, buf); - return ret; -} - -typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, - const char *module_name); - - -#if defined(_WIN32) -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); - return NULL; -} -#else -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JSModuleDef *m; - void *hd; - JSInitModuleFunc *init; - char *filename; - - if (!strchr(module_name, '/')) { - /* must add a '/' so that the DLL is not searched in the - system library paths */ - filename = js_malloc(ctx, strlen(module_name) + 2 + 1); - if (!filename) - return NULL; - strcpy(filename, "./"); - strcpy(filename + 2, module_name); - } else { - filename = (char *)module_name; - } - - /* C module */ - hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); - if (filename != module_name) - js_free(ctx, filename); - if (!hd) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library", - module_name); - goto fail; - } - - init = dlsym(hd, "js_init_module"); - if (!init) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", - module_name); - goto fail; - } - - m = init(ctx, module_name); - if (!m) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", - module_name); - fail: - if (hd) - dlclose(hd); - return NULL; - } - return m; -} -#endif /* !_WIN32 */ - -int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - JS_BOOL use_realpath, JS_BOOL is_main) -{ - JSModuleDef *m; - char buf[PATH_MAX + 16]; - JSValue meta_obj; - JSAtom module_name_atom; - const char *module_name; - - assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); - m = JS_VALUE_GET_PTR(func_val); - - module_name_atom = JS_GetModuleName(ctx, m); - module_name = JS_AtomToCString(ctx, module_name_atom); - JS_FreeAtom(ctx, module_name_atom); - if (!module_name) - return -1; - if (!strchr(module_name, ':')) { - strcpy(buf, "file://"); -#if !defined(_WIN32) - /* realpath() cannot be used with modules compiled with qjsc - because the corresponding module source code is not - necessarily present */ - if (use_realpath) { - char *res = realpath(module_name, buf + strlen(buf)); - if (!res) { - JS_ThrowTypeError(ctx, "realpath failure"); - JS_FreeCString(ctx, module_name); - return -1; - } - } else -#endif - { - pstrcat(buf, sizeof(buf), module_name); - } - } else { - pstrcpy(buf, sizeof(buf), module_name); - } - JS_FreeCString(ctx, module_name); - - meta_obj = JS_GetImportMeta(ctx, m); - if (JS_IsException(meta_obj)) - return -1; - JS_DefinePropertyValueStr(ctx, meta_obj, "url", - JS_NewString(ctx, buf), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, meta_obj, "main", - JS_NewBool(ctx, is_main), - JS_PROP_C_W_E); - JS_FreeValue(ctx, meta_obj); - return 0; -} - -static int json_module_init(JSContext *ctx, JSModuleDef *m) -{ - JSValue val; - val = JS_GetModulePrivateValue(ctx, m); - JS_SetModuleExport(ctx, m, "default", val); - return 0; -} - -static JSModuleDef *create_json_module(JSContext *ctx, const char *module_name, JSValue val) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, json_module_init); - if (!m) { - JS_FreeValue(ctx, val); - return NULL; - } - /* only export the "default" symbol which will contain the JSON object */ - JS_AddModuleExport(ctx, m, "default"); - JS_SetModulePrivateValue(ctx, m, val); - return m; -} - -/* in order to conform with the specification, only the keys should be - tested and not the associated values. */ -int js_module_check_attributes(JSContext *ctx, void *opaque, - JSValueConst attributes) -{ - JSPropertyEnum *tab; - uint32_t i, len; - int ret; - const char *cstr; - size_t cstr_len; - - if (JS_GetOwnPropertyNames(ctx, &tab, &len, attributes, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK)) - return -1; - ret = 0; - for(i = 0; i < len; i++) { - cstr = JS_AtomToCStringLen(ctx, &cstr_len, tab[i].atom); - if (!cstr) { - ret = -1; - break; - } - if (!(cstr_len == 4 && !memcmp(cstr, "type", cstr_len))) { - JS_ThrowTypeError(ctx, "import attribute '%s' is not supported", cstr); - ret = -1; - } - JS_FreeCString(ctx, cstr); - if (ret) - break; - } - JS_FreePropertyEnum(ctx, tab, len); - return ret; -} - -/* return > 0 if the attributes indicate a JSON module */ -int js_module_test_json(JSContext *ctx, JSValueConst attributes) -{ - JSValue str; - const char *cstr; - size_t len; - BOOL res; - - if (JS_IsUndefined(attributes)) - return FALSE; - str = JS_GetPropertyStr(ctx, attributes, "type"); - if (!JS_IsString(str)) - return FALSE; - cstr = JS_ToCStringLen(ctx, &len, str); - JS_FreeValue(ctx, str); - if (!cstr) - return FALSE; - /* XXX: raise an error if unknown type ? */ - if (len == 4 && !memcmp(cstr, "json", len)) { - res = 1; - } else if (len == 5 && !memcmp(cstr, "json5", len)) { - res = 2; - } else { - res = 0; - } - JS_FreeCString(ctx, cstr); - return res; -} - -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque, - JSValueConst attributes) -{ - JSModuleDef *m; - int res; - - if (has_suffix(module_name, ".so")) { - m = js_module_loader_so(ctx, module_name); - } else { - size_t buf_len; - uint8_t *buf; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - res = js_module_test_json(ctx, attributes); - if (has_suffix(module_name, ".json") || res > 0) { - /* compile as JSON or JSON5 depending on "type" */ - JSValue val; - int flags; - if (res == 2) - flags = JS_PARSE_JSON_EXT; - else - flags = 0; - val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags); - js_free(ctx, buf); - if (JS_IsException(val)) - return NULL; - m = create_json_module(ctx, module_name, val); - if (!m) - return NULL; - } else { - JSValue func_val; - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - /* XXX: could propagate the exception */ - js_module_set_import_meta(ctx, func_val, TRUE, FALSE); - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - } - } - return m; -} - -static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int status; - if (JS_ToInt32(ctx, &status, argv[0])) - status = -1; - exit(status); - return JS_UNDEFINED; -} - -static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name, *str; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - str = getenv(name); - JS_FreeCString(ctx, name); - if (!str) - return JS_UNDEFINED; - else - return JS_NewString(ctx, str); -} - -#if defined(_WIN32) -static void setenv(const char *name, const char *value, int overwrite) -{ - char *str; - size_t name_len, value_len; - name_len = strlen(name); - value_len = strlen(value); - str = malloc(name_len + 1 + value_len + 1); - memcpy(str, name, name_len); - str[name_len] = '='; - memcpy(str + name_len + 1, value, value_len); - str[name_len + 1 + value_len] = '\0'; - _putenv(str); - free(str); -} - -static void unsetenv(const char *name) -{ - setenv(name, "", TRUE); -} -#endif /* _WIN32 */ - -static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name, *value; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - value = JS_ToCString(ctx, argv[1]); - if (!value) { - JS_FreeCString(ctx, name); - return JS_EXCEPTION; - } - setenv(name, value, TRUE); - JS_FreeCString(ctx, name); - JS_FreeCString(ctx, value); - return JS_UNDEFINED; -} - -static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - unsetenv(name); - JS_FreeCString(ctx, name); - return JS_UNDEFINED; -} - -/* return an object containing the list of the available environment - variables. */ -static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - char **envp; - const char *name, *p, *value; - JSValue obj; - uint32_t idx; - size_t name_len; - JSAtom atom; - int ret; - - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - envp = environ; - for(idx = 0; envp[idx] != NULL; idx++) { - name = envp[idx]; - p = strchr(name, '='); - name_len = p - name; - if (!p) - continue; - value = p + 1; - atom = JS_NewAtomLen(ctx, name, name_len); - if (atom == JS_ATOM_NULL) - goto fail; - ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), - JS_PROP_C_W_E); - JS_FreeAtom(ctx, atom); - if (ret < 0) - goto fail; - } - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JS_RunGC(JS_GetRuntime(ctx)); - return JS_UNDEFINED; -} - -static int interrupt_handler(JSRuntime *rt, void *opaque) -{ - return (os_pending_signals >> SIGINT) & 1; -} - -static int get_bool_option(JSContext *ctx, BOOL *pbool, - JSValueConst obj, - const char *option) -{ - JSValue val; - val = JS_GetPropertyStr(ctx, obj, option); - if (JS_IsException(val)) - return -1; - if (!JS_IsUndefined(val)) { - *pbool = JS_ToBool(ctx, val); - } - JS_FreeValue(ctx, val); - return 0; -} - -static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - const char *str; - size_t len; - JSValue ret; - JSValueConst options_obj; - BOOL backtrace_barrier = FALSE; - BOOL is_async = FALSE; - int flags; - - if (argc >= 2) { - options_obj = argv[1]; - if (get_bool_option(ctx, &backtrace_barrier, options_obj, - "backtrace_barrier")) - return JS_EXCEPTION; - if (get_bool_option(ctx, &is_async, options_obj, - "async")) - return JS_EXCEPTION; - } - - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; - if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { - /* install the interrupt handler */ - JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); - } - flags = JS_EVAL_TYPE_GLOBAL; - if (backtrace_barrier) - flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; - if (is_async) - flags |= JS_EVAL_FLAG_ASYNC; - ret = JS_Eval(ctx, str, len, "", flags); - JS_FreeCString(ctx, str); - if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { - /* remove the interrupt handler */ - JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); - os_pending_signals &= ~((uint64_t)1 << SIGINT); - /* convert the uncatchable "interrupted" error into a normal error - so that it can be caught by the REPL */ - if (JS_IsException(ret)) - JS_SetUncatchableException(ctx, FALSE); - } - return ret; -} - -static JSClassID js_std_file_class_id; - -typedef struct { - FILE *f; - BOOL close_in_finalizer; - BOOL is_popen; -} JSSTDFile; - -static void js_std_file_finalizer(JSRuntime *rt, JSValue val) -{ - JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id); - if (s) { - if (s->f && s->close_in_finalizer) { - if (s->is_popen) - pclose(s->f); - else - fclose(s->f); - } - js_free_rt(rt, s); - } -} - -static ssize_t js_get_errno(ssize_t ret) -{ - if (ret == -1) - ret = -errno; - return ret; -} - -static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int err; - if (JS_ToInt32(ctx, &err, argv[0])) - return JS_EXCEPTION; - return JS_NewString(ctx, strerror(err)); -} - -static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - const char *str; - size_t len; - - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; - obj = JS_ParseJSON2(ctx, str, len, "", JS_PARSE_JSON_EXT); - JS_FreeCString(ctx, str); - return obj; -} - -static JSValue js_new_std_file(JSContext *ctx, FILE *f, - BOOL close_in_finalizer, - BOOL is_popen) -{ - JSSTDFile *s; - JSValue obj; - obj = JS_NewObjectClass(ctx, js_std_file_class_id); - if (JS_IsException(obj)) - return obj; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - s->close_in_finalizer = close_in_finalizer; - s->is_popen = is_popen; - s->f = f; - JS_SetOpaque(obj, s); - return obj; -} - -static void js_set_error_object(JSContext *ctx, JSValue obj, int err) -{ - if (!JS_IsUndefined(obj)) { - JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); - } -} - -static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename, *mode = NULL; - FILE *f; - int err; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rwa+b")] != '\0') { - JS_ThrowTypeError(ctx, "invalid file mode"); - goto fail; - } - - f = fopen(filename, mode); - if (!f) - err = errno; - else - err = 0; - if (argc >= 3) - js_set_error_object(ctx, argv[2], err); - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, TRUE, FALSE); - fail: - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename, *mode = NULL; - FILE *f; - int err; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rw")] != '\0') { - JS_ThrowTypeError(ctx, "invalid file mode"); - goto fail; - } - - f = popen(filename, mode); - if (!f) - err = errno; - else - err = 0; - if (argc >= 3) - js_set_error_object(ctx, argv[2], err); - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, TRUE, TRUE); - fail: - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *mode; - FILE *f; - int fd, err; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rwa+")] != '\0') { - JS_ThrowTypeError(ctx, "invalid file mode"); - goto fail; - } - - f = fdopen(fd, mode); - if (!f) - err = errno; - else - err = 0; - if (argc >= 3) - js_set_error_object(ctx, argv[2], err); - JS_FreeCString(ctx, mode); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, TRUE, FALSE); - fail: - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f; - f = tmpfile(); - if (argc >= 1) - js_set_error_object(ctx, argv[0], f ? 0 : errno); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, TRUE, FALSE); -} - -static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_printf_internal(ctx, argc, argv, NULL); -} - -static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_printf_internal(ctx, argc, argv, stdout); -} - -static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) -{ - JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id); - if (!s) - return NULL; - if (!s->f) { - JS_ThrowTypeError(ctx, "invalid file handle"); - return NULL; - } - return s->f; -} - -static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f; - int i; - const char *str; - size_t len; - - if (magic == 0) { - f = stdout; - } else { - f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - } - - for(i = 0; i < argc; i++) { - str = JS_ToCStringLen(ctx, &len, argv[i]); - if (!str) - return JS_EXCEPTION; - fwrite(str, 1, len, f); - JS_FreeCString(ctx, str); - } - return JS_UNDEFINED; -} - -static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id); - int err; - if (!s) - return JS_EXCEPTION; - if (!s->f) - return JS_ThrowTypeError(ctx, "invalid file handle"); - if (s->is_popen) - err = js_get_errno(pclose(s->f)); - else - err = js_get_errno(fclose(s->f)); - s->f = NULL; - return JS_NewInt32(ctx, err); -} - -static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return js_printf_internal(ctx, argc, argv, f); -} - -static void js_print_value_write(void *opaque, const char *buf, size_t len) -{ - FILE *fo = opaque; - fwrite(buf, 1, len, fo); -} - -static JSValue js_std_file_printObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JS_PrintValue(ctx, js_print_value_write, stdout, argv[0], NULL); - return JS_UNDEFINED; -} - -static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - fflush(f); - return JS_UNDEFINED; -} - -static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_bigint) -{ - FILE *f = js_std_file_get(ctx, this_val); - int64_t pos; - if (!f) - return JS_EXCEPTION; -#if defined(__linux__) || defined(__GLIBC__) - pos = ftello(f); -#else - pos = ftell(f); -#endif - if (is_bigint) - return JS_NewBigInt64(ctx, pos); - else - return JS_NewInt64(ctx, pos); -} - -static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int64_t pos; - int whence, ret; - if (!f) - return JS_EXCEPTION; - if (JS_ToInt64Ext(ctx, &pos, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &whence, argv[1])) - return JS_EXCEPTION; -#if defined(__linux__) || defined(__GLIBC__) - ret = fseeko(f, pos, whence); -#else - ret = fseek(f, pos, whence); -#endif - if (ret < 0) - ret = -errno; - return JS_NewInt32(ctx, ret); -} - -static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewBool(ctx, feof(f)); -} - -static JSValue js_std_file_error(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewBool(ctx, ferror(f)); -} - -static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - clearerr(f); - return JS_UNDEFINED; -} - -static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewInt32(ctx, fileno(f)); -} - -static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f = js_std_file_get(ctx, this_val); - uint64_t pos, len; - size_t size, ret; - uint8_t *buf; - - if (!f) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &pos, argv[1])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[2])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[0]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); - if (magic) - ret = fwrite(buf + pos, 1, len, f); - else - ret = fread(buf + pos, 1, len, f); - return JS_NewInt64(ctx, ret); -} - -/* XXX: could use less memory and go faster */ -static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - DynBuf dbuf; - JSValue obj; - - if (!f) - return JS_EXCEPTION; - - js_std_dbuf_init(ctx, &dbuf); - for(;;) { - c = fgetc(f); - if (c == EOF) { - if (dbuf.size == 0) { - /* EOF */ - dbuf_free(&dbuf); - return JS_NULL; - } else { - break; - } - } - if (c == '\n') - break; - if (dbuf_putc(&dbuf, c)) { - dbuf_free(&dbuf); - return JS_ThrowOutOfMemory(ctx); - } - } - obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); - dbuf_free(&dbuf); - return obj; -} - -/* XXX: could use less memory and go faster */ -static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - DynBuf dbuf; - JSValue obj; - uint64_t max_size64; - size_t max_size; - JSValueConst max_size_val; - - if (!f) - return JS_EXCEPTION; - - if (argc >= 1) - max_size_val = argv[0]; - else - max_size_val = JS_UNDEFINED; - max_size = (size_t)-1; - if (!JS_IsUndefined(max_size_val)) { - if (JS_ToIndex(ctx, &max_size64, max_size_val)) - return JS_EXCEPTION; - if (max_size64 < max_size) - max_size = max_size64; - } - - js_std_dbuf_init(ctx, &dbuf); - while (max_size != 0) { - c = fgetc(f); - if (c == EOF) - break; - if (dbuf_putc(&dbuf, c)) { - dbuf_free(&dbuf); - return JS_EXCEPTION; - } - max_size--; - } - obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); - dbuf_free(&dbuf); - return obj; -} - -static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewInt32(ctx, fgetc(f)); -} - -static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - if (!f) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &c, argv[0])) - return JS_EXCEPTION; - c = fputc(c, f); - return JS_NewInt32(ctx, c); -} - -/* urlGet */ - -#define URL_GET_PROGRAM "curl -s -i --" -#define URL_GET_BUF_SIZE 4096 - -static int http_get_header_line(FILE *f, char *buf, size_t buf_size, - DynBuf *dbuf) -{ - int c; - char *p; - - p = buf; - for(;;) { - c = fgetc(f); - if (c < 0) - return -1; - if ((p - buf) < buf_size - 1) - *p++ = c; - if (dbuf) - dbuf_putc(dbuf, c); - if (c == '\n') - break; - } - *p = '\0'; - return 0; -} - -static int http_get_status(const char *buf) -{ - const char *p = buf; - while (*p != ' ' && *p != '\0') - p++; - if (*p != ' ') - return 0; - while (*p == ' ') - p++; - return atoi(p); -} - -static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *url; - DynBuf cmd_buf; - DynBuf data_buf_s, *data_buf = &data_buf_s; - DynBuf header_buf_s, *header_buf = &header_buf_s; - char *buf; - size_t i, len; - int status; - JSValue response = JS_UNDEFINED, ret_obj; - JSValueConst options_obj; - FILE *f; - BOOL binary_flag, full_flag; - - url = JS_ToCString(ctx, argv[0]); - if (!url) - return JS_EXCEPTION; - - binary_flag = FALSE; - full_flag = FALSE; - - if (argc >= 2) { - options_obj = argv[1]; - - if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) - goto fail_obj; - - if (get_bool_option(ctx, &full_flag, options_obj, "full")) { - fail_obj: - JS_FreeCString(ctx, url); - return JS_EXCEPTION; - } - } - - js_std_dbuf_init(ctx, &cmd_buf); - dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); - for(i = 0; url[i] != '\0'; i++) { - unsigned char c = url[i]; - switch (c) { - case '\'': - /* shell single quoted string does not support \' */ - dbuf_putstr(&cmd_buf, "'\\''"); - break; - case '[': case ']': case '{': case '}': case '\\': - /* prevent interpretation by curl as range or set specification */ - dbuf_putc(&cmd_buf, '\\'); - /* FALLTHROUGH */ - default: - dbuf_putc(&cmd_buf, c); - break; - } - } - JS_FreeCString(ctx, url); - dbuf_putstr(&cmd_buf, "'"); - dbuf_putc(&cmd_buf, '\0'); - if (dbuf_error(&cmd_buf)) { - dbuf_free(&cmd_buf); - return JS_EXCEPTION; - } - // printf("%s\n", (char *)cmd_buf.buf); - f = popen((char *)cmd_buf.buf, "r"); - dbuf_free(&cmd_buf); - if (!f) { - return JS_ThrowTypeError(ctx, "could not start curl"); - } - - js_std_dbuf_init(ctx, data_buf); - js_std_dbuf_init(ctx, header_buf); - - buf = js_malloc(ctx, URL_GET_BUF_SIZE); - if (!buf) - goto fail; - - /* get the HTTP status */ - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { - status = 0; - goto bad_header; - } - status = http_get_status(buf); - if (!full_flag && !(status >= 200 && status <= 299)) { - goto bad_header; - } - - /* wait until there is an empty line */ - for(;;) { - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { - bad_header: - response = JS_NULL; - goto done; - } - if (!strcmp(buf, "\r\n")) - break; - } - if (dbuf_error(header_buf)) - goto fail; - header_buf->size -= 2; /* remove the trailing CRLF */ - - /* download the data */ - for(;;) { - len = fread(buf, 1, URL_GET_BUF_SIZE, f); - if (len == 0) - break; - dbuf_put(data_buf, (uint8_t *)buf, len); - } - if (dbuf_error(data_buf)) - goto fail; - if (binary_flag) { - response = JS_NewArrayBufferCopy(ctx, - data_buf->buf, data_buf->size); - } else { - response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); - } - if (JS_IsException(response)) - goto fail; - done: - js_free(ctx, buf); - buf = NULL; - pclose(f); - f = NULL; - dbuf_free(data_buf); - data_buf = NULL; - - if (full_flag) { - ret_obj = JS_NewObject(ctx); - if (JS_IsException(ret_obj)) - goto fail; - JS_DefinePropertyValueStr(ctx, ret_obj, "response", - response, - JS_PROP_C_W_E); - if (!JS_IsNull(response)) { - JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders", - JS_NewStringLen(ctx, (char *)header_buf->buf, - header_buf->size), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, ret_obj, "status", - JS_NewInt32(ctx, status), - JS_PROP_C_W_E); - } - } else { - ret_obj = response; - } - dbuf_free(header_buf); - return ret_obj; - fail: - if (f) - pclose(f); - js_free(ctx, buf); - if (data_buf) - dbuf_free(data_buf); - if (header_buf) - dbuf_free(header_buf); - JS_FreeValue(ctx, response); - return JS_EXCEPTION; -} - -static JSClassDef js_std_file_class = { - "FILE", - .finalizer = js_std_file_finalizer, -}; - -static const JSCFunctionListEntry js_std_error_props[] = { - /* various errno values */ -#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) - DEF(EINVAL), - DEF(EIO), - DEF(EACCES), - DEF(EEXIST), - DEF(ENOSPC), - DEF(ENOSYS), - DEF(EBUSY), - DEF(ENOENT), - DEF(EPERM), - DEF(EPIPE), - DEF(EBADF), -#undef DEF -}; - -static const JSCFunctionListEntry js_std_funcs[] = { - JS_CFUNC_DEF("exit", 1, js_std_exit ), - JS_CFUNC_DEF("gc", 0, js_std_gc ), - JS_CFUNC_DEF("evalScript", 1, js_evalScript ), - JS_CFUNC_DEF("loadScript", 1, js_loadScript ), - JS_CFUNC_DEF("getenv", 1, js_std_getenv ), - JS_CFUNC_DEF("setenv", 1, js_std_setenv ), - JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), - JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), - JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), - JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), - JS_CFUNC_DEF("strerror", 1, js_std_strerror ), - JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ), - - /* FILE I/O */ - JS_CFUNC_DEF("open", 2, js_std_open ), - JS_CFUNC_DEF("popen", 2, js_std_popen ), - JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), - JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), - JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ), - JS_CFUNC_DEF("printf", 1, js_std_printf ), - JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ), - JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), - JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), - JS_CFUNC_DEF("__printObject", 1, js_std_file_printObject ), -}; - -static const JSCFunctionListEntry js_std_file_proto_funcs[] = { - JS_CFUNC_DEF("close", 0, js_std_file_close ), - JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), - JS_CFUNC_DEF("printf", 1, js_std_file_printf ), - JS_CFUNC_DEF("flush", 0, js_std_file_flush ), - JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ), - JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ), - JS_CFUNC_DEF("seek", 2, js_std_file_seek ), - JS_CFUNC_DEF("eof", 0, js_std_file_eof ), - JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ), - JS_CFUNC_DEF("error", 0, js_std_file_error ), - JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ), - JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ), - JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ), - JS_CFUNC_DEF("getline", 0, js_std_file_getline ), - JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ), - JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ), - JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ), - /* setvbuf, ... */ -}; - -static int js_std_init(JSContext *ctx, JSModuleDef *m) -{ - JSValue proto; - - /* FILE class */ - /* the class ID is created once */ - JS_NewClassID(&js_std_file_class_id); - /* the class is created once per runtime */ - JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class); - proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, - countof(js_std_file_proto_funcs)); - JS_SetClassProto(ctx, js_std_file_class_id, proto); - - JS_SetModuleExportList(ctx, m, js_std_funcs, - countof(js_std_funcs)); - JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE)); - JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE)); - JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE)); - return 0; -} - -JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_std_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); - JS_AddModuleExport(ctx, m, "in"); - JS_AddModuleExport(ctx, m, "out"); - JS_AddModuleExport(ctx, m, "err"); - return m; -} - -/**********************************************************/ -/* 'os' object */ - -static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - int flags, mode, ret; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &flags, argv[1])) - goto fail; - if (argc >= 3 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32(ctx, &mode, argv[2])) { - fail: - JS_FreeCString(ctx, filename); - return JS_EXCEPTION; - } - } else { - mode = 0666; - } -#if defined(_WIN32) - /* force binary mode by default */ - if (!(flags & O_TEXT)) - flags |= O_BINARY; -#endif - ret = js_get_errno(open(filename, flags, mode)); - JS_FreeCString(ctx, filename); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, ret; - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - ret = js_get_errno(close(fd)); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, whence; - int64_t pos, ret; - BOOL is_bigint; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - is_bigint = JS_IsBigInt(ctx, argv[1]); - if (JS_ToInt64Ext(ctx, &pos, argv[1])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &whence, argv[2])) - return JS_EXCEPTION; - ret = lseek(fd, pos, whence); - if (ret == -1) - ret = -errno; - if (is_bigint) - return JS_NewBigInt64(ctx, ret); - else - return JS_NewInt64(ctx, ret); -} - -static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - int fd; - uint64_t pos, len; - size_t size; - ssize_t ret; - uint8_t *buf; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &pos, argv[2])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[3])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[1]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); - if (magic) - ret = js_get_errno(write(fd, buf + pos, len)); - else - ret = js_get_errno(read(fd, buf + pos, len)); - return JS_NewInt64(ctx, ret); -} - -static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - return JS_NewBool(ctx, isatty(fd)); -} - -#if defined(_WIN32) -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - HANDLE handle; - CONSOLE_SCREEN_BUFFER_INFO info; - JSValue obj; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - handle = (HANDLE)_get_osfhandle(fd); - - if (!GetConsoleScreenBufferInfo(handle, &info)) - return JS_NULL; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); - return obj; -} - -/* Windows 10 built-in VT100 emulation */ -#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 -#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 - -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - HANDLE handle; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - handle = (HANDLE)_get_osfhandle(fd); - SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); - _setmode(fd, _O_BINARY); - if (fd == 0) { - handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ - SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); - } - return JS_UNDEFINED; -} -#else -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - struct winsize ws; - JSValue obj; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && - ws.ws_col >= 4 && ws.ws_row >= 4) { - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); - return obj; - } else { - return JS_NULL; - } -} - -static struct termios oldtty; - -static void term_exit(void) -{ - tcsetattr(0, TCSANOW, &oldtty); -} - -/* XXX: should add a way to go back to normal mode */ -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - struct termios tty; - int fd; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - - memset(&tty, 0, sizeof(tty)); - tcgetattr(fd, &tty); - oldtty = tty; - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP - |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); - tty.c_cflag &= ~(CSIZE|PARENB); - tty.c_cflag |= CS8; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - - tcsetattr(fd, TCSANOW, &tty); - - atexit(term_exit); - return JS_UNDEFINED; -} - -#endif /* !_WIN32 */ - -static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - int ret; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; -#if defined(_WIN32) - { - struct stat st; - if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { - ret = rmdir(filename); - } else { - ret = unlink(filename); - } - } -#else - ret = remove(filename); -#endif - ret = js_get_errno(ret); - JS_FreeCString(ctx, filename); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *oldpath, *newpath; - int ret; - - oldpath = JS_ToCString(ctx, argv[0]); - if (!oldpath) - return JS_EXCEPTION; - newpath = JS_ToCString(ctx, argv[1]); - if (!newpath) { - JS_FreeCString(ctx, oldpath); - return JS_EXCEPTION; - } - ret = js_get_errno(rename(oldpath, newpath)); - JS_FreeCString(ctx, oldpath); - JS_FreeCString(ctx, newpath); - return JS_NewInt32(ctx, ret); -} - -static BOOL is_main_thread(JSRuntime *rt) -{ - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - return !ts->recv_pipe; -} - -static JSOSRWHandler *find_rh(JSThreadState *ts, int fd) -{ - JSOSRWHandler *rh; - struct list_head *el; - - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == fd) - return rh; - } - return NULL; -} - -static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) -{ - int i; - list_del(&rh->link); - for(i = 0; i < 2; i++) { - JS_FreeValueRT(rt, rh->rw_func[i]); - } - js_free_rt(rt, rh); -} - -static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSOSRWHandler *rh; - int fd; - JSValueConst func; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - func = argv[1]; - if (JS_IsNull(func)) { - rh = find_rh(ts, fd); - if (rh) { - JS_FreeValue(ctx, rh->rw_func[magic]); - rh->rw_func[magic] = JS_NULL; - if (JS_IsNull(rh->rw_func[0]) && - JS_IsNull(rh->rw_func[1])) { - /* remove the entry */ - free_rw_handler(JS_GetRuntime(ctx), rh); - } - } - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - rh = find_rh(ts, fd); - if (!rh) { - rh = js_mallocz(ctx, sizeof(*rh)); - if (!rh) - return JS_EXCEPTION; - rh->fd = fd; - rh->rw_func[0] = JS_NULL; - rh->rw_func[1] = JS_NULL; - list_add_tail(&rh->link, &ts->os_rw_handlers); - } - JS_FreeValue(ctx, rh->rw_func[magic]); - rh->rw_func[magic] = JS_DupValue(ctx, func); - } - return JS_UNDEFINED; -} - -static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) -{ - JSOSSignalHandler *sh; - struct list_head *el; - list_for_each(el, &ts->os_signal_handlers) { - sh = list_entry(el, JSOSSignalHandler, link); - if (sh->sig_num == sig_num) - return sh; - } - return NULL; -} - -static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) -{ - list_del(&sh->link); - JS_FreeValueRT(rt, sh->func); - js_free_rt(rt, sh); -} - -static void os_signal_handler(int sig_num) -{ - os_pending_signals |= ((uint64_t)1 << sig_num); -} - -#if defined(_WIN32) -typedef void (*sighandler_t)(int sig_num); -#endif - -static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSOSSignalHandler *sh; - uint32_t sig_num; - JSValueConst func; - sighandler_t handler; - - if (!is_main_thread(rt)) - return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); - - if (JS_ToUint32(ctx, &sig_num, argv[0])) - return JS_EXCEPTION; - if (sig_num >= 64) - return JS_ThrowRangeError(ctx, "invalid signal number"); - func = argv[1]; - /* func = null: SIG_DFL, func = undefined, SIG_IGN */ - if (JS_IsNull(func) || JS_IsUndefined(func)) { - sh = find_sh(ts, sig_num); - if (sh) { - free_sh(JS_GetRuntime(ctx), sh); - } - if (JS_IsNull(func)) - handler = SIG_DFL; - else - handler = SIG_IGN; - signal(sig_num, handler); - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - sh = find_sh(ts, sig_num); - if (!sh) { - sh = js_mallocz(ctx, sizeof(*sh)); - if (!sh) - return JS_EXCEPTION; - sh->sig_num = sig_num; - list_add_tail(&sh->link, &ts->os_signal_handlers); - } - JS_FreeValue(ctx, sh->func); - sh->func = JS_DupValue(ctx, func); - signal(sig_num, os_signal_handler); - } - return JS_UNDEFINED; -} - -#if defined(__linux__) || defined(__APPLE__) -static int64_t get_time_ms(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); -} - -static int64_t get_time_ns(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; -} -#else -/* more portable, but does not work if the date is updated */ -static int64_t get_time_ms(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -} - -static int64_t get_time_ns(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000000000 + (tv.tv_usec * 1000); -} -#endif - -static JSValue js_os_now(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6); -} - -static void free_timer(JSRuntime *rt, JSOSTimer *th) -{ - list_del(&th->link); - JS_FreeValueRT(rt, th->func); - js_free_rt(rt, th); -} - -static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int64_t delay; - JSValueConst func; - JSOSTimer *th; - - func = argv[0]; - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - if (JS_ToInt64(ctx, &delay, argv[1])) - return JS_EXCEPTION; - th = js_mallocz(ctx, sizeof(*th)); - if (!th) - return JS_EXCEPTION; - th->timer_id = ts->next_timer_id; - if (ts->next_timer_id == INT32_MAX) - ts->next_timer_id = 1; - else - ts->next_timer_id++; - th->timeout = get_time_ms() + delay; - th->func = JS_DupValue(ctx, func); - list_add_tail(&th->link, &ts->os_timers); - return JS_NewInt32(ctx, th->timer_id); -} - -static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) -{ - struct list_head *el; - if (timer_id <= 0) - return NULL; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - if (th->timer_id == timer_id) - return th; - } - return NULL; -} - -static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSOSTimer *th; - int timer_id; - - if (JS_ToInt32(ctx, &timer_id, argv[0])) - return JS_EXCEPTION; - th = find_timer_by_id(ts, timer_id); - if (!th) - return JS_UNDEFINED; - free_timer(rt, th); - return JS_UNDEFINED; -} - -/* return a promise */ -static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int64_t delay; - JSOSTimer *th; - JSValue promise, resolving_funcs[2]; - - if (JS_ToInt64(ctx, &delay, argv[0])) - return JS_EXCEPTION; - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - - th = js_mallocz(ctx, sizeof(*th)); - if (!th) { - JS_FreeValue(ctx, promise); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return JS_EXCEPTION; - } - th->timer_id = -1; - th->timeout = get_time_ms() + delay; - th->func = JS_DupValue(ctx, resolving_funcs[0]); - list_add_tail(&th->link, &ts->os_timers); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; -} - -static void call_handler(JSContext *ctx, JSValueConst func) -{ - JSValue ret, func1; - /* 'func' might be destroyed when calling itself (if it frees the - handler), so must take extra care */ - func1 = JS_DupValue(ctx, func); - ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); - JS_FreeValue(ctx, func1); - if (JS_IsException(ret)) - js_std_dump_error(ctx); - JS_FreeValue(ctx, ret); -} - -#ifdef USE_WORKER - -#ifdef _WIN32 - -static int js_waker_init(JSWaker *w) -{ - w->handle = CreateEvent(NULL, TRUE, FALSE, NULL); - return w->handle ? 0 : -1; -} - -static void js_waker_signal(JSWaker *w) -{ - SetEvent(w->handle); -} - -static void js_waker_clear(JSWaker *w) -{ - ResetEvent(w->handle); -} - -static void js_waker_close(JSWaker *w) -{ - CloseHandle(w->handle); - w->handle = INVALID_HANDLE_VALUE; -} - -#else // !_WIN32 - -static int js_waker_init(JSWaker *w) -{ - int fds[2]; - - if (pipe(fds) < 0) - return -1; - w->read_fd = fds[0]; - w->write_fd = fds[1]; - return 0; -} - -static void js_waker_signal(JSWaker *w) -{ - int ret; - - for(;;) { - ret = write(w->write_fd, "", 1); - if (ret == 1) - break; - if (ret < 0 && (errno != EAGAIN || errno != EINTR)) - break; - } -} - -static void js_waker_clear(JSWaker *w) -{ - uint8_t buf[16]; - int ret; - - for(;;) { - ret = read(w->read_fd, buf, sizeof(buf)); - if (ret >= 0) - break; - if (errno != EAGAIN && errno != EINTR) - break; - } -} - -static void js_waker_close(JSWaker *w) -{ - close(w->read_fd); - close(w->write_fd); - w->read_fd = -1; - w->write_fd = -1; -} - -#endif // _WIN32 - -static void js_free_message(JSWorkerMessage *msg); - -/* return 1 if a message was handled, 0 if no message */ -static int handle_posted_message(JSRuntime *rt, JSContext *ctx, - JSWorkerMessageHandler *port) -{ - JSWorkerMessagePipe *ps = port->recv_pipe; - int ret; - struct list_head *el; - JSWorkerMessage *msg; - JSValue obj, data_obj, func, retval; - - pthread_mutex_lock(&ps->mutex); - if (!list_empty(&ps->msg_queue)) { - el = ps->msg_queue.next; - msg = list_entry(el, JSWorkerMessage, link); - - /* remove the message from the queue */ - list_del(&msg->link); - - if (list_empty(&ps->msg_queue)) - js_waker_clear(&ps->waker); - - pthread_mutex_unlock(&ps->mutex); - - data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, - JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); - - js_free_message(msg); - - if (JS_IsException(data_obj)) - goto fail; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, data_obj); - goto fail; - } - JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E); - - /* 'func' might be destroyed when calling itself (if it frees the - handler), so must take extra care */ - func = JS_DupValue(ctx, port->on_message_func); - retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj); - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, func); - if (JS_IsException(retval)) { - fail: - js_std_dump_error(ctx); - } else { - JS_FreeValue(ctx, retval); - } - ret = 1; - } else { - pthread_mutex_unlock(&ps->mutex); - ret = 0; - } - return ret; -} -#else -static int handle_posted_message(JSRuntime *rt, JSContext *ctx, - JSWorkerMessageHandler *port) -{ - return 0; -} -#endif /* !USE_WORKER */ - -#if defined(_WIN32) - -static int js_os_poll(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int min_delay, count; - int64_t cur_time, delay; - JSOSRWHandler *rh; - struct list_head *el; - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 - - /* XXX: handle signals if useful */ - - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && - list_empty(&ts->port_list)) { - return -1; /* no more events */ - } - - if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); - min_delay = 10000; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - delay = th->timeout - cur_time; - if (delay <= 0) { - JSValue func; - /* the timer expired */ - func = th->func; - th->func = JS_UNDEFINED; - free_timer(rt, th); - call_handler(ctx, func); - JS_FreeValue(ctx, func); - return 0; - } else if (delay < min_delay) { - min_delay = delay; - } - } - } else { - min_delay = -1; - } - - count = 0; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin - if (count == (int)countof(handles)) - break; - } - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (JS_IsNull(port->on_message_func)) - continue; - handles[count++] = port->recv_pipe->waker.handle; - if (count == (int)countof(handles)) - break; - } - - if (count > 0) { - DWORD ret, timeout = INFINITE; - if (min_delay != -1) - timeout = min_delay; - ret = WaitForMultipleObjects(count, handles, FALSE, timeout); - - if (ret < count) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - goto done; - } - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - if (ps->waker.handle == handles[ret]) { - if (handle_posted_message(rt, ctx, port)) - goto done; - } - } - } - } - } else { - Sleep(min_delay); - } - done: - return 0; -} - -#else - -static int js_os_poll(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int ret, fd_max, min_delay; - int64_t cur_time, delay; - fd_set rfds, wfds; - JSOSRWHandler *rh; - struct list_head *el; - struct timeval tv, *tvp; - - /* only check signals in the main thread */ - if (!ts->recv_pipe && - unlikely(os_pending_signals != 0)) { - JSOSSignalHandler *sh; - uint64_t mask; - - list_for_each(el, &ts->os_signal_handlers) { - sh = list_entry(el, JSOSSignalHandler, link); - mask = (uint64_t)1 << sh->sig_num; - if (os_pending_signals & mask) { - os_pending_signals &= ~mask; - call_handler(ctx, sh->func); - return 0; - } - } - } - - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && - list_empty(&ts->port_list)) - return -1; /* no more events */ - - if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); - min_delay = 10000; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - delay = th->timeout - cur_time; - if (delay <= 0) { - JSValue func; - /* the timer expired */ - func = th->func; - th->func = JS_UNDEFINED; - free_timer(rt, th); - call_handler(ctx, func); - JS_FreeValue(ctx, func); - return 0; - } else if (delay < min_delay) { - min_delay = delay; - } - } - tv.tv_sec = min_delay / 1000; - tv.tv_usec = (min_delay % 1000) * 1000; - tvp = &tv; - } else { - tvp = NULL; - } - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - fd_max = -1; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - fd_max = max_int(fd_max, rh->fd); - if (!JS_IsNull(rh->rw_func[0])) - FD_SET(rh->fd, &rfds); - if (!JS_IsNull(rh->rw_func[1])) - FD_SET(rh->fd, &wfds); - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - fd_max = max_int(fd_max, ps->waker.read_fd); - FD_SET(ps->waker.read_fd, &rfds); - } - } - - ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); - if (ret > 0) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (!JS_IsNull(rh->rw_func[0]) && - FD_ISSET(rh->fd, &rfds)) { - call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - goto done; - } - if (!JS_IsNull(rh->rw_func[1]) && - FD_ISSET(rh->fd, &wfds)) { - call_handler(ctx, rh->rw_func[1]); - /* must stop because the list may have been modified */ - goto done; - } - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - if (FD_ISSET(ps->waker.read_fd, &rfds)) { - if (handle_posted_message(rt, ctx, port)) - goto done; - } - } - } - } - done: - return 0; -} -#endif /* !_WIN32 */ - -static JSValue make_obj_error(JSContext *ctx, - JSValue obj, - int err) -{ - JSValue arr; - if (JS_IsException(obj)) - return obj; - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return JS_EXCEPTION; - JS_DefinePropertyValueUint32(ctx, arr, 0, obj, - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), - JS_PROP_C_W_E); - return arr; -} - -static JSValue make_string_error(JSContext *ctx, - const char *buf, - int err) -{ - return make_obj_error(ctx, JS_NewString(ctx, buf), err); -} - -/* return [cwd, errorcode] */ -static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - char buf[PATH_MAX]; - int err; - - if (!getcwd(buf, sizeof(buf))) { - buf[0] = '\0'; - err = errno; - } else { - err = 0; - } - return make_string_error(ctx, buf, err); -} - -static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *target; - int err; - - target = JS_ToCString(ctx, argv[0]); - if (!target) - return JS_EXCEPTION; - err = js_get_errno(chdir(target)); - JS_FreeCString(ctx, target); - return JS_NewInt32(ctx, err); -} - -static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int mode, ret; - const char *path; - - if (argc >= 2) { - if (JS_ToInt32(ctx, &mode, argv[1])) - return JS_EXCEPTION; - } else { - mode = 0777; - } - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if defined(_WIN32) - (void)mode; - ret = js_get_errno(mkdir(path)); -#else - ret = js_get_errno(mkdir(path, mode)); -#endif - JS_FreeCString(ctx, path); - return JS_NewInt32(ctx, ret); -} - -/* return [array, errorcode] */ -static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - DIR *f; - struct dirent *d; - JSValue obj; - int err; - uint32_t len; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) { - JS_FreeCString(ctx, path); - return JS_EXCEPTION; - } - f = opendir(path); - if (!f) - err = errno; - else - err = 0; - JS_FreeCString(ctx, path); - if (!f) - goto done; - len = 0; - for(;;) { - errno = 0; - d = readdir(f); - if (!d) { - err = errno; - break; - } - JS_DefinePropertyValueUint32(ctx, obj, len++, - JS_NewString(ctx, d->d_name), - JS_PROP_C_W_E); - } - closedir(f); - done: - return make_obj_error(ctx, obj, err); -} - -#if !defined(_WIN32) -static int64_t timespec_to_ms(const struct timespec *tv) -{ - return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); -} -#endif - -/* return [obj, errcode] */ -static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_lstat) -{ - const char *path; - int err, res; - struct stat st; - JSValue obj; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if defined(_WIN32) - res = stat(path, &st); -#else - if (is_lstat) - res = lstat(path, &st); - else - res = stat(path, &st); -#endif - if (res < 0) - err = errno; - else - err = 0; - JS_FreeCString(ctx, path); - if (res < 0) { - obj = JS_NULL; - } else { - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - JS_DefinePropertyValueStr(ctx, obj, "dev", - JS_NewInt64(ctx, st.st_dev), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ino", - JS_NewInt64(ctx, st.st_ino), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mode", - JS_NewInt32(ctx, st.st_mode), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "nlink", - JS_NewInt64(ctx, st.st_nlink), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "uid", - JS_NewInt64(ctx, st.st_uid), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "gid", - JS_NewInt64(ctx, st.st_gid), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "rdev", - JS_NewInt64(ctx, st.st_rdev), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "size", - JS_NewInt64(ctx, st.st_size), - JS_PROP_C_W_E); -#if !defined(_WIN32) - JS_DefinePropertyValueStr(ctx, obj, "blocks", - JS_NewInt64(ctx, st.st_blocks), - JS_PROP_C_W_E); -#endif -#if defined(_WIN32) - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, (int64_t)st.st_atime * 1000), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000), - JS_PROP_C_W_E); -#elif defined(__APPLE__) - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), - JS_PROP_C_W_E); -#else - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), - JS_PROP_C_W_E); -#endif - } - return make_obj_error(ctx, obj, err); -} - -#if !defined(_WIN32) -static void ms_to_timeval(struct timeval *tv, uint64_t v) -{ - tv->tv_sec = v / 1000; - tv->tv_usec = (v % 1000) * 1000; -} -#endif - -static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - int64_t atime, mtime; - int ret; - - if (JS_ToInt64(ctx, &atime, argv[1])) - return JS_EXCEPTION; - if (JS_ToInt64(ctx, &mtime, argv[2])) - return JS_EXCEPTION; - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if defined(_WIN32) - { - struct _utimbuf times; - times.actime = atime / 1000; - times.modtime = mtime / 1000; - ret = js_get_errno(_utime(path, ×)); - } -#else - { - struct timeval times[2]; - ms_to_timeval(×[0], atime); - ms_to_timeval(×[1], mtime); - ret = js_get_errno(utimes(path, times)); - } -#endif - JS_FreeCString(ctx, path); - return JS_NewInt32(ctx, ret); -} - -/* sleep(delay_ms) */ -static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t delay; - int ret; - - if (JS_ToInt64(ctx, &delay, argv[0])) - return JS_EXCEPTION; - if (delay < 0) - delay = 0; -#if defined(_WIN32) - { - if (delay > INT32_MAX) - delay = INT32_MAX; - Sleep(delay); - ret = 0; - } -#else - { - struct timespec ts; - - ts.tv_sec = delay / 1000; - ts.tv_nsec = (delay % 1000) * 1000000; - ret = js_get_errno(nanosleep(&ts, NULL)); - } -#endif - return JS_NewInt32(ctx, ret); -} - -#if defined(_WIN32) -static char *realpath(const char *path, char *buf) -{ - if (!_fullpath(buf, path, PATH_MAX)) { - errno = ENOENT; - return NULL; - } else { - return buf; - } -} -#endif - -/* return [path, errorcode] */ -static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - char buf[PATH_MAX], *res; - int err; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - res = realpath(path, buf); - JS_FreeCString(ctx, path); - if (!res) { - buf[0] = '\0'; - err = errno; - } else { - err = 0; - } - return make_string_error(ctx, buf, err); -} - -#if !defined(_WIN32) -static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *target, *linkpath; - int err; - - target = JS_ToCString(ctx, argv[0]); - if (!target) - return JS_EXCEPTION; - linkpath = JS_ToCString(ctx, argv[1]); - if (!linkpath) { - JS_FreeCString(ctx, target); - return JS_EXCEPTION; - } - err = js_get_errno(symlink(target, linkpath)); - JS_FreeCString(ctx, target); - JS_FreeCString(ctx, linkpath); - return JS_NewInt32(ctx, err); -} - -/* return [path, errorcode] */ -static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - char buf[PATH_MAX]; - int err; - ssize_t res; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - res = readlink(path, buf, sizeof(buf) - 1); - if (res < 0) { - buf[0] = '\0'; - err = errno; - } else { - buf[res] = '\0'; - err = 0; - } - JS_FreeCString(ctx, path); - return make_string_error(ctx, buf, err); -} - -static char **build_envp(JSContext *ctx, JSValueConst obj) -{ - uint32_t len, i; - JSPropertyEnum *tab; - char **envp, *pair; - const char *key, *str; - JSValue val; - size_t key_len, str_len; - - if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) - return NULL; - envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); - if (!envp) - goto fail; - for(i = 0; i < len; i++) { - val = JS_GetProperty(ctx, obj, tab[i].atom); - if (JS_IsException(val)) - goto fail; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) - goto fail; - key = JS_AtomToCString(ctx, tab[i].atom); - if (!key) { - JS_FreeCString(ctx, str); - goto fail; - } - key_len = strlen(key); - str_len = strlen(str); - pair = js_malloc(ctx, key_len + str_len + 2); - if (!pair) { - JS_FreeCString(ctx, key); - JS_FreeCString(ctx, str); - goto fail; - } - memcpy(pair, key, key_len); - pair[key_len] = '='; - memcpy(pair + key_len + 1, str, str_len); - pair[key_len + 1 + str_len] = '\0'; - envp[i] = pair; - JS_FreeCString(ctx, key); - JS_FreeCString(ctx, str); - } - done: - JS_FreePropertyEnum(ctx, tab, len); - return envp; - fail: - if (envp) { - for(i = 0; i < len; i++) - js_free(ctx, envp[i]); - js_free(ctx, envp); - envp = NULL; - } - goto done; -} - -/* execvpe is not available on non GNU systems */ -static int my_execvpe(const char *filename, char **argv, char **envp) -{ - char *path, *p, *p_next, *p1; - char buf[PATH_MAX]; - size_t filename_len, path_len; - BOOL eacces_error; - - filename_len = strlen(filename); - if (filename_len == 0) { - errno = ENOENT; - return -1; - } - if (strchr(filename, '/')) - return execve(filename, argv, envp); - - path = getenv("PATH"); - if (!path) - path = (char *)"/bin:/usr/bin"; - eacces_error = FALSE; - p = path; - for(p = path; p != NULL; p = p_next) { - p1 = strchr(p, ':'); - if (!p1) { - p_next = NULL; - path_len = strlen(p); - } else { - p_next = p1 + 1; - path_len = p1 - p; - } - /* path too long */ - if ((path_len + 1 + filename_len + 1) > PATH_MAX) - continue; - memcpy(buf, p, path_len); - buf[path_len] = '/'; - memcpy(buf + path_len + 1, filename, filename_len); - buf[path_len + 1 + filename_len] = '\0'; - - execve(buf, argv, envp); - - switch(errno) { - case EACCES: - eacces_error = TRUE; - break; - case ENOENT: - case ENOTDIR: - break; - default: - return -1; - } - } - if (eacces_error) - errno = EACCES; - return -1; -} - -/* exec(args[, options]) -> exitcode */ -static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst options, args = argv[0]; - JSValue val, ret_val; - const char **exec_argv, *file = NULL, *str, *cwd = NULL; - char **envp = environ; - uint32_t exec_argc, i; - int ret, pid, status; - BOOL block_flag = TRUE, use_path = TRUE; - static const char *std_name[3] = { "stdin", "stdout", "stderr" }; - int std_fds[3]; - uint32_t uid = -1, gid = -1; - - val = JS_GetPropertyStr(ctx, args, "length"); - if (JS_IsException(val)) - return JS_EXCEPTION; - ret = JS_ToUint32(ctx, &exec_argc, val); - JS_FreeValue(ctx, val); - if (ret) - return JS_EXCEPTION; - /* arbitrary limit to avoid overflow */ - if (exec_argc < 1 || exec_argc > 65535) { - return JS_ThrowTypeError(ctx, "invalid number of arguments"); - } - exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); - if (!exec_argv) - return JS_EXCEPTION; - for(i = 0; i < exec_argc; i++) { - val = JS_GetPropertyUint32(ctx, args, i); - if (JS_IsException(val)) - goto exception; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) - goto exception; - exec_argv[i] = str; - } - exec_argv[exec_argc] = NULL; - - for(i = 0; i < 3; i++) - std_fds[i] = i; - - /* get the options, if any */ - if (argc >= 2) { - options = argv[1]; - - if (get_bool_option(ctx, &block_flag, options, "block")) - goto exception; - if (get_bool_option(ctx, &use_path, options, "usePath")) - goto exception; - - val = JS_GetPropertyStr(ctx, options, "file"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - file = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!file) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "cwd"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - cwd = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!cwd) - goto exception; - } - - /* stdin/stdout/stderr handles */ - for(i = 0; i < 3; i++) { - val = JS_GetPropertyStr(ctx, options, std_name[i]); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - int fd; - ret = JS_ToInt32(ctx, &fd, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - std_fds[i] = fd; - } - } - - val = JS_GetPropertyStr(ctx, options, "env"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - envp = build_envp(ctx, val); - JS_FreeValue(ctx, val); - if (!envp) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "uid"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - ret = JS_ToUint32(ctx, &uid, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "gid"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - ret = JS_ToUint32(ctx, &gid, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - } - } - - pid = fork(); - if (pid < 0) { - JS_ThrowTypeError(ctx, "fork error"); - goto exception; - } - if (pid == 0) { - /* child */ - - /* remap the stdin/stdout/stderr handles if necessary */ - for(i = 0; i < 3; i++) { - if (std_fds[i] != i) { - if (dup2(std_fds[i], i) < 0) - _exit(127); - } - } -#if defined(HAVE_CLOSEFROM) - /* closefrom() is available on many recent unix systems: - Linux with glibc 2.34+, Solaris 9+, FreeBSD 7.3+, - NetBSD 3.0+, OpenBSD 3.5+. - Linux with the musl libc and macOS don't have it. - */ - - closefrom(3); -#else - { - /* Close the file handles manually, limit to 1024 to avoid - costly loop on linux Alpine where sysconf(_SC_OPEN_MAX) - returns a huge value 1048576. - Patch inspired by nicolas-duteil-nova. See also: - https://stackoverflow.com/questions/73229353/ - https://stackoverflow.com/questions/899038/#918469 - */ - int fd_max = min_int(sysconf(_SC_OPEN_MAX), 1024); - for(i = 3; i < fd_max; i++) - close(i); - } -#endif - if (cwd) { - if (chdir(cwd) < 0) - _exit(127); - } - if (uid != -1) { - if (setuid(uid) < 0) - _exit(127); - } - if (gid != -1) { - if (setgid(gid) < 0) - _exit(127); - } - - if (!file) - file = exec_argv[0]; - if (use_path) - ret = my_execvpe(file, (char **)exec_argv, envp); - else - ret = execve(file, (char **)exec_argv, envp); - _exit(127); - } - /* parent */ - if (block_flag) { - for(;;) { - ret = waitpid(pid, &status, 0); - if (ret == pid) { - if (WIFEXITED(status)) { - ret = WEXITSTATUS(status); - break; - } else if (WIFSIGNALED(status)) { - ret = -WTERMSIG(status); - break; - } - } - } - } else { - ret = pid; - } - ret_val = JS_NewInt32(ctx, ret); - done: - JS_FreeCString(ctx, file); - JS_FreeCString(ctx, cwd); - for(i = 0; i < exec_argc; i++) - JS_FreeCString(ctx, exec_argv[i]); - js_free(ctx, exec_argv); - if (envp != environ) { - char **p; - p = envp; - while (*p != NULL) { - js_free(ctx, *p); - p++; - } - js_free(ctx, envp); - } - return ret_val; - exception: - ret_val = JS_EXCEPTION; - goto done; -} - -/* getpid() -> pid */ -static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewInt32(ctx, getpid()); -} - -/* waitpid(pid, block) -> [pid, status] */ -static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, status, options, ret; - JSValue obj; - - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &options, argv[1])) - return JS_EXCEPTION; - - ret = waitpid(pid, &status, options); - if (ret < 0) { - ret = -errno; - status = 0; - } - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), - JS_PROP_C_W_E); - return obj; -} - -/* pipe() -> [read_fd, write_fd] or null if error */ -static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pipe_fds[2], ret; - JSValue obj; - - ret = pipe(pipe_fds); - if (ret < 0) - return JS_NULL; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), - JS_PROP_C_W_E); - return obj; -} - -/* kill(pid, sig) */ -static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, sig, ret; - - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &sig, argv[1])) - return JS_EXCEPTION; - ret = js_get_errno(kill(pid, sig)); - return JS_NewInt32(ctx, ret); -} - -/* dup(fd) */ -static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, ret; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - ret = js_get_errno(dup(fd)); - return JS_NewInt32(ctx, ret); -} - -/* dup2(fd) */ -static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, fd2, ret; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &fd2, argv[1])) - return JS_EXCEPTION; - ret = js_get_errno(dup2(fd, fd2)); - return JS_NewInt32(ctx, ret); -} - -#endif /* !_WIN32 */ - -#ifdef USE_WORKER - -/* Worker */ - -typedef struct { - JSWorkerMessagePipe *recv_pipe; - JSWorkerMessagePipe *send_pipe; - JSWorkerMessageHandler *msg_handler; -} JSWorkerData; - -typedef struct { - char *filename; /* module filename */ - char *basename; /* module base name */ - JSWorkerMessagePipe *recv_pipe, *send_pipe; - int strip_flags; -} WorkerFuncArgs; - -typedef struct { - int ref_count; - uint64_t buf[0]; -} JSSABHeader; - -static JSClassID js_worker_class_id; -static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); - -static int atomic_add_int(int *ptr, int v) -{ - return atomic_fetch_add((_Atomic(uint32_t) *)ptr, v) + v; -} - -/* shared array buffer allocator */ -static void *js_sab_alloc(void *opaque, size_t size) -{ - JSSABHeader *sab; - sab = malloc(sizeof(JSSABHeader) + size); - if (!sab) - return NULL; - sab->ref_count = 1; - return sab->buf; -} - -static void js_sab_free(void *opaque, void *ptr) -{ - JSSABHeader *sab; - int ref_count; - sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); - ref_count = atomic_add_int(&sab->ref_count, -1); - assert(ref_count >= 0); - if (ref_count == 0) { - free(sab); - } -} - -static void js_sab_dup(void *opaque, void *ptr) -{ - JSSABHeader *sab; - sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); - atomic_add_int(&sab->ref_count, 1); -} - -static JSWorkerMessagePipe *js_new_message_pipe(void) -{ - JSWorkerMessagePipe *ps; - - ps = malloc(sizeof(*ps)); - if (!ps) - return NULL; - if (js_waker_init(&ps->waker)) { - free(ps); - return NULL; - } - ps->ref_count = 1; - init_list_head(&ps->msg_queue); - pthread_mutex_init(&ps->mutex, NULL); - return ps; -} - -static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) -{ - atomic_add_int(&ps->ref_count, 1); - return ps; -} - -static void js_free_message(JSWorkerMessage *msg) -{ - size_t i; - /* free the SAB */ - for(i = 0; i < msg->sab_tab_len; i++) { - js_sab_free(NULL, msg->sab_tab[i]); - } - free(msg->sab_tab); - free(msg->data); - free(msg); -} - -static void js_free_message_pipe(JSWorkerMessagePipe *ps) -{ - struct list_head *el, *el1; - JSWorkerMessage *msg; - int ref_count; - - if (!ps) - return; - - ref_count = atomic_add_int(&ps->ref_count, -1); - assert(ref_count >= 0); - if (ref_count == 0) { - list_for_each_safe(el, el1, &ps->msg_queue) { - msg = list_entry(el, JSWorkerMessage, link); - js_free_message(msg); - } - pthread_mutex_destroy(&ps->mutex); - js_waker_close(&ps->waker); - free(ps); - } -} - -static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) -{ - if (port) { - js_free_message_pipe(port->recv_pipe); - JS_FreeValueRT(rt, port->on_message_func); - if (port->link.prev) - list_del(&port->link); - js_free_rt(rt, port); - } -} - -static void js_worker_finalizer(JSRuntime *rt, JSValue val) -{ - JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id); - if (worker) { - js_free_message_pipe(worker->recv_pipe); - js_free_message_pipe(worker->send_pipe); - js_free_port(rt, worker->msg_handler); - js_free_rt(rt, worker); - } -} - -static void js_worker_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id); - if (worker) { - JSWorkerMessageHandler *port = worker->msg_handler; - if (port) { - JS_MarkValue(rt, port->on_message_func, mark_func); - } - } -} - -static JSClassDef js_worker_class = { - "Worker", - .finalizer = js_worker_finalizer, - .gc_mark = js_worker_mark, -}; - -static void *worker_func(void *opaque) -{ - WorkerFuncArgs *args = opaque; - JSRuntime *rt; - JSThreadState *ts; - JSContext *ctx; - JSValue val; - - rt = JS_NewRuntime(); - if (rt == NULL) { - fprintf(stderr, "JS_NewRuntime failure"); - exit(1); - } - JS_SetStripInfo(rt, args->strip_flags); - js_std_init_handlers(rt); - - JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader, js_module_check_attributes, NULL); - - /* set the pipe to communicate with the parent */ - ts = JS_GetRuntimeOpaque(rt); - ts->recv_pipe = args->recv_pipe; - ts->send_pipe = args->send_pipe; - - /* function pointer to avoid linking the whole JS_NewContext() if - not needed */ - ctx = js_worker_new_context_func(rt); - if (ctx == NULL) { - fprintf(stderr, "JS_NewContext failure"); - } - - JS_SetCanBlock(rt, TRUE); - - js_std_add_helpers(ctx, -1, NULL); - - val = JS_LoadModule(ctx, args->basename, args->filename); - free(args->filename); - free(args->basename); - free(args); - val = js_std_await(ctx, val); - if (JS_IsException(val)) - js_std_dump_error(ctx); - JS_FreeValue(ctx, val); - - js_std_loop(ctx); - - JS_FreeContext(ctx); - js_std_free_handlers(rt); - JS_FreeRuntime(rt); - return NULL; -} - -static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, - JSWorkerMessagePipe *recv_pipe, - JSWorkerMessagePipe *send_pipe) -{ - JSValue obj = JS_UNDEFINED, proto; - JSWorkerData *s; - - /* create the object */ - if (JS_IsUndefined(new_target)) { - proto = JS_GetClassProto(ctx, js_worker_class_id); - } else { - proto = JS_GetPropertyStr(ctx, new_target, "prototype"); - if (JS_IsException(proto)) - goto fail; - } - obj = JS_NewObjectProtoClass(ctx, proto, js_worker_class_id); - JS_FreeValue(ctx, proto); - if (JS_IsException(obj)) - goto fail; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - goto fail; - s->recv_pipe = js_dup_message_pipe(recv_pipe); - s->send_pipe = js_dup_message_pipe(send_pipe); - - JS_SetOpaque(obj, s); - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - WorkerFuncArgs *args = NULL; - pthread_t tid; - pthread_attr_t attr; - JSValue obj = JS_UNDEFINED; - int ret; - const char *filename = NULL, *basename; - JSAtom basename_atom; - - /* XXX: in order to avoid problems with resource liberation, we - don't support creating workers inside workers */ - if (!is_main_thread(rt)) - return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker"); - - /* base name, assuming the calling function is a normal JS - function */ - basename_atom = JS_GetScriptOrModuleName(ctx, 1); - if (basename_atom == JS_ATOM_NULL) { - return JS_ThrowTypeError(ctx, "could not determine calling script or module name"); - } - basename = JS_AtomToCString(ctx, basename_atom); - JS_FreeAtom(ctx, basename_atom); - if (!basename) - goto fail; - - /* module name */ - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - - args = malloc(sizeof(*args)); - if (!args) - goto oom_fail; - memset(args, 0, sizeof(*args)); - args->filename = strdup(filename); - args->basename = strdup(basename); - - /* ports */ - args->recv_pipe = js_new_message_pipe(); - if (!args->recv_pipe) - goto oom_fail; - args->send_pipe = js_new_message_pipe(); - if (!args->send_pipe) - goto oom_fail; - - args->strip_flags = JS_GetStripInfo(rt); - - obj = js_worker_ctor_internal(ctx, new_target, - args->send_pipe, args->recv_pipe); - if (JS_IsException(obj)) - goto fail; - - pthread_attr_init(&attr); - /* no join at the end */ - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - ret = pthread_create(&tid, &attr, worker_func, args); - pthread_attr_destroy(&attr); - if (ret != 0) { - JS_ThrowTypeError(ctx, "could not create worker"); - goto fail; - } - JS_FreeCString(ctx, basename); - JS_FreeCString(ctx, filename); - return obj; - oom_fail: - JS_ThrowOutOfMemory(ctx); - fail: - JS_FreeCString(ctx, basename); - JS_FreeCString(ctx, filename); - if (args) { - free(args->filename); - free(args->basename); - js_free_message_pipe(args->recv_pipe); - js_free_message_pipe(args->send_pipe); - free(args); - } - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); - JSWorkerMessagePipe *ps; - size_t data_len, sab_tab_len, i; - uint8_t *data; - JSWorkerMessage *msg; - uint8_t **sab_tab; - - if (!worker) - return JS_EXCEPTION; - - data = JS_WriteObject2(ctx, &data_len, argv[0], - JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, - &sab_tab, &sab_tab_len); - if (!data) - return JS_EXCEPTION; - - msg = malloc(sizeof(*msg)); - if (!msg) - goto fail; - msg->data = NULL; - msg->sab_tab = NULL; - - /* must reallocate because the allocator may be different */ - msg->data = malloc(data_len); - if (!msg->data) - goto fail; - memcpy(msg->data, data, data_len); - msg->data_len = data_len; - - if (sab_tab_len > 0) { - msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len); - if (!msg->sab_tab) - goto fail; - memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len); - } - msg->sab_tab_len = sab_tab_len; - - js_free(ctx, data); - js_free(ctx, sab_tab); - - /* increment the SAB reference counts */ - for(i = 0; i < msg->sab_tab_len; i++) { - js_sab_dup(NULL, msg->sab_tab[i]); - } - - ps = worker->send_pipe; - pthread_mutex_lock(&ps->mutex); - /* indicate that data is present */ - if (list_empty(&ps->msg_queue)) - js_waker_signal(&ps->waker); - list_add_tail(&msg->link, &ps->msg_queue); - pthread_mutex_unlock(&ps->mutex); - return JS_UNDEFINED; - fail: - if (msg) { - free(msg->data); - free(msg->sab_tab); - free(msg); - } - js_free(ctx, data); - js_free(ctx, sab_tab); - return JS_EXCEPTION; - -} - -static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, - JSValueConst func) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); - JSWorkerMessageHandler *port; - - if (!worker) - return JS_EXCEPTION; - - port = worker->msg_handler; - if (JS_IsNull(func)) { - if (port) { - js_free_port(rt, port); - worker->msg_handler = NULL; - } - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - if (!port) { - port = js_mallocz(ctx, sizeof(*port)); - if (!port) - return JS_EXCEPTION; - port->recv_pipe = js_dup_message_pipe(worker->recv_pipe); - port->on_message_func = JS_NULL; - list_add_tail(&port->link, &ts->port_list); - worker->msg_handler = port; - } - JS_FreeValue(ctx, port->on_message_func); - port->on_message_func = JS_DupValue(ctx, func); - } - return JS_UNDEFINED; -} - -static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val) -{ - JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); - JSWorkerMessageHandler *port; - if (!worker) - return JS_EXCEPTION; - port = worker->msg_handler; - if (port) { - return JS_DupValue(ctx, port->on_message_func); - } else { - return JS_NULL; - } -} - -static const JSCFunctionListEntry js_worker_proto_funcs[] = { - JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ), - JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ), -}; - -#endif /* USE_WORKER */ - -void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) -{ -#ifdef USE_WORKER - js_worker_new_context_func = func; -#endif -} - -#if defined(_WIN32) -#define OS_PLATFORM "win32" -#elif defined(__APPLE__) -#define OS_PLATFORM "darwin" -#elif defined(EMSCRIPTEN) -#define OS_PLATFORM "js" -#else -#define OS_PLATFORM "linux" -#endif - -#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) - -static const JSCFunctionListEntry js_os_funcs[] = { - JS_CFUNC_DEF("open", 2, js_os_open ), - OS_FLAG(O_RDONLY), - OS_FLAG(O_WRONLY), - OS_FLAG(O_RDWR), - OS_FLAG(O_APPEND), - OS_FLAG(O_CREAT), - OS_FLAG(O_EXCL), - OS_FLAG(O_TRUNC), -#if defined(_WIN32) - OS_FLAG(O_BINARY), - OS_FLAG(O_TEXT), -#endif - JS_CFUNC_DEF("close", 1, js_os_close ), - JS_CFUNC_DEF("seek", 3, js_os_seek ), - JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), - JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), - JS_CFUNC_DEF("isatty", 1, js_os_isatty ), - JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), - JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), - JS_CFUNC_DEF("remove", 1, js_os_remove ), - JS_CFUNC_DEF("rename", 2, js_os_rename ), - JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), - JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), - JS_CFUNC_DEF("signal", 2, js_os_signal ), - OS_FLAG(SIGINT), - OS_FLAG(SIGABRT), - OS_FLAG(SIGFPE), - OS_FLAG(SIGILL), - OS_FLAG(SIGSEGV), - OS_FLAG(SIGTERM), -#if !defined(_WIN32) - OS_FLAG(SIGQUIT), - OS_FLAG(SIGPIPE), - OS_FLAG(SIGALRM), - OS_FLAG(SIGUSR1), - OS_FLAG(SIGUSR2), - OS_FLAG(SIGCHLD), - OS_FLAG(SIGCONT), - OS_FLAG(SIGSTOP), - OS_FLAG(SIGTSTP), - OS_FLAG(SIGTTIN), - OS_FLAG(SIGTTOU), -#endif - JS_CFUNC_DEF("now", 0, js_os_now ), - JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ), - JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), - JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ), - JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), - JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), - JS_CFUNC_DEF("chdir", 0, js_os_chdir ), - JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), - JS_CFUNC_DEF("readdir", 1, js_os_readdir ), - /* st_mode constants */ - OS_FLAG(S_IFMT), - OS_FLAG(S_IFIFO), - OS_FLAG(S_IFCHR), - OS_FLAG(S_IFDIR), - OS_FLAG(S_IFBLK), - OS_FLAG(S_IFREG), -#if !defined(_WIN32) - OS_FLAG(S_IFSOCK), - OS_FLAG(S_IFLNK), - OS_FLAG(S_ISGID), - OS_FLAG(S_ISUID), -#endif - JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), - JS_CFUNC_DEF("utimes", 3, js_os_utimes ), - JS_CFUNC_DEF("sleep", 1, js_os_sleep ), - JS_CFUNC_DEF("realpath", 1, js_os_realpath ), -#if !defined(_WIN32) - JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), - JS_CFUNC_DEF("symlink", 2, js_os_symlink ), - JS_CFUNC_DEF("readlink", 1, js_os_readlink ), - JS_CFUNC_DEF("exec", 1, js_os_exec ), - JS_CFUNC_DEF("getpid", 0, js_os_getpid ), - JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), - OS_FLAG(WNOHANG), - JS_CFUNC_DEF("pipe", 0, js_os_pipe ), - JS_CFUNC_DEF("kill", 2, js_os_kill ), - JS_CFUNC_DEF("dup", 1, js_os_dup ), - JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), -#endif -}; - -static int js_os_init(JSContext *ctx, JSModuleDef *m) -{ - os_poll_func = js_os_poll; - -#ifdef USE_WORKER - { - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSValue proto, obj; - /* Worker class */ - JS_NewClassID(&js_worker_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_worker_class_id, &js_worker_class); - proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); - - obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, - JS_CFUNC_constructor, 0); - JS_SetConstructor(ctx, obj, proto); - - JS_SetClassProto(ctx, js_worker_class_id, proto); - - /* set 'Worker.parent' if necessary */ - if (ts->recv_pipe && ts->send_pipe) { - JS_DefinePropertyValueStr(ctx, obj, "parent", - js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), - JS_PROP_C_W_E); - } - - JS_SetModuleExport(ctx, m, "Worker", obj); - } -#endif /* USE_WORKER */ - - return JS_SetModuleExportList(ctx, m, js_os_funcs, - countof(js_os_funcs)); -} - -JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_os_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); -#ifdef USE_WORKER - JS_AddModuleExport(ctx, m, "Worker"); -#endif - return m; -} - -/**********************************************************/ - -static JSValue js_print(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int i; - JSValueConst v; - - for(i = 0; i < argc; i++) { - if (i != 0) - putchar(' '); - v = argv[i]; - if (JS_IsString(v)) { - const char *str; - size_t len; - str = JS_ToCStringLen(ctx, &len, v); - if (!str) - return JS_EXCEPTION; - fwrite(str, 1, len, stdout); - JS_FreeCString(ctx, str); - } else { - JS_PrintValue(ctx, js_print_value_write, stdout, v, NULL); - } - } - putchar('\n'); - return JS_UNDEFINED; -} - -static JSValue js_console_log(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue ret; - ret = js_print(ctx, this_val, argc, argv); - fflush(stdout); - return ret; -} - -void js_std_add_helpers(JSContext *ctx, int argc, char **argv) -{ - JSValue global_obj, console, args, performance; - int i; - - /* XXX: should these global definitions be enumerable? */ - global_obj = JS_GetGlobalObject(ctx); - - console = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, console, "log", - JS_NewCFunction(ctx, js_console_log, "log", 1)); - JS_SetPropertyStr(ctx, global_obj, "console", console); - - performance = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, performance, "now", - JS_NewCFunction(ctx, js_os_now, "now", 0)); - JS_SetPropertyStr(ctx, global_obj, "performance", performance); - - /* same methods as the mozilla JS shell */ - if (argc >= 0) { - args = JS_NewArray(ctx); - for(i = 0; i < argc; i++) { - JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); - } - JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); - } - - JS_SetPropertyStr(ctx, global_obj, "print", - JS_NewCFunction(ctx, js_print, "print", 1)); - JS_SetPropertyStr(ctx, global_obj, "__loadScript", - JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1)); - - JS_FreeValue(ctx, global_obj); -} - -void js_std_init_handlers(JSRuntime *rt) -{ - JSThreadState *ts; - - ts = malloc(sizeof(*ts)); - if (!ts) { - fprintf(stderr, "Could not allocate memory for the worker"); - exit(1); - } - memset(ts, 0, sizeof(*ts)); - init_list_head(&ts->os_rw_handlers); - init_list_head(&ts->os_signal_handlers); - init_list_head(&ts->os_timers); - init_list_head(&ts->port_list); - init_list_head(&ts->rejected_promise_list); - ts->next_timer_id = 1; - - JS_SetRuntimeOpaque(rt, ts); - -#ifdef USE_WORKER - /* set the SharedArrayBuffer memory handlers */ - { - JSSharedArrayBufferFunctions sf; - memset(&sf, 0, sizeof(sf)); - sf.sab_alloc = js_sab_alloc; - sf.sab_free = js_sab_free; - sf.sab_dup = js_sab_dup; - JS_SetSharedArrayBufferFunctions(rt, &sf); - } -#endif -} - -void js_std_free_handlers(JSRuntime *rt) -{ - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - struct list_head *el, *el1; - - list_for_each_safe(el, el1, &ts->os_rw_handlers) { - JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); - free_rw_handler(rt, rh); - } - - list_for_each_safe(el, el1, &ts->os_signal_handlers) { - JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); - free_sh(rt, sh); - } - - list_for_each_safe(el, el1, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - free_timer(rt, th); - } - - list_for_each_safe(el, el1, &ts->rejected_promise_list) { - JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); - JS_FreeValueRT(rt, rp->promise); - JS_FreeValueRT(rt, rp->reason); - free(rp); - } - -#ifdef USE_WORKER - js_free_message_pipe(ts->recv_pipe); - js_free_message_pipe(ts->send_pipe); - - list_for_each_safe(el, el1, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - /* unlink the message ports. They are freed by the Worker object */ - port->link.prev = NULL; - port->link.next = NULL; - } -#endif - - free(ts); - JS_SetRuntimeOpaque(rt, NULL); /* fail safe */ -} - -static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val) -{ - JS_PrintValue(ctx, js_print_value_write, stderr, exception_val, NULL); - fputc('\n', stderr); -} - -void js_std_dump_error(JSContext *ctx) -{ - JSValue exception_val; - - exception_val = JS_GetException(ctx); - js_std_dump_error1(ctx, exception_val); - JS_FreeValue(ctx, exception_val); -} - -static JSRejectedPromiseEntry *find_rejected_promise(JSContext *ctx, JSThreadState *ts, - JSValueConst promise) -{ - struct list_head *el; - - list_for_each(el, &ts->rejected_promise_list) { - JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); - if (JS_SameValue(ctx, rp->promise, promise)) - return rp; - } - return NULL; -} - -void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, - JSValueConst reason, - BOOL is_handled, void *opaque) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSRejectedPromiseEntry *rp; - - if (!is_handled) { - /* add a new entry if needed */ - rp = find_rejected_promise(ctx, ts, promise); - if (!rp) { - rp = malloc(sizeof(*rp)); - if (rp) { - rp->promise = JS_DupValue(ctx, promise); - rp->reason = JS_DupValue(ctx, reason); - list_add_tail(&rp->link, &ts->rejected_promise_list); - } - } - } else { - /* the rejection is handled, so the entry can be removed if present */ - rp = find_rejected_promise(ctx, ts, promise); - if (rp) { - JS_FreeValue(ctx, rp->promise); - JS_FreeValue(ctx, rp->reason); - list_del(&rp->link); - free(rp); - } - } -} - -/* check if there are pending promise rejections. It must be done - asynchrously in case a rejected promise is handled later. Currently - we do it once the application is about to sleep. It could be done - more often if needed. */ -static void js_std_promise_rejection_check(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - struct list_head *el; - - if (unlikely(!list_empty(&ts->rejected_promise_list))) { - list_for_each(el, &ts->rejected_promise_list) { - JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); - fprintf(stderr, "Possibly unhandled promise rejection: "); - js_std_dump_error1(ctx, rp->reason); - } - exit(1); - } -} - -/* main loop which calls the user JS callbacks */ -void js_std_loop(JSContext *ctx) -{ - int err; - - for(;;) { - /* execute the pending jobs */ - for(;;) { - err = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL); - if (err <= 0) { - if (err < 0) - js_std_dump_error(ctx); - break; - } - } - - js_std_promise_rejection_check(ctx); - - if (!os_poll_func || os_poll_func(ctx)) - break; - } -} - -/* Wait for a promise and execute pending jobs while waiting for - it. Return the promise result or JS_EXCEPTION in case of promise - rejection. */ -JSValue js_std_await(JSContext *ctx, JSValue obj) -{ - JSValue ret; - int state; - - for(;;) { - state = JS_PromiseState(ctx, obj); - if (state == JS_PROMISE_FULFILLED) { - ret = JS_PromiseResult(ctx, obj); - JS_FreeValue(ctx, obj); - break; - } else if (state == JS_PROMISE_REJECTED) { - ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); - JS_FreeValue(ctx, obj); - break; - } else if (state == JS_PROMISE_PENDING) { - int err; - err = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL); - if (err < 0) { - js_std_dump_error(ctx); - } - if (err == 0) { - js_std_promise_rejection_check(ctx); - - if (os_poll_func) - os_poll_func(ctx); - } - } else { - /* not a promise */ - ret = obj; - break; - } - } - return ret; -} - -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int load_only) -{ - JSValue obj, val; - obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); - if (JS_IsException(obj)) - goto exception; - if (load_only) { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - js_module_set_import_meta(ctx, obj, FALSE, FALSE); - } - JS_FreeValue(ctx, obj); - } else { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - if (JS_ResolveModule(ctx, obj) < 0) { - JS_FreeValue(ctx, obj); - goto exception; - } - js_module_set_import_meta(ctx, obj, FALSE, TRUE); - val = JS_EvalFunction(ctx, obj); - val = js_std_await(ctx, val); - } else { - val = JS_EvalFunction(ctx, obj); - } - if (JS_IsException(val)) { - exception: - js_std_dump_error(ctx); - exit(1); - } - JS_FreeValue(ctx, val); - } -} - -void js_std_eval_binary_json_module(JSContext *ctx, - const uint8_t *buf, size_t buf_len, - const char *module_name) -{ - JSValue obj; - JSModuleDef *m; - - obj = JS_ReadObject(ctx, buf, buf_len, 0); - if (JS_IsException(obj)) - goto exception; - m = create_json_module(ctx, module_name, obj); - if (!m) { - exception: - js_std_dump_error(ctx); - exit(1); - } -} - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h deleted file mode 100755 index 5c8301b7..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * QuickJS C library - * - * 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 QUICKJS_LIBC_H -#define QUICKJS_LIBC_H - -#include -#include - -#include "quickjs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); -JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); -void js_std_add_helpers(JSContext *ctx, int argc, char **argv); -void js_std_loop(JSContext *ctx); -JSValue js_std_await(JSContext *ctx, JSValue obj); -void js_std_init_handlers(JSRuntime *rt); -void js_std_free_handlers(JSRuntime *rt); -void js_std_dump_error(JSContext *ctx); -uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); -int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - JS_BOOL use_realpath, JS_BOOL is_main); -int js_module_test_json(JSContext *ctx, JSValueConst attributes); -int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes); -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque, - JSValueConst attributes); -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags); -void js_std_eval_binary_json_module(JSContext *ctx, - const uint8_t *buf, size_t buf_len, - const char *module_name); -void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, - JSValueConst reason, - JS_BOOL is_handled, void *opaque); -void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_LIBC_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h deleted file mode 100755 index d9385213..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h +++ /dev/null @@ -1,365 +0,0 @@ -/* - * 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(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, 3, 0, 1, var_ref) /* push undefined if the variable does not exist */ -DEF( get_var, 3, 0, 1, var_ref) /* throw an exception if the variable does not exist */ -DEF( put_var, 3, 1, 0, var_ref) /* must come after get_var */ -DEF( put_var_init, 3, 1, 0, var_ref) /* 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( 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( get_array_el3, 1, 2, 3, none) /* obj prop -> obj prop1 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_loc_checkthis, 3, 0, 1, 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( 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( 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(for_await_of_next, 1, 3, 4, none) /* iter next catch_offset -> iter next catch_offset obj */ -DEF(iterator_check_object, 1, 1, 1, none) -DEF(iterator_get_value_done, 1, 2, 3, none) /* catch_offset obj -> catch_offset value done */ -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) - -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( pow, 1, 2, 1, none) -DEF( shl, 1, 2, 1, none) -DEF( sar, 1, 2, 1, none) -DEF( shr, 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( and, 1, 2, 1, none) -DEF( xor, 1, 2, 1, none) -DEF( or, 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 */ - -/* the following opcodes must be in the same order as the 'with_x' and - get_var_undef, get_var and put_var opcodes */ -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_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */ -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( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ - -#if SHORT_OPCODES -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, 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) -#endif - -#undef DEF -#undef def -#endif /* DEF */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c deleted file mode 100755 index a3b74e08..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c +++ /dev/null @@ -1,59580 +0,0 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2025 Fabrice Bellard - * Copyright (c) 2017-2025 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#elif defined(__linux__) || defined(__GLIBC__) -#include -#elif defined(__FreeBSD__) -#include -#endif - -#include "cutils.h" -#include "list.h" -#include "quickjs.h" -#include "libregexp.h" -#include "libunicode.h" -#include "dtoa.h" - -#define OPTIMIZE 1 -#define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) -#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(_WIN32) -/* define it if printf uses the RNDN rounding mode instead of RNDNA */ -#define CONFIG_PRINTF_RNDN -#endif - -/* define to include Atomics.* operations which depend on the OS - threads */ -#if !defined(EMSCRIPTEN) -#define CONFIG_ATOMICS -#endif - -#if !defined(EMSCRIPTEN) -/* enable stack limitation */ -#define CONFIG_STACK_CHECK -#endif - - -/* dump object free */ -//#define DUMP_FREE -//#define DUMP_CLOSURE -/* dump the bytecode of the compiled functions: combination of bits - 1: dump pass 3 final byte code - 2: dump pass 2 code - 4: dump pass 1 code - 8: dump stdlib functions - 16: dump bytecode in hex - 32: dump line number table - 64: dump compute_stack_size - */ -//#define DUMP_BYTECODE (1) -/* dump the occurence of the automatic GC */ -//#define DUMP_GC -/* dump objects freed by the garbage collector */ -//#define DUMP_GC_FREE -/* dump objects leaking when freeing the runtime */ -//#define DUMP_LEAKS 1 -/* dump memory usage before running the garbage collector */ -//#define DUMP_MEM -//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ -//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ -//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ -//#define DUMP_MODULE_RESOLVE -//#define DUMP_MODULE_EXEC -//#define DUMP_PROMISE -//#define DUMP_READ_OBJECT -//#define DUMP_ROPE_REBALANCE - -/* test the GC by forcing it before each object allocation */ -//#define FORCE_GC_AT_MALLOC - -#ifdef CONFIG_ATOMICS -#include -#include -#include -#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, /* u.array | 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_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, /* u.map_iterator_data */ - 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_GLOBAL_OBJECT, /* u.global_object */ - 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_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 */ -} JSErrorEnum; - -/* the variable and scope indexes must fit on 16 bits. The (-1) and - ARG_SCOPE_END values are reserved. */ -#define JS_MAX_LOCAL_VARS 65534 -#define JS_STACK_SIZE_MAX 65534 -#define JS_STRING_LEN_MAX ((1 << 30) - 1) - -/* 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 JSObject JSObject; - -#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 enum OPCodeEnum OPCodeEnum; - -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 */ - - 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; - struct list_head weakref_list; /* list of JSWeakRefHeader.link */ -#ifdef 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 the current exception cannot be catched */ - BOOL current_exception_is_uncatchable : 8; - /* true if inside an out of memory error, to avoid recursing */ - BOOL in_out_of_memory : 8; - - struct JSStackFrame *current_stack_frame; - - JSInterruptHandler *interrupt_handler; - void *interrupt_opaque; - - 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; - - BOOL can_block : 8; /* TRUE if Atomics.wait can block */ - /* used to allocate, free and clone SharedArrayBuffers */ - JSSharedArrayBufferFunctions sab_funcs; - /* see JS_SetStripInfo() */ - uint8_t strip_flags; - - /* 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; -}; - -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; -}; - -#define JS_MODE_STRICT (1 << 0) -#define JS_MODE_ASYNC (1 << 2) /* async function */ -#define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */ - -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 */ - const uint8_t *cur_pc; /* only used in bytecode functions : PC of the - instruction after the call */ - int arg_count; - int js_mode; /* not supported for C functions */ - /* 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, - JS_GC_OBJ_TYPE_MODULE, -} 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 enum { - JS_WEAKREF_TYPE_MAP, - JS_WEAKREF_TYPE_WEAKREF, - JS_WEAKREF_TYPE_FINREC, -} JSWeakRefHeaderTypeEnum; - -typedef struct { - struct list_head link; - JSWeakRefHeaderTypeEnum weakref_type; -} JSWeakRefHeader; - -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; - -/* bigint */ - -#if JS_LIMB_BITS == 32 - -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 - -#else - -typedef __int128 int128_t; -typedef unsigned __int128 uint128_t; -typedef int64_t js_slimb_t; -typedef uint64_t js_limb_t; -typedef int128_t js_sdlimb_t; -typedef uint128_t js_dlimb_t; - -#define JS_LIMB_DIGITS 19 - -#endif - -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, -} JSAutoInitIDEnum; - -/* 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; - int binary_object_size; - /* 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; - - 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 iterator_ctor; - 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 */ - - 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 flags, int scope_idx); - void *user_opaque; -}; - -typedef union JSFloat64Union { - double d; - uint64_t u64; - uint32_t u32[2]; -} JSFloat64Union; - -enum { - JS_ATOM_TYPE_STRING = 1, - JS_ATOM_TYPE_GLOBAL_SYMBOL, - JS_ATOM_TYPE_SYMBOL, - JS_ATOM_TYPE_PRIVATE, -}; - -typedef enum { - JS_ATOM_KIND_STRING, - JS_ATOM_KIND_SYMBOL, - JS_ATOM_KIND_PRIVATE, -} JSAtomKindEnum; - -#define JS_ATOM_HASH_MASK ((1 << 30) - 1) -#define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK - -struct JSString { - JSRefCountHeader header; /* must come first, 32-bit */ - uint32_t len : 31; - uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ - /* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3, - for JS_ATOM_TYPE_PRIVATE: hash = JS_ATOM_HASH_PRIVATE, atom_type = 3 - XXX: could change encoding to have one more bit in hash */ - uint32_t hash : 30; - uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ - uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ -#ifdef DUMP_LEAKS - struct list_head link; /* string list */ -#endif - union { - uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ - uint16_t str16[0]; - } u; -}; - -typedef 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 */ - /* XXX: could reduce memory usage by using a direct pointer with - bit 0 to select rope or string */ - JSValue left; - JSValue right; /* might be the empty string */ -} JSStringRope; - -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 a 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' in 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 { - JSClosureTypeEnum closure_type : 3; - 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 */ - uint16_t var_idx; /* is_local = TRUE: 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 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 */ - JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in JSVarDef */ -} JSVarKindEnum; - -typedef struct JSBytecodeVarDef { - JSAtom var_name; - /* index into JSFunctionBytecode.vars of the next variable in the same or - enclosing lexical scope - */ - int scope_next; /* XXX: store on 16 bits */ - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */ - uint8_t has_scope: 1; /* true if JSVarDef.scope_level != 0 */ - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* If is_captured = TRUE, provides, the index of the corresponding - JSVarRef on stack. It would be more compact to have a separate - table with the corresponding inverted table but it requires - more modifications in the code. */ - uint16_t var_ref_idx; -} JSBytecodeVarDef; - -/* 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 js_mode; - 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 has_debug : 1; - uint8_t read_only_bytecode : 1; - uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */ - /* XXX: 10 bits available */ - uint8_t *byte_code_buf; /* (self pointer) */ - int byte_code_len; - JSAtom func_name; - JSBytecodeVarDef *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 */ - JSContext *realm; /* function realm */ - JSValue *cpool; /* constant pool (self pointer) */ - int cpool_count; - int closure_var_count; - struct { - /* debug info, move to separate structure to save memory? */ - JSAtom filename; - int source_len; - int pc2line_len; - uint8_t *pc2line_buf; - char *source; - } debug; -} JSFunctionBytecode; - -typedef struct JSBoundFunction { - JSValue func_obj; - JSValue this_val; - int argc; - JSValue argv[0]; -} JSBoundFunction; - -typedef enum JSIteratorKindEnum { - JS_ITERATOR_KIND_KEY, - JS_ITERATOR_KIND_VALUE, - JS_ITERATOR_KIND_KEY_AND_VALUE, -} JSIteratorKindEnum; - -typedef struct JSForInIterator { - JSValue obj; - uint32_t idx; - uint32_t atom_count; - uint8_t in_prototype_chain; - uint8_t is_array; - JSPropertyEnum *tab_atom; /* is_array = FALSE */ -} 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 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 JSGlobalObject { - JSValue uninitialized_vars; /* hidden object containing the list of uninitialized variables */ -} JSGlobalObject; - -typedef struct JSAsyncFunctionState { - JSGCObjectHeader header; - JSValue this_val; /* 'this' argument */ - int argc; /* number of function arguments */ - BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ - BOOL is_completed; /* TRUE if the function has returned. The stack - frame is no longer valid */ - JSValue resolving_funcs[2]; /* only used in JS async functions */ - JSStackFrame frame; - /* arg_buf, var_buf, stack_buf and var_refs follow */ -} JSAsyncFunctionState; - -typedef enum { - /* binary operators */ - JS_OVOP_ADD, - JS_OVOP_SUB, - JS_OVOP_MUL, - JS_OVOP_DIV, - JS_OVOP_MOD, - JS_OVOP_POW, - JS_OVOP_OR, - JS_OVOP_AND, - JS_OVOP_XOR, - JS_OVOP_SHL, - JS_OVOP_SAR, - JS_OVOP_SHR, - JS_OVOP_EQ, - JS_OVOP_LESS, - - JS_OVOP_BINARY_COUNT, - /* unary operators */ - JS_OVOP_POS = JS_OVOP_BINARY_COUNT, - JS_OVOP_NEG, - JS_OVOP_INC, - JS_OVOP_DEC, - JS_OVOP_NOT, - - JS_OVOP_COUNT, -} JSOverloadableOperatorEnum; - -typedef struct { - uint32_t operator_index; - JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ -} JSBinaryOperatorDefEntry; - -typedef struct { - int count; - JSBinaryOperatorDefEntry *tab; -} JSBinaryOperatorDef; - -typedef struct { - uint32_t operator_counter; - BOOL is_primitive; /* OperatorSet for a primitive type */ - /* NULL if no operator is defined */ - JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ - JSBinaryOperatorDef left; - JSBinaryOperatorDef right; -} JSOperatorSetData; - -typedef struct JSReqModuleEntry { - JSAtom module_name; - JSModuleDef *module; /* used using resolution */ - JSValue attributes; /* JS_UNDEFINED or an object contains 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 */ - BOOL is_star; /* import_name = '*' is a valid import name, so need a flag */ - 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 { - JSGCObjectHeader header; /* must come first */ - 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 : 8; /* true if func_obj contains await */ - BOOL resolved : 8; - BOOL func_created : 8; - 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; /* true: async_evaluation_timestamp corresponds to [[AsyncEvaluationOrder]] - false: [[AsyncEvaluationOrder]] is UNSET or DONE */ - 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 : 8; - 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 *realm; - JSJobFunc *job_func; - int argc; - JSValue argv[0]; -} 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 */ - -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[0]; /* 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 has_immutable_prototype : 1; /* cannot modify the prototype */ - 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 */ - }; - }; - /* count the number of weak references to this object. The object - structure is freed only if header.ref_count = 0 and - weakref_count = 0 */ - uint32_t weakref_count; - JSShape *shape; /* prototype and property names + flag */ - JSProperty *prop; /* array of properties */ - union { - void *opaque; - struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ - struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ - 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 JSAsyncFunctionState *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 */ - JSGlobalObject global_object; - } u; -}; - -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - BOOL empty : 8; /* TRUE if the record is deleted */ - struct list_head link; - struct JSMapRecord *hash_next; - 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; - JSMapRecord **hash_table; - int hash_bits; - uint32_t hash_size; /* = 2 ^ hash_bits */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ - JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */ -} JSMapState; - -enum { - __JS_ATOM_NULL = JS_ATOM_NULL, -#define DEF(name, str) JS_ATOM_ ## name, -#include "quickjs-atom.h" -#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" -#include "quickjs-atom.h" -#undef DEF -; - -typedef enum OPCodeFormat { -#define FMT(f) OP_FMT_ ## f, -#define DEF(id, size, n_pop, n_push, f) -#include "quickjs-opcode.h" -#undef DEF -#undef FMT -} OPCodeFormat; - -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) -#include "quickjs-opcode.h" -#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, -#include "quickjs-opcode.h" -#undef def -#undef DEF -#undef FMT - OP_TEMP_END, -}; - -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, JSValue *argv, int flags); -static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValue *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); -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); -static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); -static __maybe_unused void JS_DumpString(JSRuntime *rt, const 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_DumpAtom(JSContext *ctx, const char *str, JSAtom atom); -static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val); -static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val); -static __maybe_unused void JS_DumpShapes(JSRuntime *rt); -static void js_dump_value_write(void *opaque, const char *buf, size_t len); -static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -static void js_array_finalizer(JSRuntime *rt, JSValue val); -static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValue val); -static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_object_data_finalizer(JSRuntime *rt, JSValue val); -static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_c_function_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_regexp_finalizer(JSRuntime *rt, JSValue val); -static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_proxy_finalizer(JSRuntime *rt, JSValue val); -static void js_proxy_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_finalizer(JSRuntime *rt, JSValue val); -static void js_map_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_iterator_concat_finalizer(JSRuntime *rt, JSValue val); -static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val); -static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val); -static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_generator_finalizer(JSRuntime *rt, JSValue obj); -static void js_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_global_object_finalizer(JSRuntime *rt, JSValue obj); -static void js_global_object_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_promise_finalizer(JSRuntime *rt, JSValue val); -static void js_promise_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue 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_new_string8_len(JSContext *ctx, const char *buf, int len); -static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, - JSValueConst flags); -static JSValue JS_NewRegexp(JSContext *ctx, 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); - -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, JSValueConst op1, JSValueConst 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_ToObject(JSContext *ctx, JSValueConst val); -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); -JSValue JS_ThrowOutOfMemory(JSContext *ctx); -static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); - -static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); -static int JS_CreateProperty(JSContext *ctx, JSObject *p, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); -static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2, - int pos2, int len); -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 typed_array_is_oob(JSObject *p); -static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj); -static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); -static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); -static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical); -static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, - BOOL is_arg); -static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); -static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); -static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags); -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue 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 flags, int scope_idx); -static void js_free_module_def(JSRuntime *rt, 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 BOOL js_string_eq(JSContext *ctx, - const JSString *p1, const JSString *p2); -static int js_string_compare(JSContext *ctx, - const JSString *p1, const JSString *p2); -static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); -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 void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); -static int 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 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 BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, - JSValue **arrpp, uint32_t *countp); -static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, - JSValueConst sync_iter); -static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv, int flags); -static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); -static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, - JSGCObjectTypeEnum type); -static void remove_gc_object(JSGCObjectHeader *h); -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 JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_map); -static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh); -static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh); -static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh); -static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects); -static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, - JSValueConst obj, JSValueConst method); -static int js_string_find_invalid_codepoint(JSString *p); -static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p, - JSAtom atom, BOOL is_lexical); - -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 JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; - -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 DUMP_GC - printf("GC: size=%" PRIu64 "\n", - (uint64_t)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_malloc_rt(JSRuntime *rt, size_t size) -{ - return rt->mf.js_malloc(&rt->malloc_state, size); -} - -void js_free_rt(JSRuntime *rt, void *ptr) -{ - rt->mf.js_free(&rt->malloc_state, ptr); -} - -void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) -{ - return rt->mf.js_realloc(&rt->malloc_state, ptr, size); -} - -size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) -{ - return rt->mf.js_malloc_usable_size(ptr); -} - -void *js_mallocz_rt(JSRuntime *rt, size_t size) -{ - void *ptr; - ptr = js_malloc_rt(rt, size); - if (!ptr) - return NULL; - return memset(ptr, 0, size); -} - -/* 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 inline void js_dbuf_init(JSContext *ctx, DynBuf *s) -{ - dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); -} - -static void *js_realloc_bytecode_rt(void *opaque, void *ptr, size_t size) -{ - JSRuntime *rt = opaque; - if (size > (INT32_MAX / 2)) { - /* the bytecode cannot be larger than 2G. Leave some slack to - avoid some overflows. */ - return NULL; - } else { - return rt->mf.js_realloc(&rt->malloc_state, ptr, size); - } -} - -static inline void js_dbuf_bytecode_init(JSContext *ctx, DynBuf *s) -{ - dbuf_init2(s, ctx->rt, js_realloc_bytecode_rt); -} - -static inline int is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static inline int string_get(const JSString *p, int idx) { - return p->is_wide_char ? p->u.str16[idx] : p->u.str8[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_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, js_map_mark }, /* JS_CLASS_WEAKMAP */ - { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* 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 */ - { JS_ATOM_Object, js_global_object_finalizer, js_global_object_mark }, /* JS_CLASS_GLOBAL_OBJECT */ -}; - -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; -} - -#if !defined(CONFIG_STACK_CHECK) -/* no stack limitation */ -static inline uintptr_t js_get_stack_pointer(void) -{ - return 0; -} - -static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -{ - return FALSE; -} -#else -/* Note: OS and CPU dependent */ -static inline uintptr_t js_get_stack_pointer(void) -{ - return (uintptr_t)__builtin_frame_address(0); -} - -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); -} -#endif - -JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) -{ - JSRuntime *rt; - JSMallocState ms; - - memset(&ms, 0, sizeof(ms)); - ms.opaque = opaque; - ms.malloc_limit = -1; - - rt = mf->js_malloc(&ms, sizeof(JSRuntime)); - if (!rt) - return NULL; - memset(rt, 0, sizeof(*rt)); - 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; - } - 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; - init_list_head(&rt->weakref_list); - -#ifdef 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_c_function_data_call; - rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; - rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; - if (init_shape_hash(rt)) - goto fail; - - rt->stack_size = JS_DEFAULT_STACK_SIZE; - 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; -} - -/* default memory allocation functions with memory limitation */ -static size_t js_def_malloc_usable_size(const void *ptr) -{ -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize((void *)ptr); -#elif defined(EMSCRIPTEN) - return 0; -#elif defined(__linux__) || defined(__GLIBC__) - return malloc_usable_size((void *)ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size((void *)ptr); -#endif -} - -static void *js_def_malloc(JSMallocState *s, size_t size) -{ - void *ptr; - - /* Do not allocate zero bytes: behavior is platform dependent */ - assert(size != 0); - - if (unlikely(s->malloc_size + size > s->malloc_limit)) - return NULL; - - ptr = malloc(size); - if (!ptr) - return NULL; - - s->malloc_count++; - s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - return ptr; -} - -static void js_def_free(JSMallocState *s, void *ptr) -{ - if (!ptr) - return; - - s->malloc_count--; - s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); -} - -static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) -{ - size_t old_size; - - if (!ptr) { - if (size == 0) - return NULL; - return js_def_malloc(s, size); - } - old_size = js_def_malloc_usable_size(ptr); - if (size == 0) { - s->malloc_count--; - s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); - return NULL; - } - if (s->malloc_size + size - old_size > s->malloc_limit) - return NULL; - - ptr = realloc(ptr, size); - if (!ptr) - return NULL; - - s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; - return ptr; -} - -static const JSMallocFunctions def_malloc_funcs = { - js_def_malloc, - js_def_free, - js_def_realloc, - js_def_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; -} - -/* 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; -} - -void JS_SetStripInfo(JSRuntime *rt, int flags) -{ - rt->strip_flags = flags; -} - -int JS_GetStripInfo(JSRuntime *rt) -{ - return rt->strip_flags; -} - -/* 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; - - e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); - if (!e) - return -1; - e->realm = JS_DupContext(ctx); - e->job_func = job_func; - e->argc = argc; - for(i = 0; i < argc; i++) { - e->argv[i] = JS_DupValue(ctx, 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' - if pctx != NULL. It may be NULL if the context was already - destroyed or if no job was pending. The 'pctx' parameter is now - absolete. */ -int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) -{ - JSContext *ctx; - JSJobEntry *e; - JSValue res; - int i, ret; - - if (list_empty(&rt->job_list)) { - if (pctx) - *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->realm; - res = e->job_func(ctx, e->argc, (JSValueConst *)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); - if (pctx) { - if (ctx->header.ref_count > 1) - *pctx = ctx; - else - *pctx = NULL; - } - JS_FreeContext(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->atom_type = 0; - str->hash = 0; /* optional but costless */ - str->hash_next = 0; /* optional */ -#ifdef 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; -} - -/* same as JS_FreeValueRT() but faster */ -static inline void js_free_string(JSRuntime *rt, JSString *str) -{ - if (--str->header.ref_count <= 0) { - if (str->atom_type) { - JS_FreeAtomStruct(rt, str); - } else { -#ifdef DUMP_LEAKS - list_del(&str->link); -#endif - 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; - - 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_FreeContext(e->realm); - js_free_rt(rt, e); - } - init_list_head(&rt->job_list); - - /* don't remove the weak objects to avoid create new jobs with - FinalizationRegistry */ - JS_RunGCInternal(rt, FALSE); - -#ifdef DUMP_LEAKS - /* leaking objects */ - { - 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)); - assert(list_empty(&rt->weakref_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 DUMP_LEAKS - /* only the atoms defined in JS_InitAtoms() should be left */ - { - 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_PRIVATE) { - 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 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 DUMP_LEAKS - if (!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"); - } - { - JSMallocState *s = &rt->malloc_state; - if (s->malloc_count > 1) { - if (rt->rt_info) - printf("%s:1: ", rt->rt_info); - printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", - (uint64_t)(s->malloc_size - sizeof(JSRuntime)), - (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); - } - } -#endif - - { - JSMallocState ms = rt->malloc_state; - rt->mf.js_free(&ms, 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->regexp_ctor = JS_NULL; - ctx->promise_ctor = JS_NULL; - init_list_head(&ctx->loaded_modules); - - if (JS_AddIntrinsicBasicObjects(ctx)) { - JS_FreeContext(ctx); - return NULL; - } - return ctx; -} - -JSContext *JS_NewContext(JSRuntime *rt) -{ - JSContext *ctx; - - ctx = JS_NewContextRaw(rt); - if (!ctx) - return NULL; - - if (JS_AddIntrinsicBaseObjects(ctx) || - JS_AddIntrinsicDate(ctx) || - JS_AddIntrinsicEval(ctx) || - JS_AddIntrinsicStringNormalize(ctx) || - JS_AddIntrinsicRegExp(ctx) || - JS_AddIntrinsicJSON(ctx) || - JS_AddIntrinsicProxy(ctx) || - JS_AddIntrinsicMapSet(ctx) || - JS_AddIntrinsicTypedArrays(ctx) || - JS_AddIntrinsicPromise(ctx) || - JS_AddIntrinsicWeakRef(ctx)) { - JS_FreeContext(ctx); - return NULL; - } - 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) -{ - JSRuntime *rt = ctx->rt; - assert(class_id < rt->class_count); - set_value(ctx, &ctx->class_proto[class_id], obj); -} - -JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) -{ - JSRuntime *rt = ctx->rt; - assert(class_id < rt->class_count); - return JS_DupValue(ctx, ctx->class_proto[class_id]); -} - -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)) { - /* warning: the module may be referenced elsewhere. It - could be simpler to use an array instead of a list for - 'ctx->loaded_modules' */ - list_del(&m->link); - m->link.prev = NULL; - m->link.next = NULL; - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, 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; - - list_for_each(el, &ctx->loaded_modules) { - JSModuleDef *m = list_entry(el, JSModuleDef, link); - JS_MarkValue(rt, JS_MKPTR(JS_TAG_MODULE, 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); - } - 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->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 DUMP_ATOMS - JS_DumpAtoms(ctx->rt); -#endif -#ifdef DUMP_SHAPES - JS_DumpShapes(ctx->rt); -#endif -#ifdef 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 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]); - } - 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->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 (rt->stack_size == 0) { - rt->stack_limit = 0; /* no limit */ - } else { - rt->stack_limit = rt->stack_top - rt->stack_size; - } -} - -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->js_mode & JS_MODE_STRICT)); -} - -/* 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) -{ -#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 - return (int32_t)v <= 0; -#else - return (int32_t)v < JS_ATOM_END; -#endif -} - -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, const 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(const JSString *str, uint32_t h) -{ - if (str->is_wide_char) - h = hash_string16(str->u.str16, str->len, h); - else - h = hash_string8(str->u.str8, 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_DumpChar(FILE *fo, int c, int sep) -{ - if (c == sep || c == '\\') { - fputc('\\', fo); - fputc(c, fo); - } else if (c >= ' ' && c <= 126) { - fputc(c, fo); - } else if (c == '\n') { - fputc('\\', fo); - fputc('n', fo); - } else { - fprintf(fo, "\\u%04x", c); - } -} - -static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) -{ - int i, sep; - - if (p == NULL) { - printf(""); - return; - } - printf("%d", p->header.ref_count); - sep = (p->header.ref_count == 1) ? '\"' : '\''; - putchar(sep); - for(i = 0; i < p->len; i++) { - JS_DumpChar(stdout, string_get(p, i), sep); - } - 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, 512)) /* there are at least 504 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; -} - -static 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: - if (p->hash == JS_ATOM_HASH_PRIVATE) - return JS_ATOM_KIND_PRIVATE; - else - return JS_ATOM_KIND_SYMBOL; - default: - abort(); - } -} - -static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) -{ - return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; -} - -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 0 - printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); -#endif - 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, 0, str, 0, 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 = 0; - } 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 504). - */ - new_size = max_int(711, 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 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; -#ifdef DUMP_LEAKS - list_add_tail(&p->link, &rt->string_list); -#endif - memcpy(p->u.str8, str->u.str8, (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; -#ifdef 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; - - 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; -} - -/* only works with zero terminated 8 bit strings */ -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(p->u.str8, str, len); - p->u.str8[len] = '\0'; - return __JS_NewAtom(rt, p, atom_type); -} - -/* Warning: str must be ASCII only */ -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(p->u.str8, 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) -{ -#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ - if (unlikely(i == JS_ATOM_NULL)) { - p->header.ref_count = INT32_MAX / 2; - return; - } -#endif - 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; - /* free the string structure */ -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - if (p->atom_type == JS_ATOM_TYPE_SYMBOL && - p->hash != JS_ATOM_HASH_PRIVATE && p->hash != 0) { - /* live weak references are still present on this object: keep - it */ - } else { - 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); -} - -/* XXX: optimize */ -static size_t count_ascii(const uint8_t *buf, size_t len) -{ - const uint8_t *p, *p_end; - p = buf; - p_end = buf + len; - while (p < p_end && *p < 128) - p++; - return p - buf; -} - -/* str is UTF-8 encoded */ -JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) -{ - JSValue val; - - if (len == 0 || - (!is_digit(*str) && - count_ascii((const uint8_t *)str, len) == len)) { - 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)); -} - -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[11]; - JSValue val; - size_t len; - len = u32toa(buf, n); - 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]; - JSValue val; - size_t len; - len = i64toa(buf, n); - 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_NewSymbol(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_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - return JS_NewSymbol(ctx, p, atom_type); -} - -#define ATOM_GET_STR_BUF_SIZE 64 - -/* Should only be used for debug. */ -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 { - JSAtomStruct *p; - assert(atom < rt->atom_size); - if (atom == JS_ATOM_NULL) { - snprintf(buf, buf_size, ""); - } else { - int i, c; - char *q; - JSString *str; - - q = buf; - p = rt->atom_array[atom]; - assert(!atom_is_free(p)); - str = p; - if (str) { - if (!str->is_wide_char) { - /* special case ASCII strings */ - c = 0; - for(i = 0; i < str->len; i++) { - c |= str->u.str8[i]; - } - if (c < 0x80) - return (const char *)str->u.str8; - } - for(i = 0; i < str->len; i++) { - c = string_get(str, i); - if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) - break; - if (c < 128) { - *q++ = c; - } else { - q += unicode_to_utf8((uint8_t *)q, c); - } - } - } - *q = '\0'; - } - } - 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_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - } else { - return JS_DupValue(ctx, 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, ret; - JSValue num, str; - - if (__JS_AtomIsTaggedInt(atom)) - return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); - assert(atom < rt->atom_size); - p1 = rt->atom_array[atom]; - if (p1->atom_type != JS_ATOM_TYPE_STRING) - return JS_UNDEFINED; - switch(atom) { - case JS_ATOM_minus_zero: - return __JS_NewFloat64(ctx, -0.0); - case JS_ATOM_Infinity: - return __JS_NewFloat64(ctx, INFINITY); - case JS_ATOM_minus_Infinity: - return __JS_NewFloat64(ctx, -INFINITY); - case JS_ATOM_NaN: - return __JS_NewFloat64(ctx, NAN); - default: - break; - } - p = p1; - if (p->len == 0) - return JS_UNDEFINED; - c = string_get(p, 0); - if (!is_num(c) && c != '-') - 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(ctx, 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_PRIVATE) || - p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && - !(p->len == 0 && p->is_wide_char != 0)); -} - -/* 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; -} - -/* return a string atom containing name concatenated with str1 */ -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); -} - -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 */ - -#ifdef CONFIG_ATOMICS -static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - -/* a new class ID is allocated if *pclass_id != 0 */ -JSClassID JS_NewClassID(JSClassID *pclass_id) -{ - JSClassID class_id; -#ifdef CONFIG_ATOMICS - pthread_mutex_lock(&js_class_id_mutex); -#endif - class_id = *pclass_id; - if (class_id == 0) { - class_id = js_class_id_alloc++; - *pclass_id = class_id; - } -#ifdef CONFIG_ATOMICS - pthread_mutex_unlock(&js_class_id_mutex); -#endif - return class_id; -} - -JSClassID JS_GetClassID(JSValue 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); -} - -/* 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; - - 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 JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) -{ - JSString *str; - - if (len <= 0) { - return JS_AtomToString(ctx, JS_ATOM_empty_string); - } - str = js_alloc_string(ctx, len, 0); - if (!str) - return JS_EXCEPTION; - memcpy(str->u.str8, buf, len); - str->u.str8[len] = '\0'; - return JS_MKPTR(JS_TAG_STRING, str); -} - -static JSValue js_new_string8(JSContext *ctx, const char *buf) -{ - return js_new_string8_len(ctx, buf, strlen(buf)); -} - -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(str->u.str16, buf, len * 2); - return JS_MKPTR(JS_TAG_STRING, str); -} - -static JSValue js_new_string_char(JSContext *ctx, uint16_t c) -{ - if (c < 0x100) { - uint8_t ch8 = c; - return js_new_string8_len(ctx, (const char *)&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) -{ - int len = end - start; - if (start == 0 && end == p->len) { - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - } - if (p->is_wide_char && len > 0) { - JSString *str; - int i; - uint16_t c = 0; - for (i = start; i < end; i++) { - c |= p->u.str16[i]; - } - if (c > 0xFF) - return js_new_string16_len(ctx, p->u.str16 + start, len); - - str = js_alloc_string(ctx, len, 0); - if (!str) - return JS_EXCEPTION; - for (i = 0; i < len; i++) { - str->u.str8[i] = p->u.str16[start + i]; - } - str->u.str8[len] = '\0'; - return JS_MKPTR(JS_TAG_STRING, str); - } else { - return js_new_string8_len(ctx, (const char *)(p->u.str8 + 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 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;) { - str->u.str16[i] = str->u.str8[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_ThrowInternalError(s->ctx, "string too long"); - 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) { - s->str->u.str16[s->len++] = c; - } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; - } else { - if (string_buffer_widen(s, s->size)) - return -1; - s->str->u.str16[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) { - s->str->u.str16[s->len++] = c; - } else { - s->str->u.str8[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) { - s->str->u.str16[s->len++] = c; - return 0; - } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; - return 0; - } - } - return string_buffer_putc16_slow(s, c); -} - -static int string_buffer_putc_slow(StringBuffer *s, uint32_t c) -{ - if (unlikely(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) { - s->str->u.str16[s->len++] = c; - return 0; - } else if (likely((s->len + 1) < s->size)) { - s->str->u.str16[s->len++] = get_hi_surrogate(c); - s->str->u.str16[s->len++] = get_lo_surrogate(c); - return 0; - } - } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; - return 0; - } - } - return string_buffer_putc_slow(s, c); -} - -static int string_getc(const JSString *p, int *pidx) -{ - int idx, c, c1; - idx = *pidx; - if (p->is_wide_char) { - c = p->u.str16[idx++]; - if (is_hi_surrogate(c) && idx < p->len) { - c1 = p->u.str16[idx]; - if (is_lo_surrogate(c1)) { - c = from_surrogate(c, c1); - idx++; - } - } - } else { - c = p->u.str8[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++) { - s->str->u.str16[s->len + i] = p[i]; - } - s->len += len; - } else { - memcpy(&s->str->u.str8[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(&s->str->u.str16[s->len], p, len << 1); - s->len += len; - } else { - for (i = 0; i < len; i++) { - s->str->u.str8[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, const JSString *p, - uint32_t from, uint32_t to) -{ - if (to <= from) - return 0; - if (p->is_wide_char) - return string_buffer_write16(s, p->u.str16 + from, to - from); - else - return string_buffer_write8(s, p->u.str8 + from, to - from); -} - -static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) -{ - JSString *p; - JSValue v1; - int res; - - if (s->error_status) { - /* prevent exception overload */ - return -1; - } - if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { - if (JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE) { - JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v); - /* recursion is acceptable because the rope depth is bounded */ - if (string_buffer_concat_value(s, r->left)) - return -1; - return string_buffer_concat_value(s, r->right); - } else { - 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; - - if (s->error_status) { - /* prevent exception overload */ - JS_FreeValue(s->ctx, v); - return -1; - } - if (unlikely(JS_VALUE_GET_TAG(v) != 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_AtomToString(s->ctx, JS_ATOM_empty_string); - } - 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) - str->u.str8[s->len] = 0; -#ifdef 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) -{ - const uint8_t *p, *p_end, *p_start, *p_next; - uint32_t c; - StringBuffer b_s, *b = &b_s; - size_t len1; - - p_start = (const uint8_t *)buf; - p_end = p_start + buf_len; - len1 = count_ascii(p_start, buf_len); - p = p_start + len1; - if (len1 > JS_STRING_LEN_MAX) - return JS_ThrowInternalError(ctx, "string too long"); - if (p == p_end) { - /* ASCII string */ - return js_new_string8_len(ctx, buf, buf_len); - } else { - if (string_buffer_init(ctx, b, buf_len)) - goto fail; - string_buffer_write8(b, p_start, len1); - while (p < p_end) { - if (*p < 128) { - string_buffer_putc8(b, *p++); - } else { - /* parse utf-8 sequence, return 0xFFFFFFFF for error */ - c = unicode_from_utf8(p, p_end - p, &p_next); - if (c < 0x10000) { - p = p_next; - } else if (c <= 0x10FFFF) { - p = p_next; - /* surrogate pair */ - string_buffer_putc16(b, get_hi_surrogate(c)); - c = get_lo_surrogate(c); - } else { - /* invalid char */ - c = 0xfffd; - /* skip the invalid chars */ - /* XXX: seems incorrect. Why not just use c = *p++; ? */ - while (p < p_end && (*p >= 0x80 && *p < 0xc0)) - p++; - if (p < p_end) { - p++; - while (p < p_end && (*p >= 0x80 && *p < 0xc0)) - p++; - } - } - string_buffer_putc16(b, c); - } - } - } - return string_buffer_end(b); - - fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -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; -} - -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; -} - -/* 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; - - if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { - val = JS_ToString(ctx, val1); - if (JS_IsException(val)) - goto fail; - } else { - val = JS_DupValue(ctx, val1); - } - - str = JS_VALUE_GET_STRING(val); - len = str->len; - if (!str->is_wide_char) { - const uint8_t *src = str->u.str8; - 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) { - if (plen) - *plen = len; - return (const char *)src; - } - str_new = js_alloc_string(ctx, len + count, 0); - if (!str_new) - goto fail; - q = str_new->u.str8; - 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 = str->u.str16; - /* 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 = str_new->u.str8; - 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 += unicode_to_utf8(q, c); - } - } - } - - *q = '\0'; - str_new->len = q - str_new->u.str8; - JS_FreeValue(ctx, val); - if (plen) - *plen = str_new->len; - return (const char *)str_new->u.str8; - fail: - if (plen) - *plen = 0; - return NULL; -} - -void JS_FreeCString(JSContext *ctx, const char *ptr) -{ - JSString *p; - if (!ptr) - return; - /* purposely removing constness */ - p = container_of(ptr, JSString, u); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); -} - -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(const JSString *p1, int pos1, const JSString *p2, - int pos2, int len) -{ - int res; - - if (likely(!p1->is_wide_char)) { - if (likely(!p2->is_wide_char)) - res = memcmp(p1->u.str8 + pos1, p2->u.str8 + pos2, len); - else - res = -memcmp16_8(p2->u.str16 + pos2, p1->u.str8 + pos1, len); - } else { - if (!p2->is_wide_char) - res = memcmp16_8(p1->u.str16 + pos1, p2->u.str8 + pos2, len); - else - res = memcmp16(p1->u.str16 + pos1, p2->u.str16 + pos2, len); - } - return res; -} - -static BOOL js_string_eq(JSContext *ctx, - const JSString *p1, const JSString *p2) -{ - if (p1->len != p2->len) - return FALSE; - if (p1 == p2) - return TRUE; - return js_string_memcmp(p1, 0, p2, 0, p1->len) == 0; -} - -/* return < 0, 0 or > 0 */ -static int js_string_compare(JSContext *ctx, - const JSString *p1, const JSString *p2) -{ - int res, len; - len = min_int(p1->len, p2->len); - res = js_string_memcmp(p1, 0, p2, 0, len); - if (res == 0) { - if (p1->len == p2->len) - res = 0; - else if (p1->len < p2->len) - res = -1; - else - res = 1; - } - return res; -} - -static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) -{ - if (p->is_wide_char) { - memcpy(dst, p->u.str16 + offset, len * 2); - } else { - const uint8_t *src1 = p->u.str8 + offset; - int i; - - for(i = 0; i < len; i++) - dst[i] = src1[i]; - } -} - -static JSValue JS_ConcatString1(JSContext *ctx, - const JSString *p1, const JSString *p2) -{ - JSString *p; - uint32_t len; - int is_wide_char; - - len = p1->len + p2->len; - if (len > JS_STRING_LEN_MAX) - return JS_ThrowInternalError(ctx, "string too long"); - 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(p->u.str8, p1->u.str8, p1->len); - memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); - p->u.str8[len] = '\0'; - } else { - copy_str16(p->u.str16, p1, 0, p1->len); - copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); - } - return JS_MKPTR(JS_TAG_STRING, p); -} - -static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) { - if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { - JSString *p2 = JS_VALUE_GET_STRING(op2); - size_t size1; - - if (p2->len == 0) - return TRUE; - if (p1->header.ref_count != 1) - return FALSE; - size1 = js_malloc_usable_size(ctx, p1); - if (p1->is_wide_char) { - if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) { - if (p2->is_wide_char) { - memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); - p1->len += p2->len; - return TRUE; - } else { - size_t i; - for (i = 0; i < p2->len; i++) { - p1->u.str16[p1->len++] = p2->u.str8[i]; - } - return TRUE; - } - } - } else if (!p2->is_wide_char) { - if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) { - memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); - p1->len += p2->len; - p1->u.str8[p1->len] = '\0'; - return TRUE; - } - } - } - return FALSE; -} - -static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2) -{ - JSValue ret; - JSString *p1, *p2; - p1 = JS_VALUE_GET_STRING(op1); - if (JS_ConcatStringInPlace(ctx, p1, op2)) { - JS_FreeValue(ctx, op2); - return op1; - } - p2 = JS_VALUE_GET_STRING(op2); - ret = JS_ConcatString1(ctx, p1, p2); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return ret; -} - -/* Return the character at position 'idx'. 'val' must be a string or rope */ -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; - } -} - -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 js_string_rope_compare(JSContext *ctx, 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(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; -} - -/* '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, JSValue 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_DupValue(ctx, r->left); - JS_FreeValue(ctx, rope); - 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_DupValue(ctx, ret); - r->right = JS_AtomToString(ctx, JS_ATOM_empty_string); - } - JS_FreeValue(ctx, rope); - return ret; - fail: - JS_FreeValue(ctx, rope); - return JS_EXCEPTION; -} - -static JSValue js_rebalancee_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_rebalancee_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 - -/* Fibonacii 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_rebalancee_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_DupValue(ctx, val)); - if (JS_IsException(a)) - return -1; - } else { - a = JS_DupValue(ctx, 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); - js_rebalancee_string_rope_rec(ctx, buckets, r->left); - js_rebalancee_string_rope_rec(ctx, buckets, r->right); - } - 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_rebalancee_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_rebalancee_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; -} - -/* 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(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING && - JS_VALUE_GET_TAG(op1) != JS_TAG_STRING_ROPE)) { - op1 = JS_ToStringFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - } - if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING && - JS_VALUE_GET_TAG(op2) != JS_TAG_STRING_ROPE)) { - 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_DupValue(ctx, r1->right), op2); - if (JS_IsException(val)) { - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - ret = js_new_string_rope(ctx, JS_DupValue(ctx, 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_DupValue(ctx, r2->left)); - if (JS_IsException(val)) { - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - ret = js_new_string_rope(ctx, val, JS_DupValue(ctx, 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 = 4; /* 16 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'. It is not hashed */ -static inline JSShape *js_new_shape_nohash(JSContext *ctx, JSObject *proto, - int hash_size, int prop_size) -{ - JSRuntime *rt = ctx->rt; - void *sh_alloc; - JSShape *sh; - - 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_DupValue(ctx, 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; - sh->is_hashed = FALSE; - return sh; -} - -/* 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; - 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 = js_new_shape_nohash(ctx, proto, hash_size, prop_size); - if (!sh) - return NULL; - - /* 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_DupValue(ctx, 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; - JSShape *old_sh; - - 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; - /* resize the property shapes. Using js_realloc() is not possible in - case the GC runs during the allocation */ - 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 shape 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); - - if (new_hash_size != (sh->prop_hash_mask + 1)) { - /* resize the hash table and the properties */ - 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; - } - } - } else { - /* just copy the previous hash table */ - memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size, - sizeof(prop_hash_end(sh)[0]) * new_hash_size); - } - js_free(ctx, get_alloc_from_shape(old_sh)); - *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->is_prototype = 0; - p->extensible = TRUE; - p->free_mark = 0; - p->is_exotic = 0; - p->fast_array = 0; - p->is_constructor = 0; - p->has_immutable_prototype = 0; - p->tmp_mark = 0; - p->is_HTMLDDA = 0; - p->weakref_count = 0; - 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_NewInt32(ctx, 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; - break; - case JS_CLASS_GLOBAL_OBJECT: - p->u.global_object.uninitialized_vars = JS_UNDEFINED; - break; - 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); -} - -/* WARNING: the shape is not hashed. It is used for objects where - factorizing the shape is not relevant (prototypes, constructors) */ -static JSValue JS_NewObjectProtoClassAlloc(JSContext *ctx, JSValueConst proto_val, - JSClassID class_id, int n_alloc_props) -{ - JSShape *sh; - JSObject *proto; - int hash_size, hash_bits; - - if (n_alloc_props <= JS_PROP_INITIAL_SIZE) { - n_alloc_props = JS_PROP_INITIAL_SIZE; - hash_size = JS_PROP_INITIAL_HASH_SIZE; - } else { - hash_bits = 32 - clz32(n_alloc_props - 1); /* ceil(log2(radix)) */ - hash_size = 1 << hash_bits; - } - proto = get_proto_obj(proto_val); - sh = js_new_shape_nohash(ctx, proto, hash_size, n_alloc_props); - if (!sh) - return JS_EXCEPTION; - return JS_NewObjectFromShape(ctx, sh, class_id, NULL); -} - -#if 0 -static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) -{ - 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: - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_UNDEFINED; -} -#endif - -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, int 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_NewArray(JSContext *ctx) -{ - return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), - JS_CLASS_ARRAY, NULL); -} - -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, JSValueConst 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_NewInt32(ctx, 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, JSValueConst func_obj, - JSValueConst 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_DupValue(ctx, 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, JSValueConst func_obj, - JSAtom name, int flags, JSValueConst 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' */ -static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic, - JSValueConst proto_val, int n_fields) -{ - JSValue func_obj; - JSObject *p; - JSAtom name_atom; - - if (n_fields > 0) { - func_obj = JS_NewObjectProtoClassAlloc(ctx, proto_val, JS_CLASS_C_FUNCTION, n_fields); - } else { - 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); - 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, 0); -} - -typedef struct JSCFunctionDataRecord { - JSCFunctionData *func; - uint8_t length; - uint8_t data_len; - uint16_t magic; - JSValue data[0]; -} JSCFunctionDataRecord; - -static void js_c_function_data_finalizer(JSRuntime *rt, JSValue 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_c_function_data_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv, int flags) -{ - JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); - JSValueConst *arg_buf; - int i; - - /* XXX: could add the function on the stack for debug */ - if (unlikely(argc < s->length)) { - arg_buf = alloca(sizeof(arg_buf[0]) * s->length); - for(i = 0; i < argc; i++) - arg_buf[i] = argv[i]; - for(i = argc; i < s->length; i++) - arg_buf[i] = JS_UNDEFINED; - } else { - arg_buf = argv; - } - - return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); -} - -JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, - int length, int magic, int data_len, - JSValueConst *data) -{ - JSCFunctionDataRecord *s; - 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_DupValue(ctx, data[i]); - JS_SetOpaque(func_obj, s); - js_function_set_properties(ctx, func_obj, - JS_ATOM_empty_string, length); - return func_obj; -} - -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); -} - -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 force_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 force_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; -} - -/* indicate that the object may be part of a function prototype cycle */ -static void set_cycle_flag(JSContext *ctx, JSValueConst obj) -{ -} - -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); - } 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; - if (sf->js_mode & JS_MODE_ASYNC) { - JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); - async_func_free(rt, async_func); - } - } - remove_gc_object(&var_ref->header); - js_free_rt(rt, var_ref); - } - } -} - -static void js_array_finalizer(JSRuntime *rt, JSValue 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, JSValue 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, JSValue 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, JSValue 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) { - 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, JSValue 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, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSForInIterator *it = p->u.for_in_iterator; - int i; - - JS_FreeValueRT(rt, it->obj); - if (!it->is_array) { - for(i = 0; i < it->atom_count; i++) { - JS_FreeAtomRT(rt, it->tab_atom[i].atom); - } - js_free_rt(rt, it->tab_atom); - } - 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; - - 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) { - if (p->header.ref_count == 0 && p->weakref_count == 0) { - js_free_rt(rt, p); - } else { - /* keep the object structure because there are may be - references to it */ - list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); - } - } else { - /* keep the object structure in case there are weak references to it */ - if (p->weakref_count == 0) { - js_free_rt(rt, p); - } else { - p->header.mark = 0; /* reset the mark so that the weakref can be freed */ - } - } -} - -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; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - __async_func_free(rt, (JSAsyncFunctionState *)gp); - break; - case JS_GC_OBJ_TYPE_MODULE: - js_free_module_def(rt, (JSModuleDef *)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. */ -void __JS_FreeValueRT(JSRuntime *rt, JSValue v) -{ - uint32_t tag = JS_VALUE_GET_TAG(v); - -#ifdef DUMP_FREE - { - printf("Freeing "); - if (tag == JS_TAG_OBJECT) { - JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); - } else { - JS_DumpValueShort(rt, v); - printf("\n"); - } - } -#endif - - switch(tag) { - case JS_TAG_STRING: - { - JSString *p = JS_VALUE_GET_STRING(v); - if (p->atom_type) { - JS_FreeAtomStruct(rt, p); - } else { -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - } - } - break; - case JS_TAG_STRING_ROPE: - /* Note: recursion is acceptable because the rope depth is bounded */ - { - 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: - case JS_TAG_MODULE: - { - 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); - p->mark = 1; /* indicate that the object is about to be freed */ - if (rt->gc_phase == JS_GC_PHASE_NONE) { - free_zero_refcount(rt); - } - } - } - 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: - abort(); - } -} - -void __JS_FreeValue(JSContext *ctx, JSValue v) -{ - __JS_FreeValueRT(ctx->rt, v); -} - -/* garbage collection */ - -static void gc_remove_weak_objects(JSRuntime *rt) -{ - struct list_head *el; - - /* add the freed objects to rt->gc_zero_ref_count_list so that - rt->weakref_list is not modified while we traverse it */ - rt->gc_phase = JS_GC_PHASE_DECREF; - - list_for_each(el, &rt->weakref_list) { - JSWeakRefHeader *wh = list_entry(el, JSWeakRefHeader, link); - switch(wh->weakref_type) { - case JS_WEAKREF_TYPE_MAP: - map_delete_weakrefs(rt, wh); - break; - case JS_WEAKREF_TYPE_WEAKREF: - weakref_delete_weakref(rt, wh); - break; - case JS_WEAKREF_TYPE_FINREC: - finrec_delete_weakref(rt, wh); - break; - default: - abort(); - } - } - - rt->gc_phase = JS_GC_PHASE_NONE; - /* free the freed objects here. */ - free_zero_refcount(rt); -} - -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: - case JS_TAG_MODULE: - mark_func(rt, JS_VALUE_GET_PTR(val)); - break; - default: - break; - } - } -} - -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) { - /* 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 (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; - if (var_ref->is_detached) { - JS_MarkValue(rt, *var_ref->pvalue, mark_func); - } else { - JSStackFrame *sf = var_ref->stack_frame; - if (sf->js_mode & JS_MODE_ASYNC) { - JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); - mark_func(rt, &async_func->header); - } - } - } - break; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - { - JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp; - JSStackFrame *sf = &s->frame; - JSValue *sp; - - if (!s->is_completed) { - JS_MarkValue(rt, sf->cur_func, mark_func); - JS_MarkValue(rt, s->this_val, mark_func); - /* sf->cur_sp = NULL if the function is running */ - 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); - } - } - 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; - case JS_GC_OBJ_TYPE_MODULE: - { - JSModuleDef *m = (JSModuleDef *)gp; - js_mark_module_def(rt, m, 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 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 - or async functions. 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: - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - case JS_GC_OBJ_TYPE_MODULE: -#ifdef 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 || - p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION || - p->gc_obj_type == JS_GC_OBJ_TYPE_MODULE); - if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT && - ((JSObject *)p)->weakref_count != 0) { - /* keep the object because there are weak references to it */ - p->mark = 0; - } else { - js_free_rt(rt, p); - } - } - - init_list_head(&rt->gc_zero_ref_count_list); -} - -static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects) -{ - if (remove_weak_objects) { - /* free the weakly referenced object or symbol structures, delete - the associated Map/Set entries and queue the finalization - registry callbacks. */ - gc_remove_weak_objects(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); -} - -void JS_RunGC(JSRuntime *rt) -{ - JS_RunGCInternal(rt, TRUE); -} - -/* 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(JSValueConst 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 = offsetof(JSFunctionBytecode, debug); - 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++) { - JSValueConst 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->read_only_bytecode && b->byte_code_buf) { - hp->js_func_code_size += b->byte_code_len; - } - if (b->has_debug) { - js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); - if (b->debug.source) { - memory_used_count++; - js_func_size += b->debug.source_len + 1; - } - if (b->debug.pc2line_len) { - memory_used_count++; - hp->js_func_pc2line_count += 1; - hp->js_func_pc2line_size += b->debug.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(JSValueConst 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(JSValue) * 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_MAPPED_ARGUMENTS: /* u.array | length */ - 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.var_refs); - s->fast_array_elements += p->u.array.count; - for (i = 0; i < p->u.array.count; i++) { - compute_value_size(*p->u.array.u.var_refs[i]->pvalue, 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_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 memory usage -- version, %d-bit, malloc limit: %"PRId64"\n\n", - (int)sizeof(void *) * 8, s->malloc_limit); -#if 1 - 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"); - } -#endif - 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_DupValue(ctx, 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; - rt->current_exception_is_uncatchable = FALSE; - 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; -} - -JS_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; -} - -/* use pc_value = -1 to get the position of the function definition */ -static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, - uint32_t pc_value, int *pcol_num) -{ - const uint8_t *p_end, *p; - int new_line_num, line_num, pc, v, ret, new_col_num, col_num; - uint32_t val; - unsigned int op; - - if (!b->has_debug || !b->debug.pc2line_buf) - goto fail; /* function was stripped */ - - p = b->debug.pc2line_buf; - p_end = p + b->debug.pc2line_len; - - /* get the function line and column numbers */ - ret = get_leb128(&val, p, p_end); - if (ret < 0) - goto fail; - p += ret; - line_num = val + 1; - - ret = get_leb128(&val, p, p_end); - if (ret < 0) - goto fail; - p += ret; - col_num = val + 1; - - if (pc_value != -1) { - pc = 0; - while (p < p_end) { - op = *p++; - if (op == 0) { - 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) - goto done; - line_num = new_line_num; - col_num = new_col_num; - } - } - done: - *pcol_num = col_num; - return line_num; - fail: - *pcol_num = 0; - return 0; -} - -/* return a string property without executing arbitrary JS code (used - when dumping the stack trace or in debug print). */ -static const char *get_prop_string(JSContext *ctx, JSValueConst obj, JSAtom prop) -{ - JSObject *p; - JSProperty *pr; - JSShapeProperty *prs; - JSValueConst val; - - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return NULL; - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - /* we look at one level in the prototype to handle the 'name' - field of the Error objects */ - p = p->shape->proto; - if (!p) - return NULL; - prs = find_own_property(&pr, p, prop); - 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); -} - -#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) - -/* 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_obj, - const char *filename, int line_num, int col_num, - int backtrace_flags) -{ - JSStackFrame *sf; - JSValue str; - DynBuf dbuf; - const char *func_name_str; - const char *str1; - JSObject *p; - - if (!JS_IsObject(error_obj)) - return; /* protection in the out of memory case */ - - js_dbuf_init(ctx, &dbuf); - if (filename) { - dbuf_printf(&dbuf, " at %s", filename); - if (line_num != -1) - dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); - dbuf_putc(&dbuf, '\n'); - str = JS_NewString(ctx, filename); - if (JS_IsException(str)) - return; - /* Note: SpiderMonkey does that, could update once there is a standard */ - if (JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_columnNumber, JS_NewInt32(ctx, col_num), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) { - return; - } - } - for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { - if (sf->js_mode & JS_MODE_BACKTRACE_BARRIER) - break; - if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { - backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; - continue; - } - func_name_str = get_prop_string(ctx, sf->cur_func, JS_ATOM_name); - 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); - - p = JS_VALUE_GET_OBJ(sf->cur_func); - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b; - const char *atom_str; - int line_num1, col_num1; - - b = p->u.func.function_bytecode; - if (b->has_debug) { - line_num1 = find_line_num(ctx, b, - sf->cur_pc - b->byte_code_buf - 1, &col_num1); - atom_str = JS_AtomToCString(ctx, b->debug.filename); - dbuf_printf(&dbuf, " (%s", - atom_str ? atom_str : ""); - JS_FreeCString(ctx, atom_str); - if (line_num1 != 0) - dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); - dbuf_putc(&dbuf, ')'); - } - } else { - dbuf_printf(&dbuf, " (native)"); - } - dbuf_putc(&dbuf, '\n'); - } - dbuf_putc(&dbuf, '\0'); - if (dbuf_error(&dbuf)) - str = JS_NULL; - else - str = JS_NewString(ctx, (char *)dbuf.buf); - dbuf_free(&dbuf); - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); -} - -/* Note: it is important that no exception is returned by this function */ -static BOOL is_backtrace_needed(JSContext *ctx, 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) - return FALSE; - if (find_own_property1(p, JS_ATOM_stack)) - return FALSE; - return TRUE; -} - -JSValue JS_NewError(JSContext *ctx) -{ - JSValue error = JS_NewObjectClass(ctx, JS_CLASS_ERROR); - build_backtrace(ctx, error, NULL, 0, 0, 0); - return error; - -} - -static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap, BOOL add_backtrace) -{ - char buf[256]; - JSValue obj, ret; - - vsnprintf(buf, sizeof(buf), fmt, ap); - obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], - JS_CLASS_ERROR); - if (unlikely(JS_IsException(obj))) { - /* out of memory: throw JS_NULL to avoid recursing */ - obj = JS_NULL; - } else { - JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, - JS_NewString(ctx, buf), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (add_backtrace) { - build_backtrace(ctx, obj, NULL, 0, 0, 0); - } - } - ret = JS_Throw(ctx, obj); - return ret; -} - -static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, - 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, fmt, ap, add_backtrace); -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, 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; - } -} - -/* never use it directly */ -static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowTypeError(ctx, fmt, - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); -} - -/* never use it directly */ -static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowSyntaxError(ctx, fmt, - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); -} - -/* %s is replaced by 'atom'. The macro is used so that gcc can check - the format string. */ -#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") -#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") - -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 __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); - va_end(ap); - return val; -} - -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_ThrowInternalError(ctx, "stack overflow"); -} - -static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "not an object"); -} - -static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx, - JSValueConst func_obj) -{ - const char *name; - if (!JS_IsFunction(ctx, func_obj)) - goto fail; - name = get_prop_string(ctx, func_obj, JS_ATOM_name); - if (!name) { - fail: - return JS_ThrowTypeError(ctx, "not a constructor"); - } - JS_ThrowTypeError(ctx, "%s is not a constructor", name); - JS_FreeCString(ctx, name); - return JS_EXCEPTION; -} - -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_SetUncatchableException(ctx, TRUE); -} - -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; - } -} - -static void JS_SetImmutablePrototype(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(obj); - p->has_immutable_prototype = TRUE; -} - -/* Return -1 (exception) or TRUE/FALSE. 'throw_flag' = FALSE indicates - that it is called from Reflect.setPrototypeOf(). */ -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->is_exotic)) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - int ret; - if (em && em->set_prototype) { - ret = em->set_prototype(ctx, obj, proto_val); - if (ret == 0 && throw_flag) { - JS_ThrowTypeError(ctx, "proxy: bad prototype"); - return -1; - } else { - return ret; - } - } - } - - sh = p->shape; - if (sh->proto == proto) - return TRUE; - if (unlikely(p->has_immutable_prototype)) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "prototype is immutable"); - return -1; - } else { - return FALSE; - } - } - if (unlikely(!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_DupValue(ctx, 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) -{ - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_SHORT_BIG_INT: - case JS_TAG_BIG_INT: - val = ctx->class_proto[JS_CLASS_BIG_INT]; - break; - case JS_TAG_INT: - case JS_TAG_FLOAT64: - val = ctx->class_proto[JS_CLASS_NUMBER]; - break; - case JS_TAG_BOOL: - val = ctx->class_proto[JS_CLASS_BOOLEAN]; - break; - case JS_TAG_STRING: - case JS_TAG_STRING_ROPE: - val = ctx->class_proto[JS_CLASS_STRING]; - break; - case JS_TAG_SYMBOL: - val = ctx->class_proto[JS_CLASS_SYMBOL]; - break; - case JS_TAG_OBJECT: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - val = JS_NULL; - break; - } - return val; -} - -/* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic 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->is_exotic)) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->get_prototype) { - return em->get_prototype(ctx, obj); - } - } - p = p->shape->proto; - if (!p) - val = JS_NULL; - else - val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - } else { - val = JS_DupValue(ctx, 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; -} - -/* 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; - BOOL 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 exotic object in the prototype chain */ - if (unlikely(p->is_exotic && !p->fast_array)) { - JSValue obj1; - obj1 = JS_DupValue(ctx, 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); -} - -/* return the value associated to the autoinit property or an exception */ -typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); - -static JSAutoInitFunc *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 */ -}; - -/* 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; - JSAutoInitIDEnum id; - - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - - realm = js_autoinit_get_realm(pr); - id = js_autoinit_get_id(pr); - func = js_autoinit_func_table[id]; - /* '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; - if (id == JS_AUTOINIT_ID_MODULE_NS && - JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { - /* WARNING: a varref is returned as a string ! */ - prs->flags |= JS_PROP_VARREF; - pr->u.var_ref = JS_VALUE_GET_PTR(val); - pr->u.var_ref->header.ref_count++; - } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { - JSVarRef *var_ref; - /* in the global object we use references */ - var_ref = js_create_var_ref(ctx, FALSE); - if (!var_ref) - return -1; - prs->flags |= JS_PROP_VARREF; - pr->u.var_ref = var_ref; - var_ref->value = val; - var_ref->is_const = !(prs->flags & JS_PROP_WRITABLE); - } else { - pr->u.value = val; - } - return 0; -} - -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; - idx = __JS_AtomToUInt32(prop); - if (idx < p1->len) { - return js_new_string_char(ctx, string_get(p1, idx)); - } - } else if (prop == JS_ATOM_length) { - return JS_NewInt32(ctx, p1->len); - } - } - break; - case JS_TAG_STRING_ROPE: - { - JSStringRope *p1 = JS_VALUE_GET_STRING_ROPE(obj); - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx; - idx = __JS_AtomToUInt32(prop); - if (idx < p1->len) { - return js_new_string_char(ctx, string_rope_get(obj, idx)); - } - } else if (prop == JS_ATOM_length) { - return JS_NewInt32(ctx, p1->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_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, 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 (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - return JS_UNDEFINED; - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - 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_DupValue(ctx, 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_DupValue(ctx, 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; - } -} - -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, - 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, (JSValue)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, (JSValue)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_DupValue(ctx, 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, (JSValue)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_DupValue(ctx, brand); - } else { - brand = JS_DupValue(ctx, 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, JSValueConst obj, JSValueConst func) -{ - JSObject *p, *p1, *home_obj; - JSShapeProperty *prs; - JSProperty *pr; - JSValueConst 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, (JSValue)brand)); - return (prs != NULL); -} - -static uint32_t js_string_obj_get_length(JSContext *ctx, - JSValueConst obj) -{ - JSObject *p; - 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) { - JSString *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; -} - -void JS_FreePropertyEnum(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_FreePropertyEnum(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; - if (atom_count < str_keys_count) - goto add_overflow; - atom_count += sym_keys_count; - if (atom_count < sym_keys_count) - goto add_overflow; - atom_count += exotic_keys_count; - if (atom_count < exotic_keys_count || atom_count > INT32_MAX) { - add_overflow: - JS_ThrowOutOfMemory(ctx); - JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); - return -1; - } - /* XXX: need generic way to test for js_malloc(ctx, a * b) overflow */ - - /* avoid allocating 0 bytes */ - tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); - if (!tab_atom) { - JS_FreePropertyEnum(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_FreePropertyEnum(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_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - desc->setter = JS_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, 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; - 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); -} - -/* return -1 if exception (exotic 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->is_exotic)) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->is_extensible) { - return em->is_extensible(ctx, obj); - } - } - return p->extensible; -} - -/* return -1 if exception (exotic 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->is_exotic)) { - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - JSTypedArray *ta; - JSArrayBuffer *abuf; - /* resizable type arrays return FALSE */ - ta = p->u.typed_array; - abuf = ta->buffer->u.array_buffer; - if (ta->track_rab || - (array_buffer_is_resizable(abuf) && !abuf->shared)) - return FALSE; - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->prevent_extensions) { - return em->prevent_extensions(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_DupValue(ctx, 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_DupValue(ctx, 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 (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - 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, JSValue val) -{ - JSAtomStruct *p = JS_VALUE_GET_PTR(val); - return js_get_atom_index(ctx->rt, p); -} - -/* return JS_ATOM_NULL in case of exception */ -JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) -{ - 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_ToPropertyKey(ctx, val); - 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; -} - -static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop) -{ - JSAtom atom; - JSValue ret; - - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && - JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { - JSObject *p; - uint32_t idx; - /* 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: - case JS_CLASS_ARGUMENTS: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_DupValue(ctx, p->u.array.u.values[idx]); - case JS_CLASS_MAPPED_ARGUMENTS: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_DupValue(ctx, *p->u.array.u.var_refs[idx]->pvalue); - case JS_CLASS_INT8_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); - case JS_CLASS_INT16_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); - case JS_CLASS_UINT16_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); - case JS_CLASS_INT32_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); - case JS_CLASS_UINT32_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); - case JS_CLASS_BIG_INT64_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); - case JS_CLASS_BIG_UINT64_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); - case JS_CLASS_FLOAT16_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return __JS_NewFloat64(ctx, fromfp16(p->u.array.u.fp16_ptr[idx])); - case JS_CLASS_FLOAT32_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); - case JS_CLASS_FLOAT64_ARRAY: - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); - default: - goto slow_path; - } - } else { - slow_path: - /* ToObject() must be done before ToPropertyKey() */ - if (JS_IsNull(this_obj) || JS_IsUndefined(this_obj)) { - JS_FreeValue(ctx, prop); - return JS_ThrowTypeError(ctx, "cannot read property of %s", JS_IsNull(this_obj) ? "null" : "undefined"); - } - 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_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); -} - -/* Check if an object has a generalized numeric property. Return value: - -1 for exception, - TRUE if property exists, stored into *pval, - FALSE if proprty does not exist. - */ -static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) -{ - JSValue val = JS_UNDEFINED; - JSAtom prop; - int present; - - if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { - /* fast path */ - present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); - if (present > 0) { - val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - if (unlikely(JS_IsException(val))) - present = -1; - } - } else { - prop = JS_NewAtomInt64(ctx, idx); - present = -1; - 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; - } - JS_FreeAtom(ctx, prop); - } - } - *pval = val; - return present; -} - -static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) -{ - JSAtom prop; - JSValue val; - - if ((uint64_t)idx <= INT32_MAX) { - /* fast path for fast arrays */ - return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - } - 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; -} - -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; - - 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; - - /* track modification of Array.prototype */ - if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) { - ctx->std_array_prototype = FALSE; - } - return 0; -} - -static int remove_global_object_property(JSContext *ctx, JSObject *p, - JSShapeProperty *prs, JSProperty *pr) -{ - JSVarRef *var_ref; - JSObject *p1; - JSProperty *pr1; - - var_ref = pr->u.var_ref; - if (var_ref->header.ref_count == 1) - return 0; - p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars); - pr1 = add_property(ctx, p1, prs->atom, JS_PROP_C_W_E | JS_PROP_VARREF); - if (!pr1) - return -1; - pr1->u.var_ref = var_ref; - var_ref->header.ref_count++; - JS_FreeValue(ctx, var_ref->value); - var_ref->is_lexical = FALSE; - var_ref->is_const = FALSE; - var_ref->value = JS_UNINITIALIZED; - 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]; - if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) { - if ((pr->flags & JS_PROP_TMASK) == JS_PROP_VARREF) - if (remove_global_object_property(ctx, p, pr, pr1)) - return -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_DupValue(ctx, func); - ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&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_NewUint32(ctx, 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_NewUint32(ctx, 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 inline 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_NewInt32(ctx, 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; -} - -/* Allocate a new fast array. Its 'length' property is set to zero. It - maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit - integer. WARNING: the content of the array is not initialized. */ -static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len) -{ - JSValue arr; - JSObject *p; - - if (len > INT32_MAX) - return JS_ThrowRangeError(ctx, "invalid array length"); - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return arr; - if (len > 0) { - p = JS_VALUE_GET_OBJ(arr); - if (expand_fast_array(ctx, p, len) < 0) { - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; - } - p->u.array.count = len; - } - return arr; -} - -static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) -{ - JSValue obj; - JSObject *p; - int i; - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - if (len > 0) { - p = JS_VALUE_GET_OBJ(obj); - if (expand_fast_array(ctx, p, len) < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - p->u.array.count = len; - for(i = 0; i < len; i++) - p->u.array.u.values[i] = JS_DupValue(ctx, tab[i]); - /* update the 'length' field */ - set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len)); - } - return obj; -} - -static JSValue js_create_array_free(JSContext *ctx, int len, JSValue *tab) -{ - JSValue obj; - JSObject *p; - int i; - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - goto fail; - if (len > 0) { - p = JS_VALUE_GET_OBJ(obj); - if (expand_fast_array(ctx, p, len) < 0) { - JS_FreeValue(ctx, obj); - fail: - for(i = 0; i < len; i++) - JS_FreeValue(ctx, tab[i]); - return JS_EXCEPTION; - } - p->u.array.count = len; - for(i = 0; i < len; i++) - p->u.array.u.values[i] = tab[i]; - /* update the 'length' field */ - set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len)); - } - return obj; -} - -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_THROW and - JS_PROP_THROW_STRICT. 'this_obj' is the receiver. If obj != - this_obj, then obj must be an object (Reflect.set case). */ -int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValue val, JSValueConst this_obj, int flags) -{ - JSObject *p, *p1; - JSShapeProperty *prs; - JSProperty *pr; - uint32_t tag; - JSPropertyDescriptor desc; - int ret; -#if 0 - printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); -#endif - tag = JS_VALUE_GET_TAG(this_obj); - if (unlikely(tag != JS_TAG_OBJECT)) { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = NULL; - p1 = JS_VALUE_GET_OBJ(obj); - goto prototype_lookup; - } else { - switch(tag) { - case JS_TAG_NULL: - JS_FreeValue(ctx, val); - JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); - return -1; - case JS_TAG_UNDEFINED: - JS_FreeValue(ctx, val); - JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); - return -1; - default: - /* even on a primitive type we can have setters on the prototype */ - p = NULL; - p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); - goto prototype_lookup; - } - } - } else { - p = JS_VALUE_GET_OBJ(this_obj); - p1 = JS_VALUE_GET_OBJ(obj); - if (unlikely(p != p1)) - goto retry2; - } - - /* fast path if obj == this_obj */ - 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) { - /* XXX: already use var_ref->is_const. Cannot simplify use the - writable flag for JS_CLASS_MODULE_NS. */ - if (p->class_id == JS_CLASS_MODULE_NS || pr->u.var_ref->is_const) - 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)) { - JS_FreeValue(ctx, val); - return -1; - } - 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_NewInt32(ctx, idx), val, flags); - else - break; - } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && - p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { - goto typed_array_oob; - } - } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && - p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) { - JS_FreeValue(ctx, val); - return -1; - } - typed_array_oob: - if (p == p1) { - /* must convert the argument even if out of bound access */ - 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; - } - } else { - JS_FreeValue(ctx, val); - } - 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_DupValue(ctx, 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_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - ret = em->get_own_property(ctx, &desc, - obj1, prop); - JS_FreeValue(ctx, obj1); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - 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(!p)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); - } - - if (unlikely(!p->extensible)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); - } - - if (likely(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); - } else { - goto generic_create_prop; - } - } else { - goto generic_create_prop; - } - } else { - if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) - goto generic_create_prop; - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { - JS_FreeValue(ctx, val); - return -1; - } - pr->u.value = val; - return TRUE; - } - } else { - /* generic case: modify the property in this_obj if it already exists */ - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE) || - p->class_id == JS_CLASS_MODULE_NS) { - read_only_prop: - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); - } - } - ret = JS_DefineProperty(ctx, this_obj, prop, val, - JS_UNDEFINED, JS_UNDEFINED, - JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; - } 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); - JS_FreeValue(ctx, val); - return ret; - } - } -} - -/* 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)) - return -1; - /* 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)) - return -1; - 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)) - return -1; - 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)) - return -1; - 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)) - return -1; - 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)) - return -1; - 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)) - return -1; - 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)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - ta_out_of_bound: - return TRUE; - } - 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, this_obj, 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_NewUint32(ctx, 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_NewInt32(ctx, 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; -} - -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, this_obj, 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; - JSVarRef *var_ref; - JSObject *delete_obj; - - /* 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_DupValue(ctx, 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_NewUint32(ctx, len)); - } - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - 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"); - } - - var_ref = NULL; - delete_obj = NULL; - 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; - if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { - JSObject *p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars); - JSShapeProperty *prs1; - JSProperty *pr1; - prs1 = find_own_property(&pr1, p1, prop); - if (prs1) { - delete_obj = p1; - var_ref = pr1->u.var_ref; - var_ref->header.ref_count++; - } else { - var_ref = js_create_var_ref(ctx, FALSE); - if (!var_ref) - return -1; - } - var_ref->is_const = !(prop_flags & JS_PROP_WRITABLE); - prop_flags |= JS_PROP_VARREF; - } - } - pr = add_property(ctx, p, prop, prop_flags); - if (unlikely(!pr)) { - if (var_ref) - free_var_ref(ctx->rt, var_ref); - 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_DupValue(ctx, 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_DupValue(ctx, setter)); - } - } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { - if (delete_obj) - delete_property(ctx, delete_obj, prop); - pr->u.var_ref = var_ref; - if (flags & JS_PROP_HAS_VALUE) { - *var_ref->pvalue = JS_DupValue(ctx, val); - } else { - *var_ref->pvalue = JS_UNDEFINED; - } - } else { - if (flags & JS_PROP_HAS_VALUE) { - pr->u.value = JS_DupValue(ctx, 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)) { - 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 (!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_DupValue(ctx, val), FALSE)) { - return -1; - } - /* this code relies on the fact that Uint32 are never allocated */ - val = (JSValueConst)JS_NewUint32(ctx, 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) { - if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) { - if (remove_global_object_property(ctx, p, prs, pr)) - return -1; - } - 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_DupValue(ctx, 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_DupValue(ctx, setter); - pr->u.getset.setter = new_setter; - } - } else { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - /* convert to data descriptor */ - JSVarRef *var_ref; - if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) { - var_ref = js_global_object_find_uninitialized_var(ctx, p, prop, FALSE); - if (!var_ref) - return -1; - } else { - var_ref = NULL; - } - if (js_shape_prepare_update(ctx, p, &prs)) { - if (var_ref) - free_var_ref(ctx->rt, var_ref); - 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)); - if (var_ref) { - prs->flags = (prs->flags & ~JS_PROP_TMASK) | - JS_PROP_VARREF | JS_PROP_WRITABLE; - pr->u.var_ref = var_ref; - } else { - 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; - } else { - /* update the reference */ - set_value(ctx, pr->u.var_ref->pvalue, - JS_DupValue(ctx, val)); - } - } - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { - JSValue val1; - if (p->class_id == JS_CLASS_MODULE_NS) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false"); - } - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { - pr->u.var_ref->is_const = TRUE; /* mark as read-only */ - prs->flags &= ~JS_PROP_WRITABLE; - } else { - /* if writable is set to false, no longer a - reference (for mapped arguments) */ - val1 = JS_DupValue(ctx, *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_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, val)); - } - return TRUE; - } - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - 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_NewInt32(ctx, idx), JS_DupValue(ctx, 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_NewUint32(ctx, 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_NewInt64(ctx, idx), - val, flags); -} - -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_NewInt64(ctx, 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, JSValueConst obj) -{ - JSProperty *pr; - JSShapeProperty *prs; - JSValueConst 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, JSValueConst 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, JSValueConst obj, - JSValueConst 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; -} - -/* 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: conformance: do these tests in - OP_put_var_ref/OP_get_var_ref ? */ - if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { - 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_DupValue(ctx, 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_DupValue(ctx, ctx->global_obj); - } else { - sp[0] = JS_UNDEFINED; - } - } - sp[1] = JS_AtomToValue(ctx, prop); - return 0; -} - -/* 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); - } -} - -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_IsError(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->class_id == JS_CLASS_ERROR); -} - -/* must be called after JS_Throw() */ -void JS_SetUncatchableException(JSContext *ctx, BOOL flag) -{ - ctx->rt->current_exception_is_uncatchable = flag; -} - -void JS_SetOpaque(JSValue obj, void *opaque) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - 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, (JSValueConst *)&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_DupValue(ctx, 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; - 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_DupValue(ctx, val)); -} - -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 < 128) { - if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) - break; - p++; - } else { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (!lre_is_space(c)) - break; - p = p_next; - } - } - return p - p_start; -} - -static inline int 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 JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ - -/* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */ -#if JS_SHORT_BIG_INT_BITS == 32 -#define JS_SHORT_BIG_INT_MIN INT32_MIN -#define JS_SHORT_BIG_INT_MAX INT32_MAX -#elif JS_SHORT_BIG_INT_BITS == 64 -#define JS_SHORT_BIG_INT_MIN INT64_MIN -#define JS_SHORT_BIG_INT_MAX INT64_MAX -#else -#error unsupported -#endif - -#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) - -#if JS_LIMB_BITS == 32 -/* a != 0 */ -static inline js_limb_t js_limb_clz(js_limb_t a) -{ - return clz32(a); -} -#else -static inline js_limb_t js_limb_clz(js_limb_t a) -{ - return clz64(a); -} -#endif - -/* handle a = 0 too */ -static inline js_limb_t js_limb_safe_clz(js_limb_t a) -{ - if (a == 0) - return JS_LIMB_BITS; - else - return js_limb_clz(a); -} - -static js_limb_t 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 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 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 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 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 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] = 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 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 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 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 = udiv1norm_init(b); - for(i = n - 1; i >= 0; i--) { - tabr[i] = 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 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] = mp_div1norm(tabq, taba, na, b1, 0); - return; - } - n = na - nb; - - if (n >= UDIV1NORM_THRESHOLD) - b1_inv = 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) { - 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 = 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 = 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 = 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 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 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) -{ -#if JS_LIMB_BITS == 64 - return js_bigint_set_si(buf, a); -#else - 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; -#endif -} - -/* 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--) { -#if JS_LIMB_BITS == 32 - printf(" %08x", tab[i]); -#else - printf(" %016" PRIx64, tab[i]); -#endif - } - 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 JS_LIMB_BITS == 64 - return js_bigint_new_si(ctx, a); -#else - 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; - } -#endif -} - -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; -#if JS_LIMB_BITS == 64 - r->tab[0] = a; - r->tab[1] = 0; -#else - r->tab[0] = a; - r->tab[1] = a >> 32; - r->tab[2] = 0; -#endif - 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_LIMB_BITS == 32 - if (js_bigint_sign(a)) - return INT32_MIN; - else - return INT32_MAX; -#else - if (js_bigint_sign(a)) - return INT64_MIN; - else - return INT64_MAX; -#endif - } -} - -/* 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; - 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)) - mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); - if (js_bigint_sign(b)) - 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) { - 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) { - 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) { - mp_shl(tabb, tabb, nb, shift); - h = 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); - mp_divnorm(q->tab, r->tab, na, tabb, nb); - js_free(ctx, tabb); - - if (is_rem) { - js_free(ctx, q); - if (shift != 0) - mp_shr(r->tab, r->tab, nb, shift, 0); - r->tab[nb++] = 0; - if (a_sign) - 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) { - 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 = 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 { - 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; - } - -#if JS_LIMB_BITS == 32 - a1 = ((uint64_t)t[2] << 32) | t[1]; - a0 = (uint64_t)t[0] << 32; -#else - a1 = t[1]; - a0 = t[0]; -#endif - a0 |= (low_bits != 0); - /* normalize */ - if (a1 == 0) { - /* JS_LIMB_BITS = 64 bit only */ - shift = 64; - a1 = a0; - a0 = 0; - } else { - 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, -#if JS_LIMB_BITS == 64 - 10000000000U, - 100000000000U, - 1000000000000U, - 10000000000000U, - 100000000000000U, - 1000000000000000U, - 10000000000000000U, - 100000000000000000U, - 1000000000000000000U, - 10000000000000000000U, -#endif -}; - -/* 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 = 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 = 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 = 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] = "0123456789abcdefghijklmnopqrstuvwxyz"; - -/* 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 *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 digits_per_limb_table[JS_RADIX_MAX - 1] = { -#if JS_LIMB_BITS == 32 -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, -#else -64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, -#endif -}; - -static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = { -#if JS_LIMB_BITS == 32 - 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, -#else - 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d, - 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821, - 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d, - 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1, - 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49, - 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9, - 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd, - 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981, - 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000, -#endif -}; - -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_safe_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 = 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 = mp_div1(r->tab, r->tab, len, radix_base, 0); - q = limb_to_a(q, v, radix, 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 (to_digit((uint8_t)*p) >= radix) - goto fail; - no_prefix: ; - } else { - no_radix_prefix: - if (!(flags & ATOD_INT_ONLY) && - (atod_type == ATOD_TYPE_FLOAT64) && - strstart(p, "Infinity", &p)) { - double d = 1.0 / 0.0; - if (is_neg) - d = -d; - val = JS_NewFloat64(ctx, d); - goto done; - } - } - if (radix == 0) - radix = 10; - is_float = FALSE; - p_start = p; - while (to_digit((uint8_t)*p) < radix - || (*p == sep && (radix != 10 || - p != p_start + 1 || p[-1] != '0') && - to_digit((uint8_t)p[1]) < radix)) { - p++; - } - if (!(flags & ATOD_INT_ONLY)) { - if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { - is_float = TRUE; - p++; - if (*p == sep) - goto fail; - while (to_digit((uint8_t)*p) < radix || - (*p == sep && to_digit((uint8_t)p[1]) < radix)) - p++; - } - if (p > p_start && - (((*p == 'e' || *p == 'E') && radix == 10) || - ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { - 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; - } else { - if (is_float && radix != 10) - goto fail; - } - } else { - if (atod_type == ATOD_TYPE_FLOAT64) { - if (is_float && radix != 10) - goto fail; - } - } - - 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_NewFloat64(ctx, 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_NewInt32(ctx, 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_DupValue(ctx, 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 = JS_FLOAT64_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_DupValue(ctx, val)); -} - -static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) -{ - return JS_ToNumberFree(ctx, JS_DupValue(ctx, 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_NewInt32(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = JS_NewInt32(ctx, 0); - } else { - /* convert -0 to +0 */ - d = trunc(d) + 0.0; - ret = JS_NewFloat64(ctx, 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; -} - -int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) -{ - return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, - int min, int max, int min_offset) -{ - int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, 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) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ - *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_DupValue(ctx, 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_DupValue(ctx, 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) - 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_DupValue(ctx, val)); -} - -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - if (JS_IsBigInt(ctx, 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) - 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_DupValue(ctx, 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); -} - -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, BOOL is_ToPropertyKey) -{ - uint32_t tag; - char buf[32]; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_STRING: - return JS_DupValue(ctx, val); - case JS_TAG_STRING_ROPE: - return js_linearize_string_rope(ctx, JS_DupValue(ctx, val)); - case JS_TAG_INT: - { - size_t len; - len = i32toa(buf, JS_VALUE_GET_INT(val)); - return js_new_string8_len(ctx, buf, len); - } - break; - 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: - { - JSValue val1, ret; - val1 = JS_ToPrimitive(ctx, val, HINT_STRING); - if (JS_IsException(val1)) - return val1; - ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); - JS_FreeValue(ctx, val1); - return ret; - } - break; - case JS_TAG_FUNCTION_BYTECODE: - return js_new_string8(ctx, "[function bytecode]"); - case JS_TAG_SYMBOL: - if (is_ToPropertyKey) { - return JS_DupValue(ctx, 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); - default: - return js_new_string8(ctx, "[unsupported type]"); - } -} - -JSValue JS_ToString(JSContext *ctx, JSValueConst val) -{ - return JS_ToStringInternal(ctx, val, FALSE); -} - -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); -} - -JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) -{ - return JS_ToStringInternal(ctx, val, TRUE); -} - -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); -} - -#define JS_PRINT_MAX_DEPTH 8 - -typedef struct { - JSRuntime *rt; - JSContext *ctx; /* may be NULL */ - JSPrintValueOptions options; - JSPrintValueWrite *write_func; - void *write_opaque; - int level; - JSObject *print_stack[JS_PRINT_MAX_DEPTH]; /* level values */ -} JSPrintValueState; - -static void js_print_value(JSPrintValueState *s, JSValueConst val); - -static void js_putc(JSPrintValueState *s, char c) -{ - s->write_func(s->write_opaque, &c, 1); -} - -static void js_puts(JSPrintValueState *s, const char *str) -{ - s->write_func(s->write_opaque, str, strlen(str)); -} - -static void __attribute__((format(printf, 2, 3))) js_printf(JSPrintValueState *s, const char *fmt, ...) -{ - va_list ap; - char buf[256]; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - s->write_func(s->write_opaque, buf, strlen(buf)); -} - -static void js_print_float64(JSPrintValueState *s, double d) -{ - JSDTOATempMem dtoa_mem; - char buf[32]; - int len; - len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &dtoa_mem); - s->write_func(s->write_opaque, buf, len); -} - -static uint32_t js_string_get_length(JSValueConst val) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { - JSString *p = JS_VALUE_GET_STRING(val); - return p->len; - } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { - JSStringRope *r = JS_VALUE_GET_PTR(val); - return r->len; - } else { - return 0; - } -} - -/* pretty print the first 'len' characters of 'p' */ -static void js_print_string1(JSPrintValueState *s, JSString *p, int len, int sep) -{ - uint8_t buf[UTF8_CHAR_LEN_MAX]; - int l, i, c, c1; - - for(i = 0; i < len; i++) { - c = string_get(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 '\\': - quote: - js_putc(s, '\\'); - js_putc(s, c); - break; - default: - if (c == sep) - goto quote; - if (c >= 32 && c <= 126) { - js_putc(s, c); - } else if (c < 32 || - (c >= 0x7f && c <= 0x9f)) { - escape: - js_printf(s, "\\u%04x", c); - } else { - if (is_hi_surrogate(c)) { - if ((i + 1) >= len) - goto escape; - c1 = string_get(p, i + 1); - if (!is_lo_surrogate(c1)) - goto escape; - i++; - c = from_surrogate(c, c1); - } else if (is_lo_surrogate(c)) { - goto escape; - } - l = unicode_to_utf8(buf, c); - s->write_func(s->write_opaque, (char *)buf, l); - } - break; - } - } -} - -static void js_print_string_rec(JSPrintValueState *s, JSValueConst val, - int sep, uint32_t pos) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { - JSString *p = JS_VALUE_GET_STRING(val); - uint32_t len; - if (pos < s->options.max_string_length) { - len = min_uint32(p->len, s->options.max_string_length - pos); - js_print_string1(s, p, len, sep); - } - } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { - JSStringRope *r = JS_VALUE_GET_PTR(val); - js_print_string_rec(s, r->left, sep, pos); - js_print_string_rec(s, r->right, sep, pos + js_string_get_length(r->left)); - } else { - js_printf(s, "", (int)JS_VALUE_GET_TAG(val)); - } -} - -static void js_print_string(JSPrintValueState *s, JSValueConst val) -{ - int sep; - if (s->options.raw_dump && JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { - JSString *p = JS_VALUE_GET_STRING(val); - js_printf(s, "%d", p->header.ref_count); - sep = (p->header.ref_count == 1) ? '\"' : '\''; - } else { - sep = '\"'; - } - js_putc(s, sep); - js_print_string_rec(s, val, sep, 0); - js_putc(s, sep); - if (js_string_get_length(val) > s->options.max_string_length) { - uint32_t n = js_string_get_length(val) - s->options.max_string_length; - js_printf(s, "... %u more character%s", n, n > 1 ? "s" : ""); - } -} - -static void js_print_raw_string(JSPrintValueState *s, JSValueConst val) -{ - const char *cstr; - size_t len; - cstr = JS_ToCStringLen(s->ctx, &len, val); - if (cstr) { - s->write_func(s->write_opaque, cstr, len); - JS_FreeCString(s->ctx, cstr); - } -} - -static BOOL is_ascii_ident(const JSString *p) -{ - int i, c; - - if (p->len == 0) - return FALSE; - for(i = 0; i < p->len; i++) { - c = string_get(p, i); - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) - return FALSE; - } - return TRUE; -} - -static void js_print_atom(JSPrintValueState *s, JSAtom atom) -{ - int i; - if (__JS_AtomIsTaggedInt(atom)) { - js_printf(s, "%u", __JS_AtomToUInt32(atom)); - } else if (atom == JS_ATOM_NULL) { - js_puts(s, ""); - } else { - assert(atom < s->rt->atom_size); - JSString *p; - p = s->rt->atom_array[atom]; - if (is_ascii_ident(p)) { - for(i = 0; i < p->len; i++) { - js_putc(s, string_get(p, i)); - } - } else { - js_putc(s, '"'); - js_print_string1(s, p, p->len, '\"'); - js_putc(s, '"'); - } - } -} - -/* return 0 if invalid length */ -static uint32_t js_print_array_get_length(JSObject *p) -{ - JSProperty *pr; - JSShapeProperty *prs; - JSValueConst val; - - prs = find_own_property(&pr, p, JS_ATOM_length); - if (!prs) - return 0; - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) - return 0; - val = pr->u.value; - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_INT: - return JS_VALUE_GET_INT(val); - case JS_TAG_FLOAT64: - return (uint32_t)JS_VALUE_GET_FLOAT64(val); - default: - return 0; - } -} - -static void js_print_comma(JSPrintValueState *s, int *pcomma_state) -{ - switch(*pcomma_state) { - case 0: - break; - case 1: - js_printf(s, ", "); - break; - case 2: - js_printf(s, " { "); - break; - } - *pcomma_state = 1; -} - -static void js_print_more_items(JSPrintValueState *s, int *pcomma_state, - uint32_t n) -{ - js_print_comma(s, pcomma_state); - js_printf(s, "... %u more item%s", n, n > 1 ? "s" : ""); -} - -/* similar to js_regexp_toString() but without side effect */ -static void js_print_regexp(JSPrintValueState *s, JSObject *p1) -{ - JSRegExp *re = &p1->u.regexp; - JSString *p; - int i, n, c, c2, bra, flags; - static const char regexp_flags[] = { 'g', 'i', 'm', 's', 'u', 'y', 'd', 'v' }; - - if (!re->pattern || !re->bytecode) { - /* the regexp fields are zeroed at init */ - js_puts(s, "[uninitialized_regexp]"); - return; - } - p = re->pattern; - js_putc(s, '/'); - if (p->len == 0) { - js_puts(s, "(?:)"); - } else { - bra = 0; - for (i = 0, n = p->len; i < n;) { - c2 = -1; - switch (c = string_get(p, i++)) { - case '\\': - if (i < n) - c2 = string_get(p, i++); - break; - case ']': - bra = 0; - break; - case '[': - if (!bra) { - if (i < n && string_get(p, i) == ']') - c2 = string_get(p, i++); - bra = 1; - } - break; - case '\n': - c = '\\'; - c2 = 'n'; - break; - case '\r': - c = '\\'; - c2 = 'r'; - break; - case '/': - if (!bra) { - c = '\\'; - c2 = '/'; - } - break; - } - js_putc(s, c); - if (c2 >= 0) - js_putc(s, c2); - } - } - js_putc(s, '/'); - - flags = lre_get_flags(re->bytecode->u.str8); - for(i = 0; i < countof(regexp_flags); i++) { - if ((flags >> i) & 1) { - js_putc(s, regexp_flags[i]); - } - } -} - -/* similar to js_error_toString() but without side effect */ -static void js_print_error(JSPrintValueState *s, JSObject *p) -{ - const char *str; - size_t len; - - str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_name); - if (!str) { - js_puts(s, "Error"); - } else { - js_puts(s, str); - JS_FreeCString(s->ctx, str); - } - - str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_message); - if (str && str[0] != '\0') { - js_puts(s, ": "); - js_puts(s, str); - } - JS_FreeCString(s->ctx, str); - - /* dump the stack if present */ - str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_stack); - if (str) { - js_putc(s, '\n'); - - /* XXX: should remove the last '\n' in stack as - v8. SpiderMonkey does not do it */ - len = strlen(str); - if (len > 0 && str[len - 1] == '\n') - len--; - s->write_func(s->write_opaque, str, len); - - JS_FreeCString(s->ctx, str); - } -} - -static void js_print_object(JSPrintValueState *s, JSObject *p) -{ - JSRuntime *rt = s->rt; - JSShape *sh; - JSShapeProperty *prs; - JSProperty *pr; - int comma_state; - BOOL is_array; - uint32_t i; - - comma_state = 0; - is_array = FALSE; - if (p->class_id == JS_CLASS_ARRAY) { - is_array = TRUE; - js_printf(s, "[ "); - /* XXX: print array like properties even if not fast array */ - if (p->fast_array) { - uint32_t len, n, len1; - len = js_print_array_get_length(p); - - len1 = min_uint32(p->u.array.count, s->options.max_item_count); - for(i = 0; i < len1; i++) { - js_print_comma(s, &comma_state); - js_print_value(s, p->u.array.u.values[i]); - } - if (len1 < p->u.array.count) - js_print_more_items(s, &comma_state, p->u.array.count - len1); - if (p->u.array.count < len) { - n = len - p->u.array.count; - js_print_comma(s, &comma_state); - js_printf(s, "<%u empty item%s>", n, n > 1 ? "s" : ""); - } - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - uint32_t size = 1 << typed_array_size_log2(p->class_id); - uint32_t len1; - int64_t v; - - js_print_atom(s, rt->class_array[p->class_id].class_name); - js_printf(s, "(%u) [ ", p->u.array.count); - - is_array = TRUE; - len1 = min_uint32(p->u.array.count, s->options.max_item_count); - for(i = 0; i < len1; i++) { - const uint8_t *ptr = p->u.array.u.uint8_ptr + i * size; - js_print_comma(s, &comma_state); - switch(p->class_id) { - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - v = *ptr; - goto ta_int64; - case JS_CLASS_INT8_ARRAY: - v = *(int8_t *)ptr; - goto ta_int64; - case JS_CLASS_INT16_ARRAY: - v = *(int16_t *)ptr; - goto ta_int64; - case JS_CLASS_UINT16_ARRAY: - v = *(uint16_t *)ptr; - goto ta_int64; - case JS_CLASS_INT32_ARRAY: - v = *(int32_t *)ptr; - goto ta_int64; - case JS_CLASS_UINT32_ARRAY: - v = *(uint32_t *)ptr; - goto ta_int64; - case JS_CLASS_BIG_INT64_ARRAY: - v = *(int64_t *)ptr; - ta_int64: - js_printf(s, "%" PRId64, v); - break; - case JS_CLASS_BIG_UINT64_ARRAY: - js_printf(s, "%" PRIu64, *(uint64_t *)ptr); - break; - case JS_CLASS_FLOAT16_ARRAY: - js_print_float64(s, fromfp16(*(uint16_t *)ptr)); - break; - case JS_CLASS_FLOAT32_ARRAY: - js_print_float64(s, *(float *)ptr); - break; - case JS_CLASS_FLOAT64_ARRAY: - js_print_float64(s, *(double *)ptr); - break; - } - } - if (len1 < p->u.array.count) - js_print_more_items(s, &comma_state, p->u.array.count - len1); - } else if (p->class_id == JS_CLASS_BYTECODE_FUNCTION || - (rt->class_array[p->class_id].call != NULL && - p->class_id != JS_CLASS_PROXY)) { - js_printf(s, "[Function"); - /* XXX: allow dump without ctx */ - if (!s->options.raw_dump && s->ctx) { - const char *func_name_str; - js_putc(s, ' '); - func_name_str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_name); - if (!func_name_str || func_name_str[0] == '\0') - js_puts(s, "(anonymous)"); - else - js_puts(s, func_name_str); - JS_FreeCString(s->ctx, func_name_str); - } - js_printf(s, "]"); - comma_state = 2; - } else if (p->class_id == JS_CLASS_MAP || p->class_id == JS_CLASS_SET) { - JSMapState *ms = p->u.opaque; - struct list_head *el; - - if (!ms) - goto default_obj; - js_print_atom(s, rt->class_array[p->class_id].class_name); - js_printf(s, "(%u) { ", ms->record_count); - i = 0; - list_for_each(el, &ms->records) { - JSMapRecord *mr = list_entry(el, JSMapRecord, link); - js_print_comma(s, &comma_state); - if (mr->empty) - continue; - js_print_value(s, mr->key); - if (p->class_id == JS_CLASS_MAP) { - js_printf(s, " => "); - js_print_value(s, mr->value); - } - i++; - if (i >= s->options.max_item_count) - break; - } - if (i < ms->record_count) - js_print_more_items(s, &comma_state, ms->record_count - i); - } else if (p->class_id == JS_CLASS_REGEXP && s->ctx) { - js_print_regexp(s, p); - comma_state = 2; - } else if (p->class_id == JS_CLASS_DATE && s->ctx) { - /* get_date_string() has no side effect */ - JSValue str = get_date_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL, 0x23); /* toISOString() */ - if (JS_IsException(str)) - goto default_obj; - js_print_raw_string(s, str); - JS_FreeValueRT(s->rt, str); - comma_state = 2; - } else if (p->class_id == JS_CLASS_ERROR && s->ctx) { - js_print_error(s, p); - comma_state = 2; - } else { - default_obj: - if (p->class_id != JS_CLASS_OBJECT) { - js_print_atom(s, rt->class_array[p->class_id].class_name); - js_printf(s, " "); - } - js_printf(s, "{ "); - } - - sh = p->shape; /* the shape can be NULL while freeing an object */ - if (sh) { - uint32_t j; - - j = 0; - for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - if (prs->atom != JS_ATOM_NULL) { - if (!(prs->flags & JS_PROP_ENUMERABLE) && - !s->options.show_hidden) { - continue; - } - if (j < s->options.max_item_count) { - pr = &p->prop[i]; - js_print_comma(s, &comma_state); - js_print_atom(s, prs->atom); - js_printf(s, ": "); - - /* XXX: autoinit property */ - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (s->options.raw_dump) { - js_printf(s, "[Getter %p Setter %p]", - pr->u.getset.getter, pr->u.getset.setter); - } else { - if (pr->u.getset.getter && pr->u.getset.setter) { - js_printf(s, "[Getter/Setter]"); - } else if (pr->u.getset.setter) { - js_printf(s, "[Setter]"); - } else { - js_printf(s, "[Getter]"); - } - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - if (s->options.raw_dump) { - js_printf(s, "[varref %p]", (void *)pr->u.var_ref); - } else { - js_print_value(s, *pr->u.var_ref->pvalue); - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - if (s->options.raw_dump) { - js_printf(s, "[autoinit %p %d %p]", - (void *)js_autoinit_get_realm(pr), - js_autoinit_get_id(pr), - (void *)pr->u.init.opaque); - } else { - /* XXX: could autoinit but need to restart - the iteration */ - js_printf(s, "[autoinit]"); - } - } else { - js_print_value(s, pr->u.value); - } - } - j++; - } - } - if (j > s->options.max_item_count) - js_print_more_items(s, &comma_state, j - s->options.max_item_count); - } - if (s->options.raw_dump && js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b = p->u.func.function_bytecode; - if (b->closure_var_count) { - JSVarRef **var_refs; - var_refs = p->u.func.var_refs; - - js_print_comma(s, &comma_state); - js_printf(s, "[[Closure]]: ["); - for(i = 0; i < b->closure_var_count; i++) { - if (i != 0) - js_printf(s, ", "); - js_print_value(s, var_refs[i]->value); - } - js_printf(s, " ]"); - } - if (p->u.func.home_object) { - js_print_comma(s, &comma_state); - js_printf(s, "[[HomeObject]]: "); - js_print_value(s, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); - } - } - - if (!is_array) { - if (comma_state != 2) { - js_printf(s, " }"); - } - } else { - js_printf(s, " ]"); - } -} - -static int js_print_stack_index(JSPrintValueState *s, JSObject *p) -{ - int i; - for(i = 0; i < s->level; i++) - if (s->print_stack[i] == p) - return i; - return -1; -} - -static void js_print_value(JSPrintValueState *s, JSValueConst val) -{ - uint32_t tag = JS_VALUE_GET_NORM_TAG(val); - const char *str; - - switch(tag) { - case JS_TAG_INT: - js_printf(s, "%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: - js_puts(s, str); - break; - case JS_TAG_FLOAT64: - js_print_float64(s, JS_VALUE_GET_FLOAT64(val)); - break; - case JS_TAG_SHORT_BIG_INT: - js_printf(s, "%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); - break; - case JS_TAG_BIG_INT: - if (!s->options.raw_dump && s->ctx) { - JSValue str = js_bigint_to_string(s->ctx, val); - if (JS_IsException(str)) - goto raw_bigint; - js_print_raw_string(s, str); - js_putc(s, 'n'); - JS_FreeValueRT(s->rt, str); - } else { - JSBigInt *p; - int sgn, i; - raw_bigint: - p = JS_VALUE_GET_PTR(val); - /* In order to avoid allocations we just dump the limbs */ - sgn = js_bigint_sign(p); - if (sgn) - js_printf(s, "BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); - js_printf(s, "0x"); - for(i = p->len - 1; i >= 0; i--) { - if (i != p->len - 1) - js_putc(s, '_'); -#if JS_LIMB_BITS == 32 - js_printf(s, "%08x", p->tab[i]); -#else - js_printf(s, "%016" PRIx64, p->tab[i]); -#endif - } - js_putc(s, 'n'); - if (sgn) - js_putc(s, ')'); - } - break; - case JS_TAG_STRING: - case JS_TAG_STRING_ROPE: - if (s->options.raw_dump && tag == JS_TAG_STRING_ROPE) { - JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); - js_printf(s, "[rope len=%d depth=%d]", r->len, r->depth); - } else { - js_print_string(s, val); - } - break; - case JS_TAG_FUNCTION_BYTECODE: - { - JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); - js_puts(s, "[bytecode "); - js_print_atom(s, b->func_name); - js_putc(s, ']'); - } - break; - case JS_TAG_OBJECT: - { - JSObject *p = JS_VALUE_GET_OBJ(val); - int idx; - idx = js_print_stack_index(s, p); - if (idx >= 0) { - js_printf(s, "[circular %d]", idx); - } else if (s->level < s->options.max_depth) { - s->print_stack[s->level++] = p; - js_print_object(s, JS_VALUE_GET_OBJ(val)); - s->level--; - } else { - JSAtom atom = s->rt->class_array[p->class_id].class_name; - js_putc(s, '['); - js_print_atom(s, atom); - if (s->options.raw_dump) { - js_printf(s, " %p", (void *)p); - } - js_putc(s, ']'); - } - } - break; - case JS_TAG_SYMBOL: - { - JSAtomStruct *p = JS_VALUE_GET_PTR(val); - js_puts(s, "Symbol("); - js_print_atom(s, js_get_atom_index(s->rt, p)); - js_putc(s, ')'); - } - break; - case JS_TAG_MODULE: - js_puts(s, "[module]"); - break; - default: - js_printf(s, "[unknown tag %d]", tag); - break; - } -} - -void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options) -{ - memset(options, 0, sizeof(*options)); - options->max_depth = 2; - options->max_string_length = 1000; - options->max_item_count = 100; -} - -static void JS_PrintValueInternal(JSRuntime *rt, JSContext *ctx, - JSPrintValueWrite *write_func, void *write_opaque, - JSValueConst val, const JSPrintValueOptions *options) -{ - JSPrintValueState ss, *s = &ss; - if (options) - s->options = *options; - else - JS_PrintValueSetDefaultOptions(&s->options); - if (s->options.max_depth <= 0) - s->options.max_depth = JS_PRINT_MAX_DEPTH; - else - s->options.max_depth = min_int(s->options.max_depth, JS_PRINT_MAX_DEPTH); - if (s->options.max_string_length == 0) - s->options.max_string_length = UINT32_MAX; - if (s->options.max_item_count == 0) - s->options.max_item_count = UINT32_MAX; - s->rt = rt; - s->ctx = ctx; - s->write_func = write_func; - s->write_opaque = write_opaque; - s->level = 0; - js_print_value(s, val); -} - -void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_opaque, - JSValueConst val, const JSPrintValueOptions *options) -{ - JS_PrintValueInternal(rt, NULL, write_func, write_opaque, val, options); -} - -void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque, - JSValueConst val, const JSPrintValueOptions *options) -{ - JS_PrintValueInternal(ctx->rt, ctx, write_func, write_opaque, val, options); -} - -static void js_dump_value_write(void *opaque, const char *buf, size_t len) -{ - FILE *fo = opaque; - fwrite(buf, 1, len, fo); -} - -static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) -{ - JSPrintValueState ss, *s = &ss; - memset(s, 0, sizeof(*s)); - s->rt = ctx->rt; - s->ctx = ctx; - s->write_func = js_dump_value_write; - s->write_opaque = stdout; - js_print_atom(s, atom); -} - -static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom atom) -{ - printf("%s=", str); - print_atom(ctx, atom); - printf("\n"); -} - -static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val) -{ - printf("%s=", str); - JS_PrintValue(ctx, js_dump_value_write, stdout, val, NULL); - printf("\n"); -} - -static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val) -{ - printf("%s=", str); - JS_PrintValueRT(rt, js_dump_value_write, stdout, val, NULL); - printf("\n"); -} - -static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) -{ - printf("%14s %4s %4s %14s %s\n", - "ADDRESS", "REFS", "SHRF", "PROTO", "CONTENT"); -} - -/* for debug only: dump an object without side effect */ -static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) -{ - JSShape *sh; - JSPrintValueOptions options; - - /* 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 ", "-", "-"); - } - - JS_PrintValueSetDefaultOptions(&options); - options.max_depth = 1; - options.show_hidden = TRUE; - options.raw_dump = TRUE; - JS_PrintValueRT(rt, js_dump_value_write, stdout, JS_MKPTR(JS_TAG_OBJECT, p), &options); - - 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; - case JS_GC_OBJ_TYPE_MODULE: - printf("[module]"); - break; - default: - printf("[unknown %d]", p->gc_obj_type); - break; - } - printf("\n"); - } -} - -/* return -1 if exception (proxy case) or TRUE/FALSE */ -// TODO: should take flags to make proxy resolution and exceptions optional -int JS_IsArray(JSContext *ctx, JSValueConst val) -{ - if (js_resolve_proxy(ctx, &val, TRUE)) - return -1; - if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ARRAY; - } else { - return FALSE; - } -} - -static double js_pow(double a, double b) -{ - if (unlikely(!isfinite(b)) && fabs(a) == 1) { - /* not compatible with IEEE 754 */ - return JS_FLOAT64_NAN; - } else { - return pow(a, b); - } -} - -JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) -{ -#if JS_SHORT_BIG_INT_BITS == 64 - return __JS_NewShortBigInt(ctx, v); -#else - 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); - } -#endif -} - -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_DupValue(ctx, 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 JS_LIMB_BITS == 32 - if (p->len >= 2) - res |= (uint64_t)p->tab[1] << 32; -#endif - JS_FreeValue(ctx, val); - } - *pres = res; - return 0; -} - -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, 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_NewFloat64(ctx, -0.0); - return 0; - } else { - v64 = -v64; - } - break; - default: - abort(); - } - sp[-1] = JS_NewInt64(ctx, 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_NewFloat64(ctx, 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_DupValue(ctx, 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_NewInt32(ctx, ~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_NewFloat64(ctx, -0.0); - return 0; - } - break; - case OP_div: - sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); - return 0; - case OP_mod: - if (v1 < 0 || v2 <= 0) { - sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); - return 0; - } else { - v = (int64_t)v1 % (int64_t)v2; - } - break; - case OP_pow: - sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); - return 0; - default: - abort(); - } - sp[-2] = JS_NewInt64(ctx, 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: - 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_pow(d1, d2); - break; - default: - abort(); - } - sp[-2] = __JS_NewFloat64(ctx, dr); - } - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static inline BOOL tag_is_string(uint32_t tag) -{ - return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE; -} - -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_NewFloat64(ctx, 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_NewInt64(ctx, 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; - sp[-2] = __JS_NewFloat64(ctx, d1 + d2); - } - 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_NewInt32(ctx, 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(ctx, JS_VALUE_GET_STRING(op1), - JS_VALUE_GET_STRING(op2)); - } else { - res = js_string_rope_compare(ctx, 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) && - tag_is_string(tag2)) || - ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && - tag_is_string(tag1)))) { - 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; - } - } - - 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_NewBool(ctx, 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); - } - } 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_string(tag2)) { - /* needed when comparing strings and ropes */ - res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); - } 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_eq2(ctx, op1, op2, JS_EQ_STRICT); - } else if (tag1 == JS_TAG_BOOL) { - op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); - goto redo; - } else if (tag2 == JS_TAG_BOOL) { - op2 = JS_NewInt32(ctx, 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_NewBool(ctx, 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_NewUint32(ctx, r); - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -/* XXX: Should take JSValueConst arguments */ -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(ctx, JS_VALUE_GET_STRING(op1), - JS_VALUE_GET_STRING(op2)); - } else { - res = (js_string_rope_compare(ctx, 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, JSValueConst op1, JSValueConst op2) -{ - return js_strict_eq2(ctx, - JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), - JS_EQ_STRICT); -} - -BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2) -{ - return js_strict_eq(ctx, op1, op2); -} - -static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) -{ - return js_strict_eq2(ctx, - JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), - JS_EQ_SAME_VALUE); -} - -BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2) -{ - return js_same_value(ctx, op1, op2); -} - -static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) -{ - return js_strict_eq2(ctx, - JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), - JS_EQ_SAME_VALUE_ZERO); -} - -BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2) -{ - return js_same_value_zero(ctx, op1, op2); -} - -static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) -{ - BOOL res; - res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT); - sp[-2] = JS_NewBool(ctx, 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_NewBool(ctx, 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_NewBool(ctx, ret); - return 0; -} - -static __exception int js_has_unscopable(JSContext *ctx, JSValueConst 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; - BOOL 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_NewBool(ctx, ret); - return 0; -} - -static __exception int js_operator_typeof(JSContext *ctx, JSValueConst 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_NewBool(ctx, ret); - return 0; -} - -/* XXX: not 100% compatible, but mozilla seems to use a similar - implementation to ensure that caller in non strict mode does not - throw (ES5 compatibility) */ -static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); - if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype || argc >= 1) { - 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 && b->has_debug) { - return JS_AtomToString(ctx, b->debug.filename); - } - return JS_UNDEFINED; -} - -static JSValue js_function_proto_lineNumber(JSContext *ctx, - JSValueConst this_val, int is_col) -{ - JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); - if (b && b->has_debug) { - int line_num, col_num; - line_num = find_line_num(ctx, b, -1, &col_num); - if (is_col) - return JS_NewInt32(ctx, col_num); - else - return JS_NewInt32(ctx, line_num); - } - 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_NewInt32(ctx, argc); /* length */ - props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */ - props[2].u.getset.getter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */ - props[2].u.getset.setter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, 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_DupValue(ctx, 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, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSVarRef **var_refs = p->u.array.u.var_refs; - int i; - for(i = 0; i < p->u.array.count; 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); - JSVarRef **var_refs = p->u.array.u.var_refs; - int i; - - for(i = 0; i < p->u.array.count; 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_NewInt32(ctx, argc); /* length */ - props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */ - props[2].u.value = JS_DupValue(ctx, 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, FALSE); - 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_DupValue(ctx, 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, *p1; - JSPropertyEnum *tab_atom; - int i; - JSValue enum_obj; - 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; - it->tab_atom = NULL; - it->atom_count = 0; - it->in_prototype_chain = FALSE; - p1 = JS_VALUE_GET_OBJ(enum_obj); - p1->u.for_in_iterator = it; - - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) - return enum_obj; - - 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->atom_count = p->u.array.count; - } else { - normal_case: - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { - JS_FreeValue(ctx, enum_obj); - return JS_EXCEPTION; - } - it->tab_atom = tab_atom; - it->atom_count = tab_atom_count; - } - return enum_obj; -} - -/* 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; -} - -/* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */ -static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx, - JSValueConst enum_obj) -{ - JSObject *p; - JSForInIterator *it; - JSPropertyEnum *tab_atom; - uint32_t tab_atom_count, i; - JSValue obj1; - - p = JS_VALUE_GET_OBJ(enum_obj); - it = p->u.for_in_iterator; - - /* check if there are enumerable properties in the prototype chain (fast path) */ - obj1 = JS_DupValue(ctx, it->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_FreePropertyEnum(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; - } - } - JS_FreeValue(ctx, obj1); - return 1; - - slow_path: - /* add the visited properties, even if they are not enumerable */ - if (it->is_array) { - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, - JS_VALUE_GET_OBJ(it->obj), - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { - goto fail; - } - it->is_array = FALSE; - it->tab_atom = tab_atom; - it->atom_count = tab_atom_count; - } - - for(i = 0; i < it->atom_count; i++) { - if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0) - goto fail; - } - return 0; - fail: - return -1; -} - -/* enum_obj -> enum_obj value done */ -static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) -{ - JSValueConst enum_obj; - JSObject *p; - JSAtom prop; - JSForInIterator *it; - JSPropertyEnum *tab_atom; - uint32_t tab_atom_count; - 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->idx >= it->atom_count) { - if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj)) - goto done; /* not an object */ - /* no more property in the current object: look in the prototype */ - if (!it->in_prototype_chain) { - ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj); - if (ret < 0) - return -1; - if (ret) - goto done; - it->in_prototype_chain = TRUE; - } - it->obj = JS_GetPrototypeFree(ctx, it->obj); - if (JS_IsException(it->obj)) - return -1; - if (JS_IsNull(it->obj)) - goto done; /* no more prototype */ - - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) - return -1; - - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, - JS_VALUE_GET_OBJ(it->obj), - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { - return -1; - } - JS_FreePropertyEnum(ctx, it->tab_atom, it->atom_count); - it->tab_atom = tab_atom; - it->atom_count = tab_atom_count; - it->idx = 0; - } else { - if (it->is_array) { - prop = __JS_AtomFromUInt32(it->idx); - it->idx++; - } else { - BOOL is_enumerable; - prop = it->tab_atom[it->idx].atom; - is_enumerable = it->tab_atom[it->idx].is_enumerable; - it->idx++; - if (it->in_prototype_chain) { - /* slow case: we are in the prototype chain */ - ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop); - if (ret < 0) - return ret; - if (ret) - continue; /* already visited */ - /* add to the visited property list */ - if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL, - JS_PROP_ENUMERABLE) < 0) - return -1; - } - if (!is_enumerable) - continue; - } - /* check if the property was deleted */ - ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->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; -} - -/* Note: always return JS_UNDEFINED when *pdone = TRUE. */ -static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, - JSValueConst method, - int argc, JSValueConst *argv, BOOL *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_NewBool(ctx, done); - return 0; -} - -static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp) -{ - JSValue obj, iter, next; - - sp[-1] = JS_UNDEFINED; /* disable the catch offset so that - exceptions do not close the iterator */ - iter = sp[-3]; - next = sp[-2]; - obj = JS_Call(ctx, next, iter, 0, NULL); - if (JS_IsException(obj)) - return -1; - sp[0] = obj; - return 0; -} - -static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, - BOOL *pdone) -{ - JSValue done_val, value; - BOOL 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; - BOOL 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); - /* put again the catch offset so that exceptions close the - iterator */ - sp[-2] = JS_NewCatchOffset(ctx, 0); - sp[-1] = value; - sp[0] = JS_NewBool(ctx, 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_NewBool(ctx, 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, - BOOL *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, JSValueConst 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, JSValueConst 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; - JSCFunctionType ft; - - 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; - 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; - } - - ft.iterator_next = js_array_iterator_next; - if (is_array_iterator - && JS_IsCFunction(ctx, method, ft.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_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) - goto exception; - } - } else { - general_case: - for (;;) { - BOOL 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_NewInt32(ctx, 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, - JSValueConst target, - JSValueConst source, - JSValueConst 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_FreePropertyEnum(ctx, tab_atom, tab_atom_count); - return 0; - exception: - JS_FreePropertyEnum(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; -} - -static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical) -{ - JSVarRef *var_ref; - var_ref = js_malloc(ctx, sizeof(JSVarRef)); - if (!var_ref) - return NULL; - var_ref->header.ref_count = 1; - if (is_lexical) - var_ref->value = JS_UNINITIALIZED; - else - var_ref->value = JS_UNDEFINED; - var_ref->pvalue = &var_ref->value; - var_ref->is_detached = TRUE; - var_ref->is_lexical = FALSE; - var_ref->is_const = FALSE; - 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; - JSBytecodeVarDef *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]; - } - assert(vd->is_captured); - var_ref_idx = vd->var_ref_idx; - assert(var_ref_idx < b->var_ref_count); - var_ref = sf->var_refs[var_ref_idx]; - if (var_ref) { - /* reference to the already created local variable */ - assert(var_ref->pvalue == pvalue); - 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; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - 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; - if (sf->js_mode & JS_MODE_ASYNC) { - JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); - /* The stack frame is detached and may be destroyed at any - time so its reference count must be increased. Calling - close_var_refs() when destroying the stack frame is not - possible because it would change the graph between the GC - objects. Another solution could be to temporarily detach - the JSVarRef of async functions during the GC. It would - have the advantage of allowing the release of unused stack - frames in a cycle. */ - async_func->header.ref_count++; - } - var_ref->pvalue = pvalue; - return var_ref; -} - -static void js_global_object_finalizer(JSRuntime *rt, JSValue obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - JS_FreeValueRT(rt, p->u.global_object.uninitialized_vars); -} - -static void js_global_object_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JS_MarkValue(rt, p->u.global_object.uninitialized_vars, mark_func); -} - -static JSVarRef *js_global_object_get_uninitialized_var(JSContext *ctx, JSObject *p1, - JSAtom atom) -{ - JSObject *p = JS_VALUE_GET_OBJ(p1->u.global_object.uninitialized_vars); - JSShapeProperty *prs; - JSProperty *pr; - JSVarRef *var_ref; - - prs = find_own_property(&pr, p, atom); - if (prs) { - assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); - var_ref = pr->u.var_ref; - var_ref->header.ref_count++; - return var_ref; - } - - var_ref = js_create_var_ref(ctx, TRUE); - if (!var_ref) - return NULL; - pr = add_property(ctx, p, atom, JS_PROP_C_W_E | JS_PROP_VARREF); - if (unlikely(!pr)) { - free_var_ref(ctx->rt, var_ref); - return NULL; - } - pr->u.var_ref = var_ref; - var_ref->header.ref_count++; - return var_ref; -} - -/* return a new variable reference. Get it from the uninitialized - variables if it is present. Return NULL in case of memory error. */ -static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p, - JSAtom atom, BOOL is_lexical) -{ - JSObject *p1; - JSShapeProperty *prs; - JSProperty *pr; - JSVarRef *var_ref; - - p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars); - prs = find_own_property(&pr, p1, atom); - if (prs) { - assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); - var_ref = pr->u.var_ref; - var_ref->header.ref_count++; - delete_property(ctx, p1, atom); - if (!is_lexical) - var_ref->value = JS_UNDEFINED; - } else { - var_ref = js_create_var_ref(ctx, is_lexical); - if (!var_ref) - return NULL; - } - return var_ref; -} - -static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv, - BOOL is_direct_or_indirect_eval) -{ - JSObject *p, *p1; - JSShapeProperty *prs; - int flags; - JSProperty *pr; - JSVarRef *var_ref; - - if (cv->is_lexical) { - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; - if (!cv->is_const) - flags |= JS_PROP_WRITABLE; - - prs = find_own_property(&pr, p, cv->var_name); - if (prs) { - assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); - var_ref = pr->u.var_ref; - var_ref->header.ref_count++; - return var_ref; - } - - /* if there is a corresponding global variable, reuse its - reference and create a new one for the global variable */ - p1 = JS_VALUE_GET_OBJ(ctx->global_obj); - prs = find_own_property(&pr, p1, cv->var_name); - if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSVarRef *var_ref1; - var_ref1 = js_create_var_ref(ctx, FALSE); - if (!var_ref1) - return NULL; - var_ref = pr->u.var_ref; - var_ref1->value = var_ref->value; - var_ref->value = JS_UNINITIALIZED; - pr->u.var_ref = var_ref1; - goto add_var_ref; - } - } else { - p = JS_VALUE_GET_OBJ(ctx->global_obj); - flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE; - if (is_direct_or_indirect_eval) - flags |= JS_PROP_CONFIGURABLE; - - retry: - prs = find_own_property(&pr, p, cv->var_name); - if (prs) { - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) { - if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs)) - return NULL; - goto retry; - } else if ((prs->flags & JS_PROP_TMASK) != JS_PROP_VARREF) { - var_ref = js_global_object_get_uninitialized_var(ctx, p, cv->var_name); - if (!var_ref) - return NULL; - } else { - var_ref = pr->u.var_ref; - var_ref->header.ref_count++; - } - if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL && - (prs->flags & JS_PROP_CONFIGURABLE)) { - /* update the property flags if possible when - declaring a global function */ - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - free_property(ctx->rt, pr, prs->flags); - prs->flags = flags | JS_PROP_VARREF; - pr->u.var_ref = var_ref; - var_ref->header.ref_count++; - } else { - assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); - prs->flags = (prs->flags & ~JS_PROP_C_W_E) | flags; - } - var_ref->is_const = FALSE; - } - return var_ref; - } - - if (!p->extensible) { - return js_global_object_get_uninitialized_var(ctx, p, cv->var_name); - } - } - - /* if there is a corresponding uninitialized variable, use it */ - p1 = JS_VALUE_GET_OBJ(ctx->global_obj); - var_ref = js_global_object_find_uninitialized_var(ctx, p1, cv->var_name, cv->is_lexical); - if (!var_ref) - return NULL; - add_var_ref: - if (cv->is_lexical) { - var_ref->is_lexical = TRUE; - var_ref->is_const = cv->is_const; - } - - pr = add_property(ctx, p, cv->var_name, flags | JS_PROP_VARREF); - if (unlikely(!pr)) { - free_var_ref(ctx->rt, var_ref); - return NULL; - } - pr->u.var_ref = var_ref; - var_ref->header.ref_count++; - return var_ref; -} - -static JSVarRef *js_closure_global_var(JSContext *ctx, JSClosureVar *cv) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSVarRef *var_ref; - - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, cv->var_name); - if (prs) { - assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); - var_ref = pr->u.var_ref; - var_ref->header.ref_count++; - return var_ref; - } - p = JS_VALUE_GET_OBJ(ctx->global_obj); - redo: - prs = find_own_property(&pr, p, cv->var_name); - if (prs) { - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs)) - return NULL; - goto redo; - } - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - var_ref = pr->u.var_ref; - var_ref->header.ref_count++; - return var_ref; - } - } - return js_global_object_get_uninitialized_var(ctx, p, cv->var_name); -} - -static JSValue js_closure2(JSContext *ctx, JSValue func_obj, - JSFunctionBytecode *b, - JSVarRef **cur_var_refs, - JSStackFrame *sf, - BOOL is_eval, JSModuleDef *m) -{ - 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; - if (is_eval) { - /* first pass to check the global variable definitions */ - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - if (cv->closure_type == JS_CLOSURE_GLOBAL_DECL) { - int flags; - flags = 0; - if (cv->is_lexical) - flags |= DEFINE_GLOBAL_LEX_VAR; - if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL) - flags |= DEFINE_GLOBAL_FUNC_VAR; - if (JS_CheckDefineGlobalVar(ctx, cv->var_name, flags)) - goto fail; - } - } - } - 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_MODULE_IMPORT: - /* imported from other modules */ - continue; - case JS_CLOSURE_MODULE_DECL: - var_ref = js_create_var_ref(ctx, cv->is_lexical); - break; - case JS_CLOSURE_GLOBAL_DECL: - var_ref = js_closure_define_global_var(ctx, cv, b->is_direct_or_indirect_eval); - break; - case JS_CLOSURE_GLOBAL: - var_ref = js_closure_global_var(ctx, cv); - break; - 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; - set_cycle_flag(ctx, obj); - set_cycle_flag(ctx, this_val); - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, - JS_DupValue(ctx, 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, BOOL is_eval) -{ - 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, is_eval, NULL); - 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_DupValue(ctx, 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_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); - parent_class = JS_DupValue(ctx, 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, FALSE, NULL); - 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_NewInt32(ctx, 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_DupValue(ctx, 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_DupValue(ctx, proto), JS_PROP_THROW) < 0) - goto fail; - set_cycle_flag(ctx, ctor); - set_cycle_flag(ctx, proto); - - 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, JSStackFrame *sf, JSVarRef *var_ref) -{ - if (sf->js_mode & JS_MODE_ASYNC) { - JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); - async_func_free(rt, async_func); - } - var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue); - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; -} - -static void close_var_refs(JSRuntime *rt, JSFunctionBytecode *b, JSStackFrame *sf) -{ - JSVarRef *var_ref; - int i; - - for(i = 0; i < b->var_ref_count; i++) { - var_ref = sf->var_refs[i]; - if (var_ref) - close_var_ref(rt, sf, 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, sf, 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->js_mode = 0; - sf->cur_func = (JSValue)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_NewFloat64(ctx, 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_NewFloat64(ctx, 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, -} OPSpecialObjectEnum; - -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 -#define FUNC_RET_INITIAL_YIELD 3 - -/* 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, JSValue *argv, int flags) -{ - JSRuntime *rt = caller_ctx->rt; - JSContext *ctx; - JSObject *p; - JSFunctionBytecode *b; - JSStackFrame sf_s, *sf = &sf_s; - const 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; - -#if !DIRECT_DISPATCH -#define SWITCH(pc) switch (opcode = *pc++) -#define CASE(op) case op -#define DEFAULT default -#define BREAK break -#else - static const void * const dispatch_table[256] = { -#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, -#if SHORT_OPCODES -#define def(id, size, n_pop, n_push, f) -#else -#define def(id, size, n_pop, n_push, f) && case_default, -#endif -#include "quickjs-opcode.h" - [ OP_COUNT ... 255 ] = &&case_default - }; -#define SWITCH(pc) 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_ThrowTypeError(caller_ctx, "not a function"); - } - return call_func(caller_ctx, func_obj, this_obj, argc, - (JSValueConst *)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->js_mode = b->js_mode; - arg_buf = argv; - sf->arg_count = argc; - sf->cur_func = (JSValue)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_DupValue(caller_ctx, 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); - for(i = 0; i < b->var_ref_count; i++) - sf->var_refs[i] = NULL; - sp = stack_buf; - pc = b->byte_code_buf; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - ctx = b->realm; /* set the current realm */ - - restart: - for(;;) { - int call_argc; - JSValue *call_argv; - - SWITCH(pc) { - CASE(OP_push_i32): - *sp++ = JS_NewInt32(ctx, 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_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - BREAK; -#if SHORT_OPCODES - 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_NewInt32(ctx, opcode - OP_push_0); - BREAK; - CASE(OP_push_i8): - *sp++ = JS_NewInt32(ctx, get_i8(pc)); - pc += 1; - BREAK; - CASE(OP_push_i16): - *sp++ = JS_NewInt32(ctx, get_i16(pc)); - pc += 2; - BREAK; - CASE(OP_push_const8): - *sp++ = JS_DupValue(ctx, b->cpool[*pc++]); - BREAK; - CASE(OP_fclosure8): - *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf, FALSE); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_push_empty_string): - *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); - BREAK; -#endif - 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->js_mode & JS_MODE_STRICT)) { - 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_DupValue(ctx, ctx->global_obj); - } else { - val = JS_ToObject(ctx, this_obj); - if (JS_IsException(val)) - goto exception; - } - } else { - normal_this: - val = JS_DupValue(ctx, 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, (JSValueConst *)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: - *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)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_DupValue(ctx, sf->cur_func); - break; - case OP_SPECIAL_OBJECT_NEW_TARGET: - *sp++ = JS_DupValue(ctx, 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_DupValue(ctx, 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; - default: - abort(); - } - } - BREAK; - CASE(OP_rest): - { - int first = get_u16(pc); - pc += 2; - first = min_int(first, argc); - *sp++ = js_create_array(ctx, argc - first, (JSValueConst *)(argv + first)); - 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_DupValue(ctx, sp[-1]); - sp++; - BREAK; - CASE(OP_dup2): /* a b -> a b a b */ - sp[0] = JS_DupValue(ctx, sp[-2]); - sp[1] = JS_DupValue(ctx, sp[-1]); - sp += 2; - BREAK; - CASE(OP_dup3): /* a b c -> a b c a b c */ - sp[0] = JS_DupValue(ctx, sp[-3]); - sp[1] = JS_DupValue(ctx, sp[-2]); - sp[2] = JS_DupValue(ctx, sp[-1]); - sp += 3; - BREAK; - CASE(OP_dup1): /* a b -> a a b */ - sp[0] = sp[-1]; - sp[-1] = JS_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - *sp++ = js_closure(ctx, bfunc, var_refs, sf, FALSE); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_call0): - CASE(OP_call1): - CASE(OP_call2): - CASE(OP_call3): - call_argc = opcode - OP_call0; - goto has_call_argc; -#endif - 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, 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, 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, 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; - ret_val = js_create_array_free(ctx, call_argc, sp - call_argc); - 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, (JSValueConst *)&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, (JSValueConst *)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): - { - JSValueConst obj; - int scope_idx; - call_argc = get_u16(pc); - scope_idx = get_u16(pc + 2) + ARG_SCOPE_END; - 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, 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; - JSValueConst obj; - - scope_idx = get_u16(pc) + ARG_SCOPE_END; - 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, - (JSValueConst *)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_NewRegexp(ctx, sp[-2], sp[-1]); - sp--; - if (JS_IsException(sp[-1])) - goto exception; - } - BREAK; - - CASE(OP_get_super): - { - JSValue proto; - sf->cur_pc = pc; - 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): - { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - if (unlikely(JS_IsUninitialized(val))) { - JSClosureVar *cv = &b->closure_var[idx]; - if (cv->is_lexical) { - JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name); - goto exception; - } else { - sf->cur_pc = pc; - sp[0] = JS_GetPropertyInternal(ctx, ctx->global_obj, - cv->var_name, - ctx->global_obj, - opcode - OP_get_var_undef); - if (JS_IsException(sp[0])) - goto exception; - } - } else { - sp[0] = JS_DupValue(ctx, val); - } - sp++; - } - BREAK; - - CASE(OP_put_var): - CASE(OP_put_var_init): - { - int idx, ret; - JSVarRef *var_ref; - idx = get_u16(pc); - pc += 2; - var_ref = var_refs[idx]; - if (unlikely(JS_IsUninitialized(*var_ref->pvalue) || - var_ref->is_const)) { - JSClosureVar *cv = &b->closure_var[idx]; - if (var_ref->is_lexical) { - if (opcode == OP_put_var_init) - goto put_var_ok; - if (JS_IsUninitialized(*var_ref->pvalue)) - JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name); - else - JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, cv->var_name); - goto exception; - } else { - sf->cur_pc = pc; - ret = JS_HasProperty(ctx, ctx->global_obj, cv->var_name); - if (ret < 0) - goto exception; - if (ret == 0 && is_strict_mode(ctx)) { - JS_ThrowReferenceErrorNotDefined(ctx, cv->var_name); - goto exception; - } - ret = JS_SetPropertyInternal(ctx, ctx->global_obj, cv->var_name, sp[-1], - ctx->global_obj, JS_PROP_THROW_STRICT); - sp--; - if (ret < 0) - goto exception; - } - } else { - put_var_ok: - set_value(ctx, var_ref->pvalue, sp[-1]); - sp--; - } - } - BREAK; - CASE(OP_get_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, 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_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, 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_DupValue(ctx, sp[-1])); - } - BREAK; - -#if SHORT_OPCODES - CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, 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_DupValue(ctx, sp[-1])); BREAK; - - CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; - CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; - CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; - CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, 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_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; - CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; - CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; - CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, 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_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; - CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; - CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; - CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *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_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; -#endif - - CASE(OP_get_var_ref): - { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - sp[0] = JS_DupValue(ctx, 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_DupValue(ctx, 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_DupValue(ctx, 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(ctx, b, idx, FALSE); - goto exception; - } - sp[0] = JS_DupValue(ctx, var_buf[idx]); - sp++; - } - BREAK; - CASE(OP_get_loc_checkthis): - { - 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_DupValue(ctx, 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(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(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; - sf->cur_pc = pc; - - 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; -#if SHORT_OPCODES - 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; -#endif - 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; - /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */ - 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; -#if SHORT_OPCODES - 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; -#endif - 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_NewInt32(ctx, 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_next): - sf->cur_pc = pc; - if (js_for_await_of_next(ctx, sp)) - goto exception; - sp++; - 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, (JSValueConst *)(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, (JSValueConst *)(sp - 1)); - } - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - ret_flag = FALSE; - } - sp[0] = JS_NewBool(ctx, 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_NewBool(ctx, !res); - } - BREAK; - -#define GET_FIELD_INLINE(name, keep, is_length) \ - { \ - JSValue val, obj; \ - JSAtom atom; \ - JSObject *p; \ - JSProperty *pr; \ - JSShapeProperty *prs; \ - \ - if (is_length) { \ - atom = JS_ATOM_length; \ - } else { \ - 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 name ## _slow_path; \ - val = JS_DupValue(ctx, 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 name ## _slow_path; \ - } \ - p = p->shape->proto; \ - if (!p) { \ - val = JS_UNDEFINED; \ - break; \ - } \ - } \ - } else { \ - name ## _slow_path: \ - sf->cur_pc = pc; \ - val = JS_GetPropertyInternal(ctx, obj, atom, sp[-1], 0); \ - if (unlikely(JS_IsException(val))) \ - goto exception; \ - } \ - if (keep) { \ - *sp++ = val; \ - } else { \ - JS_FreeValue(ctx, sp[-1]); \ - sp[-1] = val; \ - } \ - } - - - CASE(OP_get_field): - GET_FIELD_INLINE(get_field, 0, 0); - BREAK; - - CASE(OP_get_field2): - GET_FIELD_INLINE(get_field2, 1, 0); - BREAK; - -#if SHORT_OPCODES - CASE(OP_get_length): - GET_FIELD_INLINE(get_length, 0, 1); - BREAK; -#endif - - 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_SetPropertyInternal(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; - - 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; - 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; - sf->cur_pc = pc; - 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; - JSValueConst 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; - -#define GET_ARRAY_EL_INLINE(name, keep) \ - { \ - JSValue val, obj, prop; \ - JSObject *p; \ - uint32_t idx; \ - \ - obj = sp[-2]; \ - prop = sp[-1]; \ - if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && \ - JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { \ - p = JS_VALUE_GET_OBJ(obj); \ - idx = JS_VALUE_GET_INT(prop); \ - if (unlikely(p->class_id != JS_CLASS_ARRAY)) \ - goto name ## _slow_path; \ - if (unlikely(idx >= p->u.array.count)) \ - goto name ## _slow_path; \ - val = JS_DupValue(ctx, p->u.array.u.values[idx]); \ - } else { \ - name ## _slow_path: \ - sf->cur_pc = pc; \ - val = JS_GetPropertyValue(ctx, obj, prop); \ - if (unlikely(JS_IsException(val))) { \ - if (keep) \ - sp[-1] = JS_UNDEFINED; \ - else \ - sp--; \ - goto exception; \ - } \ - } \ - if (keep) { \ - sp[-1] = val; \ - } else { \ - JS_FreeValue(ctx, obj); \ - sp[-2] = val; \ - sp--; \ - } \ - } - - CASE(OP_get_array_el): - GET_ARRAY_EL_INLINE(get_array_el, 0); - BREAK; - - CASE(OP_get_array_el2): - GET_ARRAY_EL_INLINE(get_array_el2, 1); - BREAK; - - CASE(OP_get_array_el3): - { - JSValue val; - JSObject *p; - uint32_t idx; - - if (likely(JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT && - JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_INT)) { - p = JS_VALUE_GET_OBJ(sp[-2]); - idx = JS_VALUE_GET_INT(sp[-1]); - if (unlikely(p->class_id != JS_CLASS_ARRAY)) - goto get_array_el3_slow_path; - if (unlikely(idx >= p->u.array.count)) - goto get_array_el3_slow_path; - val = JS_DupValue(ctx, p->u.array.u.values[idx]); - } else { - get_array_el3_slow_path: - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - /* undefined and null are tested in JS_GetPropertyValue() */ - break; - default: - /* must be tested before JS_ToPropertyKey */ - if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { - JS_ThrowTypeError(ctx, "value has no property"); - goto exception; - } - 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; - } - sf->cur_pc = pc; - val = JS_GetPropertyValue(ctx, sp[-2], JS_DupValue(ctx, sp[-1])); - if (unlikely(JS_IsException(val))) - goto exception; - } - *sp++ = val; - } - BREAK; - - CASE(OP_get_ref_value): - { - JSValue val; - JSAtom atom; - int ret; - - sf->cur_pc = pc; - atom = JS_ValueToAtom(ctx, sp[-1]); - if (atom == JS_ATOM_NULL) - goto exception; - if (unlikely(JS_IsUndefined(sp[-2]))) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - goto exception; - } - ret = JS_HasProperty(ctx, sp[-2], atom); - if (unlikely(ret <= 0)) { - if (ret < 0) { - JS_FreeAtom(ctx, atom); - goto exception; - } - if (is_strict_mode(ctx)) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - goto exception; - } - val = JS_UNDEFINED; - } else { - val = JS_GetProperty(ctx, sp[-2], atom); - } - JS_FreeAtom(ctx, atom); - 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; - JSObject *p; - uint32_t idx; - - if (likely(JS_VALUE_GET_TAG(sp[-3]) == JS_TAG_OBJECT && - JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_INT)) { - p = JS_VALUE_GET_OBJ(sp[-3]); - idx = JS_VALUE_GET_INT(sp[-2]); - if (unlikely(p->class_id != JS_CLASS_ARRAY)) - goto put_array_el_slow_path; - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - uint32_t new_len, array_len; - 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 put_array_el_slow_path; - } - if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT)) - goto put_array_el_slow_path; - /* cannot overflow otherwise the length would not be an integer */ - new_len = idx + 1; - if (unlikely(new_len > p->u.array.u1.size)) - goto put_array_el_slow_path; - 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))) - goto put_array_el_slow_path; - p->prop[0].u.value = JS_NewInt32(ctx, new_len); - } - p->u.array.count = new_len; - p->u.array.u.values[idx] = sp[-1]; - } else { - set_value(ctx, &p->u.array.u.values[idx], sp[-1]); - } - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - } else { - put_array_el_slow_path: - 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; - JSAtom atom; - sf->cur_pc = pc; - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - if (unlikely(JS_IsUndefined(sp[-3]))) { - if (is_strict_mode(ctx)) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - goto exception; - } else { - sp[-3] = JS_DupValue(ctx, ctx->global_obj); - } - } - ret = JS_HasProperty(ctx, sp[-3], atom); - if (unlikely(ret <= 0)) { - if (unlikely(ret < 0)) { - JS_FreeAtom(ctx, atom); - goto exception; - } - if (is_strict_mode(ctx)) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - goto exception; - } - } - ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-3], JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-2]); - 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_SetPropertyInternal(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_DupValue(ctx, 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((int)r != r)) { - sp[-2] = __JS_NewFloat64(ctx, (double)r); - } else { - sp[-2] = JS_NewInt32(ctx, r); - } - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else if (JS_IsString(op1) && JS_IsString(op2)) { - sp[-2] = JS_ConcatString(ctx, op1, op2); - sp--; - if (JS_IsException(sp[-1])) - goto exception; - } else { - sf->cur_pc = pc; - if (js_add_slow(ctx, sp)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_add_loc): - { - JSValue op2; - JSValue *pv; - int idx; - idx = *pc; - pc += 1; - - op2 = sp[-1]; - pv = &var_buf[idx]; - if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) { - *pv = __JS_NewFloat64(ctx, (double)r); - } else { - *pv = JS_NewInt32(ctx, r); - } - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) { - *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) + - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING && - JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { - sp--; - sf->cur_pc = pc; - if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) { - JS_FreeValue(ctx, op2); - } else { - op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2); - if (JS_IsException(op2)) - goto exception; - set_value(ctx, pv, op2); - } - } 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_DupValue(ctx, *pv); - ops[1] = op2; - 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(ctx, (double)r); - } else { - sp[-2] = JS_NewInt32(ctx, r); - } - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - - JS_VALUE_GET_FLOAT64(op2)); - 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_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); - mul_fp_res: - sp[-2] = __JS_NewFloat64(ctx, 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_NewFloat64(ctx, (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_NewInt32(ctx, 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 if (tag == JS_TAG_NULL || tag == JS_TAG_BOOL) { - sp[-1] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); - } 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 || - tag == JS_TAG_BOOL || - tag == JS_TAG_NULL) { - 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_NewInt32(ctx, -val); - } else if (JS_TAG_IS_FLOAT64(tag)) { - d = -JS_VALUE_GET_FLOAT64(op1); - neg_fp_res: - sp[-1] = __JS_NewFloat64(ctx, 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_NewInt32(ctx, 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_NewInt32(ctx, 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_NewInt32(ctx, 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_NewInt32(ctx, 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_NewInt32(ctx, 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_DupValue(ctx, 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_NewInt32(ctx, 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_DupValue(ctx, 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_NewInt32(ctx, ~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); - v2 &= 0x1f; - sp[-2] = JS_NewInt32(ctx, 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_NewUint32(ctx, - (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); - v2 &= 0x1f; - sp[-2] = JS_NewInt32(ctx, - (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_NewInt32(ctx, - 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_NewInt32(ctx, - 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_NewInt32(ctx, - 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_NewBool(ctx, 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): - sf->cur_pc = pc; - 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_NewBool(ctx, 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; - -#if 0 - CASE(OP_to_string): - if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { - ret_val = JS_ToString(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; -#endif - 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): - { - 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: - /* in Object Environment Records, GetBindingValue() calls HasProperty() */ - ret = JS_HasProperty(ctx, obj, atom); - if (unlikely(ret <= 0)) { - if (ret < 0) - goto exception; - if (is_strict_mode(ctx)) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - goto exception; - } - val = JS_UNDEFINED; - } else { - 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: /* used e.g. in for in/of */ - /* in Object Environment Records, SetMutableBinding() calls HasProperty() */ - ret = JS_HasProperty(ctx, obj, atom); - if (unlikely(ret <= 0)) { - if (ret < 0) - goto exception; - if (is_strict_mode(ctx)) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - goto exception; - } - } - ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj, - 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_NewBool(ctx, 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 */ - /* in Object Environment Records, GetBindingValue() calls HasProperty() */ - ret = JS_HasProperty(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (!ret) { - val = JS_UNDEFINED; - } else { - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - } - *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_NewInt32(ctx, FUNC_RET_AWAIT); - goto done_generator; - CASE(OP_yield): - ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); - goto done_generator; - CASE(OP_yield_star): - CASE(OP_async_yield_star): - ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); - goto done_generator; - CASE(OP_return_async): - ret_val = JS_UNDEFINED; - goto done_generator; - CASE(OP_initial_yield): - ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD); - 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; - } -#if SHORT_OPCODES - 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]); -#endif - 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 (is_backtrace_needed(ctx, rt->current_exception)) { - /* add the backtrace information now (it is not done - before if the exception happens in a bytecode - operation */ - sf->cur_pc = pc; - build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0); - } - if (!rt->current_exception_is_uncatchable) { - 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; - 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(b->var_ref_count != 0)) { - /* variable references reference the stack: must close them */ - close_var_refs(rt, b, 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, (JSValue *)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, (JSValue *)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_DupValue(ctx, 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_DupValue(ctx, 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, JSValue *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_ThrowTypeError(ctx, "not a function"); - } - return call_func(ctx, func_obj, new_target, argc, - (JSValueConst *)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, (JSValue *)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, (JSValue *)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 JSAsyncFunctionState *async_func_init(JSContext *ctx, - JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - JSAsyncFunctionState *s; - JSObject *p; - JSFunctionBytecode *b; - JSStackFrame *sf; - int i, arg_buf_len, n; - - p = JS_VALUE_GET_OBJ(func_obj); - b = p->u.func.function_bytecode; - arg_buf_len = max_int(b->arg_count, argc); - s = js_malloc(ctx, sizeof(*s) + sizeof(JSValue) * (arg_buf_len + b->var_count + b->stack_size) + sizeof(JSVarRef *) * b->var_ref_count); - if (!s) - return NULL; - memset(s, 0, sizeof(*s)); - s->header.ref_count = 1; - add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); - - sf = &s->frame; - sf->js_mode = b->js_mode | JS_MODE_ASYNC; - sf->cur_pc = b->byte_code_buf; - sf->arg_buf = (JSValue *)(s + 1); - sf->cur_func = JS_DupValue(ctx, func_obj); - s->this_val = JS_DupValue(ctx, 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); - 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_DupValue(ctx, argv[i]); - n = arg_buf_len + b->var_count; - for(i = argc; i < n; i++) - sf->arg_buf[i] = JS_UNDEFINED; - s->resolving_funcs[0] = JS_UNDEFINED; - s->resolving_funcs[1] = JS_UNDEFINED; - s->is_completed = FALSE; - return s; -} - -static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s) -{ - JSStackFrame *sf = &s->frame; - JSValue *sp; - - /* 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_FreeValueRT(rt, sf->cur_func); - JS_FreeValueRT(rt, s->this_val); -} - -static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) -{ - JSRuntime *rt = ctx->rt; - JSStackFrame *sf = &s->frame; - JSValue func_obj, ret; - - assert(!s->is_completed); - if (js_check_stack_overflow(ctx->rt, 0)) { - ret = JS_ThrowStackOverflow(ctx); - } else { - /* the tag does not matter provided it is not an object */ - func_obj = JS_MKPTR(JS_TAG_INT, s); - ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, - s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR); - } - if (JS_IsException(ret) || JS_IsUndefined(ret)) { - JSObject *p; - JSFunctionBytecode *b; - - p = JS_VALUE_GET_OBJ(sf->cur_func); - b = p->u.func.function_bytecode; - - if (JS_IsUndefined(ret)) { - ret = sf->cur_sp[-1]; - sf->cur_sp[-1] = JS_UNDEFINED; - } - /* end of execution */ - s->is_completed = TRUE; - - /* close the closure variables. */ - close_var_refs(rt, b, sf); - - async_func_free_frame(rt, s); - } - return ret; -} - -static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) -{ - /* cannot close the closure variables here because it would - potentially modify the object graph */ - if (!s->is_completed) { - async_func_free_frame(rt, s); - } - - JS_FreeValueRT(rt, s->resolving_funcs[0]); - JS_FreeValueRT(rt, s->resolving_funcs[1]); - - remove_gc_object(&s->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) { - list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list); - } else { - js_free_rt(rt, s); - } -} - -static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) -{ - if (--s->header.ref_count == 0) { - if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { - list_del(&s->header.link); - list_add(&s->header.link, &rt->gc_zero_ref_count_list); - if (rt->gc_phase == JS_GC_PHASE_NONE) { - free_zero_refcount(rt); - } - } - } -} - -/* 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; - if (s->func_state) { - async_func_free(rt, s->func_state); - s->func_state = NULL; - } - s->state = JS_GENERATOR_STATE_COMPLETED; -} - -static void js_generator_finalizer(JSRuntime *rt, JSValue 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->func_state) - return; - mark_func(rt, &s->func_state->header); -} - -/* 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, - BOOL *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"); - switch(s->state) { - default: - case JS_GENERATOR_STATE_SUSPENDED_START: - sf = &s->func_state->frame; - 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: - sf = &s->func_state->frame; - /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ - ret = JS_DupValue(ctx, 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_NewInt32(ctx, 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 (s->func_state->is_completed) { - /* finalize the execution in case of exception or normal return */ - free_generator_stack(ctx, s); - return func_ret; - } else { - assert(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; - } - } - 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_DupValue(ctx, argv[0]); - break; - case GEN_MAGIC_THROW: - ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); - break; - } - break; - case JS_GENERATOR_STATE_EXECUTING: - ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); - break; - } - return ret; -} - -static JSValue js_generator_function_call(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; - s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); - if (!s->func_state) { - 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_SetOpaque(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_resolve_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionState *s = p->u.async_function_data; - if (s) { - async_func_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); - JSAsyncFunctionState *s = p->u.async_function_data; - if (s) { - mark_func(rt, &s->header); - } -} - -static int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionState *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 void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s) -{ - JSValue func_ret, ret2; - - func_ret = async_func_resume(ctx, s); - if (s->is_completed) { - if (JS_IsException(func_ret)) { - JSValue error; - fail: - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - } else { - /* normal return */ - ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&func_ret); - JS_FreeValue(ctx, func_ret); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - } - } else { - JSValue value, promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; - - value = s->frame.cur_sp[-1]; - s->frame.cur_sp[-1] = JS_UNDEFINED; - - /* await */ - JS_FreeValue(ctx, func_ret); /* not used */ - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&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, - (JSValueConst *)resolving_funcs, - (JSValueConst *)resolving_funcs1); - JS_FreeValue(ctx, promise); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; - } -} - -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); - JSAsyncFunctionState *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->throw_flag = is_reject; - if (is_reject) { - JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - /* return value of await */ - s->frame.cur_sp[-1] = JS_DupValue(ctx, arg); - } - js_async_function_resume(ctx, s); - 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; - JSAsyncFunctionState *s; - - s = async_func_init(ctx, func_obj, this_obj, argc, argv); - if (!s) - return JS_EXCEPTION; - - promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); - if (JS_IsException(promise)) { - async_func_free(ctx->rt, s); - return JS_EXCEPTION; - } - - js_async_function_resume(ctx, s); - - async_func_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; - /* func_state is NULL is state AWAITING_RETURN and COMPLETED */ - 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->func_state) - async_func_free(rt, s->func_state); - js_free_rt(rt, s); -} - -static void js_async_generator_finalizer(JSRuntime *rt, JSValue 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->func_state) { - mark_func(rt, &s->func_state->header); - } - } -} - -static JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int magic, JSValue *func_data); - -static int js_async_generator_resolve_function_create(JSContext *ctx, - JSValueConst 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, &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, - JSValueConst value) -{ - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; - - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &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, - (JSValueConst *)resolving_funcs, - (JSValueConst *)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_DupValue(ctx, 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); - s->func_state = NULL; - } -} - -static int js_async_generator_completed_return(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst 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, &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, (JSValueConst *)&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, - (JSValueConst *)resolving_funcs1, - (JSValueConst *)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_DupValue(ctx, 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_NewInt32(ctx, 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 (s->func_state->is_completed) { - 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 { - /* end of function */ - js_async_generator_complete(ctx, s); - js_async_generator_resolve(ctx, s, func_ret, TRUE); - JS_FreeValue(ctx, func_ret); - } - } else { - int func_ret_code, ret; - assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); - func_ret_code = JS_VALUE_GET_INT(func_ret); - value = s->func_state->frame.cur_sp[-1]; - s->func_state->frame.cur_sp[-1] = JS_UNDEFINED; - 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(); - } - } - break; - default: - abort(); - } - } - done: ; -} - -static JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int magic, JSValue *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_DupValue(ctx, arg)); - } else { - /* return value of await */ - s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, 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, (JSValueConst *)&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_DupValue(ctx, argv[0]); - req->promise = JS_DupValue(ctx, 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); - s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); - if (!s->func_state) - 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_SetOpaque(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 LineNumberSlot { - uint32_t pc; - uint32_t source_pos; -} LineNumberSlot; - -typedef struct { - /* last source position */ - const uint8_t *ptr; - int line_num; - int col_num; - const uint8_t *buf_start; -} GetLineColCache; - -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 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 struct JSVarDef { - JSAtom var_name; - /* index into fd->scopes of this variable lexical scope */ - int scope_level; - /* - 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 - */ - int scope_next; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */ - 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; - /* 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; -} JSVarDef; - -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; - - BOOL is_eval; /* TRUE if eval code */ - int eval_type; /* only valid if is_eval = TRUE */ - BOOL is_global_var; /* TRUE if variables are not defined locally: - eval global, eval module or non strict eval */ - BOOL is_func_expr; /* TRUE if function expression */ - BOOL has_home_object; /* TRUE if the home object is available */ - BOOL has_prototype; /* true if a prototype field is necessary */ - BOOL has_simple_parameter_list; - BOOL has_parameter_expressions; /* if true, an argument scope is created */ - BOOL has_use_strict; /* to reject directive in special cases */ - BOOL has_eval_call; /* true if the function contains a call to eval() */ - BOOL has_arguments_binding; /* true if the 'arguments' binding is - available in the function */ - BOOL has_this_binding; /* true if the 'this' and new.target binding are - available in the function */ - BOOL new_target_allowed; /* true if the 'new.target' does not - throw a syntax error */ - BOOL super_call_allowed; /* true if super() is allowed */ - BOOL super_allowed; /* true if super. or super[] is allowed */ - BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ - BOOL is_derived_class_constructor; - BOOL in_function_body; - JSFunctionKindEnum func_kind : 8; - JSParseFunctionEnum func_type : 8; - uint8_t js_mode; /* bitmap of JS_MODE_x */ - JSAtom func_name; /* JS_ATOM_NULL if no name */ - - JSVarDef *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; - BOOL need_home_object; - - 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 */ - const uint8_t *last_opcode_source_ptr; - BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ - - 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; - - LineNumberSlot *line_number_slots; - int line_number_size; - int line_number_count; - int line_number_last; - int line_number_last_pc; - - /* pc2line table */ - BOOL strip_debug : 1; /* strip all debug info (implies strip_source = TRUE) */ - BOOL strip_source : 1; /* strip only source code */ - JSAtom filename; - uint32_t source_pos; /* pointer in the eval() source */ - GetLineColCache *get_line_col_cache; /* XXX: could remove to save memory */ - DynBuf pc2line; - - char *source; /* raw source, utf-8 encoded */ - int source_len; - - JSModuleDef *module; /* != NULL when parsing a module */ - BOOL has_await; /* TRUE if await is used (used in module eval) */ -} JSFunctionDef; - -typedef struct JSToken { - int val; - const uint8_t *ptr; /* position in the source */ - 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; - 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; - - /* current function code */ - JSFunctionDef *cur_func; - BOOL is_module; /* parsing a module */ - BOOL allow_html_comments; - BOOL ext_json; /* true if accepting JSON superset */ - GetLineColCache get_line_col_cache; -} JSParseState; - -typedef struct JSOpCode { -#ifdef 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 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 -#include "quickjs-opcode.h" -#undef DEF -#undef FMT -}; - -#if SHORT_OPCODES -/* 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)] -#else -#define short_opcode_info(op) opcode_info[op] -#endif - -static __exception int next_token(JSParseState *s); - -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) -{ - 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; - } -} - -/* return the zero based line and column number in the source. */ -/* Note: we no longer support '\r' as line terminator */ -static int get_line_col(int *pcol_num, const uint8_t *buf, size_t len) -{ - int line_num, col_num, c; - size_t i; - - line_num = 0; - col_num = 0; - for(i = 0; i < len; i++) { - c = buf[i]; - if (c == '\n') { - line_num++; - col_num = 0; - } else if (c < 0x80 || c >= 0xc0) { - col_num++; - } - } - *pcol_num = col_num; - return line_num; -} - -static int get_line_col_cached(GetLineColCache *s, int *pcol_num, const uint8_t *ptr) -{ - int line_num, col_num; - if (ptr >= s->ptr) { - line_num = get_line_col(&col_num, s->ptr, ptr - s->ptr); - if (line_num == 0) { - s->col_num += col_num; - } else { - s->line_num += line_num; - s->col_num = col_num; - } - } else { - line_num = get_line_col(&col_num, ptr, s->ptr - ptr); - if (line_num == 0) { - s->col_num -= col_num; - } else { - const uint8_t *p; - s->line_num -= line_num; - /* find the absolute column position */ - col_num = 0; - for(p = ptr - 1; p >= s->buf_start; p--) { - if (*p == '\n') { - break; - } else if (*p < 0x80 || *p >= 0xc0) { - col_num++; - } - } - s->col_num = col_num; - } - } - s->ptr = ptr; - *pcol_num = s->col_num; - return s->line_num; -} - -/* 'ptr' is the position of the error in the source */ -static int js_parse_error_v(JSParseState *s, const uint8_t *ptr, const char *fmt, va_list ap) -{ - JSContext *ctx = s->ctx; - int line_num, col_num; - line_num = get_line_col(&col_num, s->buf_start, ptr - s->buf_start); - JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); - build_backtrace(ctx, ctx->rt->current_exception, s->filename, - line_num + 1, col_num + 1, 0); - return -1; -} - -static __attribute__((format(printf, 3, 4))) int js_parse_error_pos(JSParseState *s, const uint8_t *ptr, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = js_parse_error_v(s, ptr, fmt, ap); - va_end(ap); - return ret; -} - -static __attribute__((format(printf, 2, 3))) int js_parse_error(JSParseState *s, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = js_parse_error_v(s, s->token.ptr, fmt, ap); - va_end(ap); - return ret; -} - -static int js_parse_expect(JSParseState *s, int tok) -{ - if (s->token.val != tok) { - /* XXX: dump token correctly in all cases */ - return js_parse_error(s, "expecting '%c'", tok); - } - return next_token(s); -} - -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) -{ - 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 >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - js_parse_error_pos(s, p - 1, "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) -{ - int ret; - uint32_t c; - StringBuffer b_s, *b = &b_s; - const uint8_t *p_escape; - 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 == '\\') { - p_escape = p - 1; - c = *p; - /* XXX: need a specific JSON case to avoid - accepting invalid escapes */ - switch(c) { - case '\0': - if (p >= s->buf_end) - goto invalid_char; - 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++; - continue; - default: - if (c >= '0' && c <= '9') { - if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') - goto parse_escape; - if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { - p++; - c = '\0'; - } else { - if (c >= '8' || sep == '`') { - /* Note: according to ES2021, \8 and \9 are not - accepted in strict mode or in templates. */ - goto invalid_escape; - } else { - if (do_throw) - js_parse_error_pos(s, p_escape, "octal escape sequences are not allowed in strict mode"); - } - goto fail; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - /* LS or PS are skipped */ - if (c == CP_LS || c == CP_PS) - continue; - } else { - parse_escape: - ret = lre_parse_escape(&p, TRUE); - if (ret == -1) { - invalid_escape: - if (do_throw) - js_parse_error_pos(s, p_escape, "malformed escape sequence in string literal"); - goto fail; - } else if (ret < 0) { - /* ignore the '\' (could output a warning) */ - p++; - } else { - c = ret; - } - } - break; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) - 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; - 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) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - if (c == CP_LS || c == CP_PS) - goto eol_error; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - invalid_utf8: - js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence"); - goto fail; - } - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - eol_error: - js_parse_error_pos(s, p - 1, "unexpected line terminator in regexp"); - goto fail; - } - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - - /* flags */ - for(;;) { - const uint8_t *p_next = p; - c = *p_next++; - if (c >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - p++; - goto invalid_utf8; - } - } - 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; -} - -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; -} - -/* 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->js_mode & JS_MODE_STRICT)) || - (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, *p1; - char ident_buf[128], *buf; - size_t ident_size, ident_pos; - JSAtom atom; - - p = *pp; - buf = ident_buf; - ident_size = sizeof(ident_buf); - ident_pos = 0; - if (is_private) - buf[ident_pos++] = '#'; - for(;;) { - p1 = p; - - if (c < 128) { - buf[ident_pos++] = c; - } else { - ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c); - } - c = *p1++; - if (c == '\\' && *p1 == 'u') { - c = lre_parse_escape(&p1, TRUE); - *pident_has_escape = TRUE; - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - } - if (!lre_js_is_ident_next(c)) - break; - p = p1; - if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { - if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { - atom = JS_ATOM_NULL; - goto done; - } - } - } - 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; - int c; - BOOL ident_has_escape; - JSAtom atom; - - if (js_check_stack_overflow(s->ctx->rt, 0)) { - return js_parse_error(s, "stack overflow"); - } - - free_token(s, &s->token); - - p = s->last_ptr = s->buf_ptr; - s->got_lf = FALSE; - redo: - 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->got_lf = TRUE; - goto redo; - case '\f': - case '\v': - case ' ': - case '\t': - 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' || *p == '\r') { - s->got_lf = TRUE; /* considered as LF for ASI */ - p++; - } else if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == CP_LS || c == CP_PS) { - s->got_lf = TRUE; /* considered as LF for ASI */ - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - 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 = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - break; - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - 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 */ - 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 */ - { - const uint8_t *p1; - p++; - p1 = p; - c = *p1++; - if (c == '\\' && *p1 == 'u') { - c = lre_parse_escape(&p1, TRUE); - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - } - if (!lre_js_is_ident_first(c)) { - js_parse_error(s, "invalid first character of private name"); - goto fail; - } - p = p1; - 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->js_mode & JS_MODE_STRICT)) { - js_parse_error(s, "octal literals are deprecated 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(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &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 ` 0;) { - } - return n; -} - -function empty_do_loop(n) { - var j = n; - do { } while (--j > 0); - return n; -} - -function date_now(n) { - var j; - for(j = 0; j < n; j++) { - Date.now(); - } - return n; -} - -function date_parse(n) { - var x0 = 0, dx = 0; - var j; - for(j = 0; j < n; j++) { - var x1 = x0 - x0 % 1000; - var x2 = -x0; - var x3 = -x1; - var d0 = new Date(x0); - var d1 = new Date(x1); - var d2 = new Date(x2); - var d3 = new Date(x3); - if (Date.parse(d0.toISOString()) != x0 - || Date.parse(d1.toGMTString()) != x1 - || Date.parse(d1.toString()) != x1 - || Date.parse(d2.toISOString()) != x2 - || Date.parse(d3.toGMTString()) != x3 - || Date.parse(d3.toString()) != x3) { - console.log("Date.parse error for " + x0); - return -1; - } - dx = (dx * 1.1 + 1) >> 0; - x0 = (x0 + dx) % 8.64e15; - } - return n * 6; -} - -function prop_read(n) -{ - var obj, sum, j; - obj = {a: 1, b: 2, c:3, d:4 }; - sum = 0; - for(j = 0; j < n; j++) { - sum += obj.a; - sum += obj.b; - sum += obj.c; - sum += obj.d; - } - global_res = sum; - return n * 4; -} - -function prop_write(n) -{ - var obj, j; - obj = {a: 1, b: 2, c:3, d:4 }; - for(j = 0; j < n; j++) { - obj.a = j; - obj.b = j; - obj.c = j; - obj.d = j; - } - return n * 4; -} - -function prop_update(n) -{ - var obj, j; - obj = {a: 1, b: 2, c:3, d:4 }; - for(j = 0; j < n; j++) { - obj.a += j; - obj.b += j; - obj.c += j; - obj.d += j; - } - return n * 4; -} - -function prop_create(n) -{ - var obj, i, j; - for(j = 0; j < n; j++) { - obj = {}; - obj.a = 1; - obj.b = 2; - obj.c = 3; - obj.d = 4; - obj.e = 5; - obj.f = 6; - obj.g = 7; - obj.h = 8; - obj.i = 9; - obj.j = 10; - for(i = 0; i < 10; i++) { - obj[i] = i; - } - } - return n * 20; -} - -function prop_clone(n) -{ - var ref, obj, j, k; - ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 }; - for(k = 0; k < 10; k++) { - ref[k] = k; - } - for (j = 0; j < n; j++) { - global_res = { ...ref }; - } - return n * 20; -} - -function prop_delete(n) -{ - var ref, obj, j, k; - ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 }; - for(k = 0; k < 10; k++) { - ref[k] = k; - } - for (j = 0; j < n; j++) { - obj = { ...ref }; - delete obj.a; - delete obj.b; - delete obj.c; - delete obj.d; - delete obj.e; - delete obj.f; - delete obj.g; - delete obj.h; - delete obj.i; - delete obj.j; - for(k = 0; k < 10; k++) { - delete obj[k]; - } - } - return n * 20; -} - -function array_read(n) -{ - var tab, len, sum, i, j; - tab = []; - len = 10; - for(i = 0; i < len; i++) - tab[i] = i; - sum = 0; - for(j = 0; j < n; j++) { - sum += tab[0]; - sum += tab[1]; - sum += tab[2]; - sum += tab[3]; - sum += tab[4]; - sum += tab[5]; - sum += tab[6]; - sum += tab[7]; - sum += tab[8]; - sum += tab[9]; - } - global_res = sum; - return len * n; -} - -function array_write(n) -{ - var tab, len, i, j; - tab = []; - len = 10; - for(i = 0; i < len; i++) - tab[i] = i; - for(j = 0; j < n; j++) { - tab[0] = j; - tab[1] = j; - tab[2] = j; - tab[3] = j; - tab[4] = j; - tab[5] = j; - tab[6] = j; - tab[7] = j; - tab[8] = j; - tab[9] = j; - } - return len * n; -} - -function array_update(n) -{ - var tab, len, i, j; - tab = []; - len = 10; - for(i = 0; i < len; i++) - tab[i] = i; - for(j = 0; j < n; j++) { - tab[0] += j; - tab[1] += j; - tab[2] += j; - tab[3] += j; - tab[4] += j; - tab[5] += j; - tab[6] += j; - tab[7] += j; - tab[8] += j; - tab[9] += j; - } - return len * n; -} - -function array_prop_create(n) -{ - var tab, i, j, len; - len = 1000; - for(j = 0; j < n; j++) { - tab = []; - for(i = 0; i < len; i++) - tab[i] = i; - } - return len * n; -} - -function array_slice(n) -{ - var ref, a, i, j, len; - len = 1000; - ref = []; - for(i = 0; i < len; i++) - ref[i] = i; - for(j = 0; j < n; j++) { - ref[0] = j; - a = ref.slice(); - a[0] = 0; - global_res = a; - } - return len * n; -} - -function array_length_read(n) -{ - var tab, sum, j; - tab = [1, 2, 3]; - sum = 0; - for(j = 0; j < n; j++) { - sum += tab.length; - sum += tab.length; - sum += tab.length; - sum += tab.length; - } - global_res = sum; - return n * 4; -} - -function array_length_decr(n) -{ - var tab, ref, i, j, len; - len = 1000; - ref = []; - for(i = 0; i < len; i++) - ref[i] = i; - for(j = 0; j < n; j++) { - tab = ref.slice(); - for(i = len; i --> 0;) - tab.length = i; - } - return len * n; -} - -function array_hole_length_decr(n) -{ - var tab, ref, i, j, len; - len = 1000; - ref = []; - for(i = 0; i < len; i++) { - if (i % 10 == 9) - ref[i] = i; - } - for(j = 0; j < n; j++) { - tab = ref.slice(); - for(i = len; i --> 0;) - tab.length = i; - } - return len * n; -} - -function array_push(n) -{ - var tab, i, j, len; - len = 500; - for(j = 0; j < n; j++) { - tab = []; - for(i = 0; i < len; i++) - tab.push(i); - } - return len * n; -} - -function array_pop(n) -{ - var tab, ref, i, j, len, sum; - len = 500; - ref = []; - for(i = 0; i < len; i++) - ref[i] = i; - for(j = 0; j < n; j++) { - tab = ref.slice(); - sum = 0; - for(i = 0; i < len; i++) - sum += tab.pop(); - global_res = sum; - } - return len * n; -} - -function typed_array_read(n) -{ - var tab, len, sum, i, j; - len = 10; - tab = new Int32Array(len); - for(i = 0; i < len; i++) - tab[i] = i; - sum = 0; - for(j = 0; j < n; j++) { - sum += tab[0]; - sum += tab[1]; - sum += tab[2]; - sum += tab[3]; - sum += tab[4]; - sum += tab[5]; - sum += tab[6]; - sum += tab[7]; - sum += tab[8]; - sum += tab[9]; - } - global_res = sum; - return len * n; -} - -function typed_array_write(n) -{ - var tab, len, i, j; - len = 10; - tab = new Int32Array(len); - for(i = 0; i < len; i++) - tab[i] = i; - for(j = 0; j < n; j++) { - tab[0] = j; - tab[1] = j; - tab[2] = j; - tab[3] = j; - tab[4] = j; - tab[5] = j; - tab[6] = j; - tab[7] = j; - tab[8] = j; - tab[9] = j; - } - return len * n; -} - -function arguments_test() -{ - return arguments[0] + arguments[1] + arguments[2]; -} - -function arguments_read(n) -{ - sum = 0; - for(j = 0; j < n; j++) { - sum += arguments_test(j, j, j); - sum += arguments_test(j, j, j); - sum += arguments_test(j, j, j); - sum += arguments_test(j, j, j); - } - global_res = sum; - return n * 4; -} - -function arguments_strict_test() -{ - "use strict"; - return arguments[0] + arguments[1] + arguments[2]; -} - -function arguments_strict_read(n) -{ - sum = 0; - for(j = 0; j < n; j++) { - sum += arguments_strict_test(j, j, j); - sum += arguments_strict_test(j, j, j); - sum += arguments_strict_test(j, j, j); - sum += arguments_strict_test(j, j, j); - } - global_res = sum; - return n * 4; -} - -var global_var0; - -function global_read(n) -{ - var sum, j; - global_var0 = 0; - sum = 0; - for(j = 0; j < n; j++) { - sum += global_var0; - sum += global_var0; - sum += global_var0; - sum += global_var0; - } - global_res = sum; - return n * 4; -} - -function global_write(n) -{ - var j; - for(j = 0; j < n; j++) { - global_var0 = j; - global_var0 = j; - global_var0 = j; - global_var0 = j; - } - return n * 4; -} - -function global_write_strict(n) -{ - "use strict"; - var j; - for(j = 0; j < n; j++) { - global_var0 = j; - global_var0 = j; - global_var0 = j; - global_var0 = j; - } - return n * 4; -} - -function local_destruct(n) -{ - var j, v1, v2, v3, v4; - var array = [ 1, 2, 3, 4, 5]; - var o = { a:1, b:2, c:3, d:4 }; - var a, b, c, d; - for(j = 0; j < n; j++) { - [ v1, v2,, v3, ...v4] = array; - ({ a, b, c, d } = o); - ({ a: a, b: b, c: c, d: d } = o); - } - return n * 12; -} - -var global_v1, global_v2, global_v3, global_v4; -var global_a, global_b, global_c, global_d; - -function global_destruct(n) -{ - var j, v1, v2, v3, v4; - var array = [ 1, 2, 3, 4, 5 ]; - var o = { a:1, b:2, c:3, d:4 }; - var a, b, c, d; - for(j = 0; j < n; j++) { - [ global_v1, global_v2,, global_v3, ...global_v4] = array; - ({ a: global_a, b: global_b, c: global_c, d: global_d } = o); - } - return n * 8; -} - -function global_destruct_strict(n) -{ - "use strict"; - var j, v1, v2, v3, v4; - var array = [ 1, 2, 3, 4, 5 ]; - var o = { a:1, b:2, c:3, d:4 }; - var a, b, c, d; - for(j = 0; j < n; j++) { - [ global_v1, global_v2,, global_v3, ...global_v4] = array; - ({ a: global_a, b: global_b, c: global_c, d: global_d } = o); - } - return n * 8; -} - -function g(a) -{ - return 1; -} - -function global_func_call(n) -{ - var j, sum; - sum = 0; - for(j = 0; j < n; j++) { - sum += g(j); - sum += g(j); - sum += g(j); - sum += g(j); - } - global_res = sum; - return n * 4; -} - -function func_call(n) -{ - function f(a) - { - return 1; - } - - var j, sum; - sum = 0; - for(j = 0; j < n; j++) { - sum += f(j); - sum += f(j); - sum += f(j); - sum += f(j); - } - global_res = sum; - return n * 4; -} - -function func_closure_call(n) -{ - function f(a) - { - sum++; - } - - var j, sum; - sum = 0; - for(j = 0; j < n; j++) { - f(j); - f(j); - f(j); - f(j); - } - global_res = sum; - return n * 4; -} - -function int_arith(n) -{ - var i, j, sum; - global_res = 0; - for(j = 0; j < n; j++) { - sum = 0; - for(i = 0; i < 1000; i++) { - sum += i * i; - } - global_res += sum; - } - return n * 1000; -} - -function float_arith(n) -{ - var i, j, sum, a, incr, a0; - global_res = 0; - a0 = 0.1; - incr = 1.1; - for(j = 0; j < n; j++) { - sum = 0; - a = a0; - for(i = 0; i < 1000; i++) { - sum += a * a; - a += incr; - } - global_res += sum; - } - return n * 1000; -} - -function bigint_arith(n, bits) -{ - var i, j, sum, a, incr, a0, sum0; - sum0 = global_res = BigInt(0); - a0 = BigInt(1) << BigInt(Math.floor((bits - 10) * 0.5)); - incr = BigInt(1); - for(j = 0; j < n; j++) { - sum = sum0; - a = a0; - for(i = 0; i < 1000; i++) { - sum += a * a; - a += incr; - } - global_res += sum; - } - return n * 1000; -} - -function bigint32_arith(n) -{ - return bigint_arith(n, 32); -} - -function bigint64_arith(n) -{ - return bigint_arith(n, 64); -} - -function bigint256_arith(n) -{ - return bigint_arith(n, 256); -} - -function map_set_string(n) -{ - var s, i, j, len = 1000; - for(j = 0; j < n; j++) { - s = new Map(); - for(i = 0; i < len; i++) { - s.set(String(i), i); - } - for(i = 0; i < len; i++) { - if (!s.has(String(i))) - throw Error("bug in Map"); - } - } - return n * len; -} - -function map_set_int(n) -{ - var s, i, j, len = 1000; - for(j = 0; j < n; j++) { - s = new Map(); - for(i = 0; i < len; i++) { - s.set(i, i); - } - for(i = 0; i < len; i++) { - if (!s.has(i)) - throw Error("bug in Map"); - } - } - return n * len; -} - -function map_set_bigint(n) -{ - var s, i, j, len = 1000; - for(j = 0; j < n; j++) { - s = new Map(); - for(i = 0; i < len; i++) { - s.set(BigInt(i), i); - } - for(i = 0; i < len; i++) { - if (!s.has(BigInt(i))) - throw Error("bug in Map"); - } - } - return n * len; -} - -function map_delete(n) -{ - var a, i, j; - - len = 1000; - for(j = 0; j < n; j++) { - a = new Map(); - for(i = 0; i < len; i++) { - a.set(String(i), i); - } - for(i = 0; i < len; i++) { - a.delete(String(i)); - } - } - return len * n; -} - -function weak_map_set(n) -{ - var a, i, j, tab; - - len = 1000; - tab = []; - for(i = 0; i < len; i++) { - tab.push({ key: i }); - } - for(j = 0; j < n; j++) { - a = new WeakMap(); - for(i = 0; i < len; i++) { - a.set(tab[i], i); - } - } - return len * n; -} - -function weak_map_delete(n) -{ - var a, i, j, tab; - - len = 1000; - for(j = 0; j < n; j++) { - tab = []; - for(i = 0; i < len; i++) { - tab.push({ key: i }); - } - a = new WeakMap(); - for(i = 0; i < len; i++) { - a.set(tab[i], i); - } - for(i = 0; i < len; i++) { - tab[i] = null; - } - } - return len * n; -} - - -function array_for(n) -{ - var r, i, j, sum, len = 100; - r = []; - for(i = 0; i < len; i++) - r[i] = i; - for(j = 0; j < n; j++) { - sum = 0; - for(i = 0; i < len; i++) { - sum += r[i]; - } - global_res = sum; - } - return n * len; -} - -function array_for_in(n) -{ - var r, i, j, sum, len = 100; - r = []; - for(i = 0; i < len; i++) - r[i] = i; - for(j = 0; j < n; j++) { - sum = 0; - for(i in r) { - sum += r[i]; - } - global_res = sum; - } - return n * len; -} - -function array_for_of(n) -{ - var r, i, j, sum, len = 100; - r = []; - for(i = 0; i < len; i++) - r[i] = i; - for(j = 0; j < n; j++) { - sum = 0; - for(i of r) { - sum += i; - } - global_res = sum; - } - return n * len; -} - -function math_min(n) -{ - var i, j, r; - r = 0; - for(j = 0; j < n; j++) { - for(i = 0; i < 1000; i++) - r = Math.min(i, 500); - global_res = r; - } - return n * 1000; -} - -function regexp_ascii(n) -{ - var i, j, r, s; - s = "the quick brown fox jumped over the lazy dog" - for(j = 0; j < n; j++) { - for(i = 0; i < 1000; i++) - r = /the quick brown fox/.exec(s) - global_res = r; - } - return n * 1000; -} - -function regexp_utf16(n) -{ - var i, j, r, s; - s = "the quick brown ᶠᵒˣ jumped over the lazy ᵈᵒᵍ" - for(j = 0; j < n; j++) { - for(i = 0; i < 1000; i++) - r = /the quick brown ᶠᵒˣ/.exec(s) - global_res = r; - } - return n * 1000; -} - -function regexp_replace(n) -{ - var i, j, r, s; - s = "the quick abc brown fox jumped abc over the lazy dog" - for(j = 0; j < n; j++) { - for(i = 0; i < 1000; i++) - r = s.replace(/abc /g, "-"); - global_res = r; - } - return n * 1000; -} - -function string_length(n) -{ - var str, sum, j; - str = "abcde"; - sum = 0; - for(j = 0; j < n; j++) { - sum += str.length; - sum += str.length; - sum += str.length; - sum += str.length; - } - global_res = sum; - return n * 4; -} - -/* incremental string contruction as local var */ -function string_build1(n) -{ - var i, j, r; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < 1000; i++) - r += "x"; - global_res = r; - } - return n * 1000; -} - -/* incremental string contruction using + */ -function string_build1x(n) -{ - var i, j, r; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < 1000; i++) - r = r + "x"; - global_res = r; - } - return n * 1000; -} - -/* incremental string contruction using +2c */ -function string_build2c(n) -{ - var i, j; - for(j = 0; j < n; j++) { - var r = ""; - for(i = 0; i < 1000; i++) - r += "xy"; - global_res = r; - } - return n * 1000; -} - -/* incremental string contruction as arg */ -function string_build2(n, r) -{ - var i, j; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < 1000; i++) - r += "x"; - global_res = r; - } - return n * 1000; -} - -/* incremental string contruction by prepending */ -function string_build3(n) -{ - var i, j, r; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < 1000; i++) - r = "x" + r; - global_res = r; - } - return n * 1000; -} - -/* incremental string contruction with multiple reference */ -function string_build4(n) -{ - var i, j, r, s; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < 1000; i++) { - s = r; - r += "x"; - } - global_res = r; - } - return n * 1000; -} - -/* append */ -function string_build_large1(n) -{ - var i, j, r, len = 20000; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < len; i++) - r += "abcdef"; - global_res = r; - } - return n * len; -} - -/* prepend */ -function string_build_large2(n) -{ - var i, j, r, len = 20000; - for(j = 0; j < n; j++) { - r = ""; - for(i = 0; i < len; i++) - r = "abcdef" + r; - global_res = r; - } - return n * len; -} - -/* sort bench */ - -function sort_bench(text) { - function random(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[(Math.random() * n) >> 0]; - } - function random8(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[(Math.random() * 256) >> 0]; - } - function random1(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[(Math.random() * 2) >> 0]; - } - function hill(arr, n, def) { - var mid = n >> 1; - for (var i = 0; i < mid; i++) - arr[i] = def[i]; - for (var i = mid; i < n; i++) - arr[i] = def[n - i]; - } - function comb(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[(i & 1) * i]; - } - function crisscross(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[(i & 1) ? n - i : i]; - } - function zero(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[0]; - } - function increasing(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[i]; - } - function decreasing(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[n - 1 - i]; - } - function alternate(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[i ^ 1]; - } - function jigsaw(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[i % (n >> 4)]; - } - function incbutone(arr, n, def) { - for (var i = 0; i < n; i++) - arr[i] = def[i]; - if (n > 0) - arr[n >> 2] = def[n]; - } - function incbutfirst(arr, n, def) { - if (n > 0) - arr[0] = def[n]; - for (var i = 1; i < n; i++) - arr[i] = def[i]; - } - function incbutlast(arr, n, def) { - for (var i = 0; i < n - 1; i++) - arr[i] = def[i + 1]; - if (n > 0) - arr[n - 1] = def[0]; - } - - var sort_cases = [ random, random8, random1, jigsaw, hill, comb, - crisscross, zero, increasing, decreasing, alternate, - incbutone, incbutlast, incbutfirst ]; - - var n = sort_bench.array_size || 10000; - var array_type = sort_bench.array_type || Array; - var def, arr; - var i, j, x, y; - var total = 0; - - var save_total_score = total_score; - var save_total_scale = total_scale; - - // initialize default sorted array (n + 1 elements) - def = new array_type(n + 1); - if (array_type == Array) { - for (i = 0; i <= n; i++) { - def[i] = i + ""; - } - } else { - for (i = 0; i <= n; i++) { - def[i] = i; - } - } - def.sort(); - for (var f of sort_cases) { - var ti = 0, tx = 0; - for (j = 0; j < 100; j++) { - arr = new array_type(n); - f(arr, n, def); - var t1 = get_clock(); - arr.sort(); - t1 = get_clock() - t1; - tx += t1; - if (!ti || ti > t1) - ti = t1; - if (tx >= clocks_per_sec) - break; - } - total += ti; - - i = 0; - x = arr[0]; - if (x !== void 0) { - for (i = 1; i < n; i++) { - y = arr[i]; - if (y === void 0) - break; - if (x > y) - break; - x = y; - } - } - while (i < n && arr[i] === void 0) - i++; - if (i < n) { - console.log("sort_bench: out of order error for " + f.name + - " at offset " + (i - 1) + - ": " + arr[i - 1] + " > " + arr[i]); - } - if (sort_bench.verbose) - log_one("sort_" + f.name, 1, ti / 100); - } - total_score = save_total_score; - total_scale = save_total_scale; - return total / n / 100; -} -sort_bench.bench = true; -sort_bench.verbose = false; - -function int_to_string(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10) + ''; - s = (j % 100) + ''; - s = (j) + ''; - } - return n * 3; -} - -function int_toString(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10).toString(); - s = (j % 100).toString(); - s = (j).toString(); - } - return n * 3; -} - -function float_to_string(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10 + 0.1) + ''; - s = (j + 0.1) + ''; - s = (j * 12345678 + 0.1) + ''; - } - return n * 3; -} - -function float_toString(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10 + 0.1).toString(); - s = (j + 0.1).toString(); - s = (j * 12345678 + 0.1).toString(); - } - return n * 3; -} - -function float_toFixed(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10 + 0.1).toFixed(j % 16); - s = (j + 0.1).toFixed(j % 16); - s = (j * 12345678 + 0.1).toFixed(j % 16); - } - return n * 3; -} - -function float_toPrecision(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10 + 0.1).toPrecision(j % 16 + 1); - s = (j + 0.1).toPrecision(j % 16 + 1); - s = (j * 12345678 + 0.1).toPrecision(j % 16 + 1); - } - return n * 3; -} - -function float_toExponential(n) -{ - var s, r, j; - r = 0; - for(j = 0; j < n; j++) { - s = (j % 10 + 0.1).toExponential(j % 16); - s = (j + 0.1).toExponential(j % 16); - s = (j * 12345678 + 0.1).toExponential(j % 16); - } - return n * 3; -} - -function string_to_int(n) -{ - var s, r, j; - r = 0; - s = "12345"; - for(j = 0; j < n; j++) { - r += (s | 0); - } - global_res = r; - return n; -} - -function string_to_float(n) -{ - var s, r, j; - r = 0; - s = "12345.6"; - for(j = 0; j < n; j++) { - r -= s; - } - global_res = r; - return n; -} - -function load_result(filename) -{ - var has_filename = filename; - var has_error = false; - var str, res; - - if (!filename) - filename = "microbench.txt"; - - if (typeof fs !== "undefined") { - // read the file in Node.js - try { - str = fs.readFileSync(filename, { encoding: "utf8" }); - } catch { - has_error = true; - } - } else - if (typeof std !== "undefined") { - // read the file in QuickJS - var f = std.open(filename, "r"); - if (f) { - str = f.readAsString(); - f.close(); - } else { - has_error = true; - } - } else { - return null; - } - if (has_error) { - if (has_filename) { - // Should throw exception? - console.log("cannot load " + filename); - } - return null; - } - res = JSON.parse(str); - return res; -} - -function save_result(filename, obj) -{ - var str = JSON.stringify(obj, null, 2) + "\n"; - var has_error = false; - - if (typeof fs !== "undefined") { - // save the file in Node.js - try { - str = fs.writeFileSync(filename, str, { encoding: "utf8" }); - } catch { - has_error = true; - } - } else - if (typeof std !== "undefined") { - // save the file in QuickJS - var f = std.open(filename, "w"); - if (f) { - f.puts(str); - f.close(); - } else { - has_error = 'true'; - } - } else { - return; - } - if (has_error) - console.log("cannot save " + filename); -} - -function main(argc, argv, g) -{ - var test_list = [ - empty_loop, - empty_down_loop, - empty_down_loop2, - empty_do_loop, - date_now, - date_parse, - prop_read, - prop_write, - prop_update, - prop_create, - prop_clone, - prop_delete, - array_read, - array_write, - array_update, - array_prop_create, - array_slice, - array_length_read, - array_length_decr, - array_hole_length_decr, - array_push, - array_pop, - typed_array_read, - typed_array_write, - arguments_read, - arguments_strict_read, - global_read, - global_write, - global_write_strict, - local_destruct, - global_destruct, - global_destruct_strict, - global_func_call, - func_call, - func_closure_call, - int_arith, - float_arith, - map_set_string, - map_set_int, - map_set_bigint, - map_delete, - weak_map_set, - weak_map_delete, - array_for, - array_for_in, - array_for_of, - math_min, - regexp_ascii, - regexp_utf16, - regexp_replace, - string_length, - string_build1, - string_build1x, - string_build2c, - string_build2, - string_build3, - string_build4, - string_build_large1, - string_build_large2, - int_to_string, - int_toString, - float_to_string, - float_toString, - float_toFixed, - float_toPrecision, - float_toExponential, - string_to_int, - string_to_float, - ]; - var tests = []; - var i, j, n, f, name, found; - var ref_file, new_ref_file = "microbench-new.txt"; - - if (typeof BigInt === "function") { - /* BigInt test */ - test_list.push(bigint32_arith); - test_list.push(bigint64_arith); - test_list.push(bigint256_arith); - } - test_list.push(sort_bench); - - for (i = 1; i < argc;) { - name = argv[i++]; - if (name == "-a") { - sort_bench.verbose = true; - continue; - } - if (name == "-t") { - name = argv[i++]; - sort_bench.array_type = g[name]; - if (typeof sort_bench.array_type !== "function") { - console.log("unknown array type: " + name); - return 1; - } - continue; - } - if (name == "-n") { - sort_bench.array_size = +argv[i++]; - continue; - } - if (name == "-r") { - ref_file = argv[i++]; - continue; - } - if (name == "-s") { - new_ref_file = argv[i++]; - continue; - } - for (j = 0, found = false; j < test_list.length; j++) { - f = test_list[j]; - if (f.name.startsWith(name)) { - tests.push(f); - found = true; - } - } - if (!found) { - console.log("unknown benchmark: " + name); - return 1; - } - } - if (tests.length == 0) - tests = test_list; - - ref_data = load_result(ref_file); - log_data = {}; - log_line.apply(null, heads); - n = 0; - - for(i = 0; i < tests.length; i++) { - f = tests[i]; - bench(f, f.name, ref_data, log_data); - if (ref_data && ref_data[f.name]) - n++; - } - if (ref_data) - log_line("total", "", total[2], total[3], Math.round(total_scale * 1000 / total_score)); - else - log_line("total", "", total[2]); - - if (tests == test_list && new_ref_file) - save_result(new_ref_file, log_data); -} - -if (typeof scriptArgs === "undefined") { - scriptArgs = []; - if (typeof process.argv === "object") - scriptArgs = process.argv.slice(1); -} -main(scriptArgs.length, scriptArgs, this); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test262.patch b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test262.patch deleted file mode 100755 index d7cba88c..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test262.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js -index 9828b15..9e24d64 100644 ---- a/harness/atomicsHelper.js -+++ b/harness/atomicsHelper.js -@@ -272,10 +272,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) { - * } - */ - $262.agent.timeouts = { -- yield: 100, -- small: 200, -- long: 1000, -- huge: 10000, -+// yield: 100, -+// small: 200, -+// long: 1000, -+// huge: 10000, -+ yield: 40, -+ small: 40, -+ long: 200, -+ huge: 1000, - }; - - /** -diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js -index b397be0..c197ddc 100644 ---- a/harness/regExpUtils.js -+++ b/harness/regExpUtils.js -@@ -6,27 +6,30 @@ description: | - defines: [buildString, testPropertyEscapes, testPropertyOfStrings, testExtendedCharacterClass, matchValidator] - ---*/ - -+if ($262 && typeof $262.codePointRange === "function") { -+ /* use C function to build the codePointRange (much faster with -+ slow JS engines) */ -+ codePointRange = $262.codePointRange; -+} else { -+ codePointRange = function codePointRange(start, end) { -+ const codePoints = []; -+ let length = 0; -+ for (codePoint = start; codePoint < end; codePoint++) { -+ codePoints[length++] = codePoint; -+ } -+ return String.fromCodePoint.apply(null, codePoints); -+ } -+} -+ - function buildString(args) { - // Use member expressions rather than destructuring `args` for improved - // compatibility with engines that only implement assignment patterns - // partially or not at all. - const loneCodePoints = args.loneCodePoints; - const ranges = args.ranges; -- const CHUNK_SIZE = 10000; - let result = String.fromCodePoint.apply(null, loneCodePoints); -- for (let i = 0; i < ranges.length; i++) { -- let range = ranges[i]; -- let start = range[0]; -- let end = range[1]; -- let codePoints = []; -- for (let length = 0, codePoint = start; codePoint <= end; codePoint++) { -- codePoints[length++] = codePoint; -- if (length === CHUNK_SIZE) { -- result += String.fromCodePoint.apply(null, codePoints); -- codePoints.length = length = 0; -- } -- } -- result += String.fromCodePoint.apply(null, codePoints); -+ for (const [start, end] of ranges) { -+ result += codePointRange(start, end + 1); - } - return result; - } diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_bigint.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_bigint.js deleted file mode 100755 index a0d028c9..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_bigint.js +++ /dev/null @@ -1,249 +0,0 @@ -"use strict"; - -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (actual === expected) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -function assertThrows(err, func) -{ - var ex; - ex = false; - try { - func(); - } catch(e) { - ex = true; - assert(e instanceof err); - } - assert(ex, true, "exception expected"); -} - -// load more elaborate version of assert if available -try { __loadScript("test_assert.js"); } catch(e) {} - -/*----------------*/ - -function bigint_pow(a, n) -{ - var r, i; - r = 1n; - for(i = 0n; i < n; i++) - r *= a; - return r; -} - -/* a must be < b */ -function test_less(a, b) -{ - assert(a < b); - assert(!(b < a)); - assert(a <= b); - assert(!(b <= a)); - assert(b > a); - assert(!(a > b)); - assert(b >= a); - assert(!(a >= b)); - assert(a != b); - assert(!(a == b)); -} - -/* a must be numerically equal to b */ -function test_eq(a, b) -{ - assert(a == b); - assert(b == a); - assert(!(a != b)); - assert(!(b != a)); - assert(a <= b); - assert(b <= a); - assert(!(a < b)); - assert(a >= b); - assert(b >= a); - assert(!(a > b)); -} - -function test_bigint1() -{ - var a, r; - - test_less(2n, 3n); - test_eq(3n, 3n); - - test_less(2, 3n); - test_eq(3, 3n); - - test_less(2.1, 3n); - test_eq(Math.sqrt(4), 2n); - - a = bigint_pow(3n, 100n); - assert((a - 1n) != a); - assert(a == 515377520732011331036461129765621272702107522001n); - assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n); - - r = 1n << 31n; - assert(r, 2147483648n, "1 << 31n === 2147483648n"); - - r = 1n << 32n; - assert(r, 4294967296n, "1 << 32n === 4294967296n"); -} - -function test_bigint2() -{ - assert(BigInt(""), 0n); - assert(BigInt(" 123"), 123n); - assert(BigInt(" 123 "), 123n); - assertThrows(SyntaxError, () => { BigInt("+") } ); - assertThrows(SyntaxError, () => { BigInt("-") } ); - assertThrows(SyntaxError, () => { BigInt("\x00a") } ); - assertThrows(SyntaxError, () => { BigInt(" 123 r") } ); -} - -function test_bigint3() -{ - assert(Number(0xffffffffffffffffn), 18446744073709552000); - assert(Number(-0xffffffffffffffffn), -18446744073709552000); - assert(100000000000000000000n == 1e20, true); - assert(100000000000000000001n == 1e20, false); - assert((1n << 100n).toString(10), "1267650600228229401496703205376"); - assert((-1n << 100n).toString(36), "-3ewfdnca0n6ld1ggvfgg"); - assert((1n << 100n).toString(8), "2000000000000000000000000000000000"); - - assert(0x5a4653ca673768565b41f775n << 78n, 8443945299673273647701379149826607537748959488376832n); - assert(-0x5a4653ca673768565b41f775n << 78n, -8443945299673273647701379149826607537748959488376832n); - assert(0x5a4653ca673768565b41f775n >> 78n, 92441n); - assert(-0x5a4653ca673768565b41f775n >> 78n, -92442n); - - assert(~0x5a653ca6n, -1516584103n); - assert(0x5a463ca6n | 0x67376856n, 2138537206n); - assert(0x5a463ca6n & 0x67376856n, 1107699718n); - assert(0x5a463ca6n ^ 0x67376856n, 1030837488n); - - assert(3213213213213213432453243n / 123434343439n, 26031760073331n); - assert(-3213213213213213432453243n / 123434343439n, -26031760073331n); - assert(-3213213213213213432453243n % -123434343439n, -26953727934n); - assert(3213213213213213432453243n % 123434343439n, 26953727934n); - - assert((-2n) ** 127n, -170141183460469231731687303715884105728n); - assert((2n) ** 127n, 170141183460469231731687303715884105728n); - assert((-256n) ** 11n, -309485009821345068724781056n); - assert((7n) ** 20n, 79792266297612001n); -} - -/* pi computation */ - -/* return floor(log2(a)) for a > 0 and 0 for a = 0 */ -function floor_log2(a) -{ - var k_max, a1, k, i; - k_max = 0n; - while ((a >> (2n ** k_max)) != 0n) { - k_max++; - } - k = 0n; - a1 = a; - for(i = k_max - 1n; i >= 0n; i--) { - a1 = a >> (2n ** i); - if (a1 != 0n) { - a = a1; - k |= (1n << i); - } - } - return k; -} - -/* return ceil(log2(a)) for a > 0 */ -function ceil_log2(a) -{ - return floor_log2(a - 1n) + 1n; -} - -/* return floor(sqrt(a)) (not efficient but simple) */ -function int_sqrt(a) -{ - var l, u, s; - if (a == 0n) - return a; - l = ceil_log2(a); - u = 1n << ((l + 1n) / 2n); - /* u >= floor(sqrt(a)) */ - for(;;) { - s = u; - u = ((a / s) + s) / 2n; - if (u >= s) - break; - } - return s; -} - -/* return pi * 2**prec */ -function calc_pi(prec) { - const CHUD_A = 13591409n; - const CHUD_B = 545140134n; - const CHUD_C = 640320n; - const CHUD_C3 = 10939058860032000n; /* C^3/24 */ - const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */ - - /* return [P, Q, G] */ - function chud_bs(a, b, need_G) { - var c, P, Q, G, P1, Q1, G1, P2, Q2, G2; - if (a == (b - 1n)) { - G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n); - P = G * (CHUD_B * b + CHUD_A); - if (b & 1n) - P = -P; - Q = b * b * b * CHUD_C3; - } else { - c = (a + b) >> 1n; - [P1, Q1, G1] = chud_bs(a, c, true); - [P2, Q2, G2] = chud_bs(c, b, need_G); - P = P1 * Q2 + P2 * G1; - Q = Q1 * Q2; - if (need_G) - G = G1 * G2; - else - G = 0n; - } - return [P, Q, G]; - } - - var n, P, Q, G; - /* number of serie terms */ - n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n; - [P, Q, G] = chud_bs(0n, n, false); - Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A); - G = int_sqrt(CHUD_C << (2n * prec)); - return (Q * G) >> prec; -} - -function compute_pi(n_digits) { - var r, n_digits, n_bits, out; - /* we add more bits to reduce the probability of bad rounding for - the last digits */ - n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n; - r = calc_pi(n_bits); - r = ((10n ** BigInt(n_digits)) * r) >> n_bits; - out = r.toString(); - return out[0] + "." + out.slice(1); -} - -function test_pi() -{ - assert(compute_pi(2000), "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009"); -} - -test_bigint1(); -test_bigint2(); -test_bigint3(); -test_pi(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_bjson.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_bjson.js deleted file mode 100755 index 12180d16..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_bjson.js +++ /dev/null @@ -1,224 +0,0 @@ -import * as bjson from "./bjson.so"; - -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (actual === expected) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -function toHex(a) -{ - var i, s = "", tab, v; - tab = new Uint8Array(a); - for(i = 0; i < tab.length; i++) { - v = tab[i].toString(16); - if (v.length < 2) - v = "0" + v; - if (i !== 0) - s += " "; - s += v; - } - return s; -} - -function isArrayLike(a) -{ - return Array.isArray(a) || - (a instanceof Uint8ClampedArray) || - (a instanceof Uint8Array) || - (a instanceof Uint16Array) || - (a instanceof Uint32Array) || - (a instanceof Int8Array) || - (a instanceof Int16Array) || - (a instanceof Int32Array) || - (a instanceof Float16Array) || - (a instanceof Float32Array) || - (a instanceof Float64Array); -} - -function toStr(a) -{ - var s, i, props, prop; - - switch(typeof(a)) { - case "object": - if (a === null) - return "null"; - if (a instanceof Date) { - s = "Date(" + toStr(a.valueOf()) + ")"; - } else if (a instanceof Number) { - s = "Number(" + toStr(a.valueOf()) + ")"; - } else if (a instanceof String) { - s = "String(" + toStr(a.valueOf()) + ")"; - } else if (a instanceof Boolean) { - s = "Boolean(" + toStr(a.valueOf()) + ")"; - } else if (isArrayLike(a)) { - s = "["; - for(i = 0; i < a.length; i++) { - if (i != 0) - s += ","; - s += toStr(a[i]); - } - s += "]"; - } else { - props = Object.keys(a); - s = "{"; - for(i = 0; i < props.length; i++) { - if (i != 0) - s += ","; - prop = props[i]; - s += prop + ":" + toStr(a[prop]); - } - s += "}"; - } - return s; - case "undefined": - return "undefined"; - case "string": - return JSON.stringify(a); - case "number": - if (a == 0 && 1 / a < 0) - return "-0"; - else - return a.toString(); - break; - default: - return a.toString(); - } -} - -function bjson_test(a) -{ - var buf, r, a_str, r_str; - a_str = toStr(a); - buf = bjson.write(a); - if (0) { - print(a_str, "->", toHex(buf)); - } - r = bjson.read(buf, 0, buf.byteLength); - r_str = toStr(r); - if (a_str != r_str) { - print(a_str); - print(r_str); - assert(false); - } -} - -function bjson_test_arraybuffer() -{ - var buf, array_buffer; - - array_buffer = new ArrayBuffer(4); - assert(array_buffer.byteLength, 4); - assert(array_buffer.maxByteLength, 4); - assert(array_buffer.resizable, false); - buf = bjson.write(array_buffer); - array_buffer = bjson.read(buf, 0, buf.byteLength); - assert(array_buffer.byteLength, 4); - assert(array_buffer.maxByteLength, 4); - assert(array_buffer.resizable, false); - - array_buffer = new ArrayBuffer(4, {maxByteLength: 4}); - assert(array_buffer.byteLength, 4); - assert(array_buffer.maxByteLength, 4); - assert(array_buffer.resizable, true); - buf = bjson.write(array_buffer); - array_buffer = bjson.read(buf, 0, buf.byteLength); - assert(array_buffer.byteLength, 4); - assert(array_buffer.maxByteLength, 4); - assert(array_buffer.resizable, true); - - array_buffer = new ArrayBuffer(4, {maxByteLength: 8}); - assert(array_buffer.byteLength, 4); - assert(array_buffer.maxByteLength, 8); - assert(array_buffer.resizable, true); - buf = bjson.write(array_buffer); - array_buffer = bjson.read(buf, 0, buf.byteLength); - assert(array_buffer.byteLength, 4); - assert(array_buffer.maxByteLength, 8); - assert(array_buffer.resizable, true); -} - -/* test multiple references to an object including circular - references */ -function bjson_test_reference() -{ - var array, buf, i, n, array_buffer; - n = 16; - array = []; - for(i = 0; i < n; i++) - array[i] = {}; - array_buffer = new ArrayBuffer(n); - for(i = 0; i < n; i++) { - array[i].next = array[(i + 1) % n]; - array[i].idx = i; - array[i].typed_array = new Uint8Array(array_buffer, i, 1); - } - buf = bjson.write(array, true); - - array = bjson.read(buf, 0, buf.byteLength, true); - - /* check the result */ - for(i = 0; i < n; i++) { - assert(array[i].next, array[(i + 1) % n]); - assert(array[i].idx, i); - assert(array[i].typed_array.buffer, array_buffer); - assert(array[i].typed_array.length, 1); - assert(array[i].typed_array.byteOffset, i); - } -} - -function bjson_test_all() -{ - var obj; - - bjson_test({x:1, y:2, if:3}); - - bjson_test([1, 2, 3]); - - /* array with holes */ - bjson_test([1, , 2, , 3]); - - /* fast array with hole */ - obj = new Array(5); - obj[0] = 1; - obj[1] = 2; - bjson_test(obj); - - bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]); - if (typeof BigInt !== "undefined") { - bjson_test([BigInt("1"), -BigInt("0x123456789"), - BigInt("0x123456789abcdef123456789abcdef")]); - } - bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]); - - bjson_test(new Int32Array([123123, 222111, -32222])); - bjson_test(new Float16Array([1024, 1024.5])); - bjson_test(new Float64Array([123123, 222111.5])); - - /* tested with a circular reference */ - obj = {}; - obj.x = obj; - try { - bjson.write(obj); - assert(false); - } catch(e) { - assert(e instanceof TypeError); - } - - bjson_test_arraybuffer(); - bjson_test_reference(); -} - -bjson_test_all(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_builtin.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_builtin.js deleted file mode 100755 index 14bcc06d..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_builtin.js +++ /dev/null @@ -1,1193 +0,0 @@ -"use strict"; - -var status = 0; -var throw_errors = true; - -function throw_error(msg) { - if (throw_errors) - throw Error(msg); - console.log(msg); - status = 1; -} - -function assert(actual, expected, message) { - function get_full_type(o) { - var type = typeof(o); - if (type === 'object') { - if (o === null) - return 'null'; - if (o.constructor && o.constructor.name) - return o.constructor.name; - } - return type; - } - - if (arguments.length == 1) - expected = true; - - if (typeof actual === typeof expected) { - if (actual === expected) { - if (actual !== 0 || (1 / actual) === (1 / expected)) - return; - } - if (typeof actual === 'number') { - if (isNaN(actual) && isNaN(expected)) - return true; - } - if (typeof actual === 'object') { - if (actual !== null && expected !== null - && actual.constructor === expected.constructor - && actual.toString() === expected.toString()) - return; - } - } - // Should output the source file and line number and extract - // the expression from the assert call - throw_error("assertion failed: got " + - get_full_type(actual) + ":|" + actual + "|, expected " + - get_full_type(expected) + ":|" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -function assert_throws(expected_error, func) -{ - var err = false; - try { - func(); - } catch(e) { - err = true; - if (!(e instanceof expected_error)) { - // Should output the source file and line number and extract - // the expression from the assert_throws() call - throw_error("unexpected exception type"); - return; - } - } - if (!err) { - // Should output the source file and line number and extract - // the expression from the assert_throws() call - throw_error("expected exception"); - } -} - -// load more elaborate version of assert if available -try { __loadScript("test_assert.js"); } catch(e) {} - -/*----------------*/ - -function my_func(a, b) -{ - return a + b; -} - -function test_function() -{ - function f(a, b) { - var i, tab = []; - tab.push(this); - for(i = 0; i < arguments.length; i++) - tab.push(arguments[i]); - return tab; - } - function constructor1(a) { - this.x = a; - } - - var r, g; - - r = my_func.call(null, 1, 2); - assert(r, 3, "call"); - - r = my_func.apply(null, [1, 2]); - assert(r, 3, "apply"); - - r = (function () { return 1; }).apply(null, undefined); - assert(r, 1); - - assert_throws(TypeError, (function() { - Reflect.apply((function () { return 1; }), null, undefined); - })); - - r = new Function("a", "b", "return a + b;"); - assert(r(2,3), 5, "function"); - - g = f.bind(1, 2); - assert(g.length, 1); - assert(g.name, "bound f"); - assert(g(3), [1,2,3]); - - g = constructor1.bind(null, 1); - r = new g(); - assert(r.x, 1); -} - -function test() -{ - var r, a, b, c, err; - - r = Error("hello"); - assert(r.message, "hello", "Error"); - - a = new Object(); - a.x = 1; - assert(a.x, 1, "Object"); - - assert(Object.getPrototypeOf(a), Object.prototype, "getPrototypeOf"); - Object.defineProperty(a, "y", { value: 3, writable: true, configurable: true, enumerable: true }); - assert(a.y, 3, "defineProperty"); - - Object.defineProperty(a, "z", { get: function () { return 4; }, set: function(val) { this.z_val = val; }, configurable: true, enumerable: true }); - assert(a.z, 4, "get"); - a.z = 5; - assert(a.z_val, 5, "set"); - - a = { get z() { return 4; }, set z(val) { this.z_val = val; } }; - assert(a.z, 4, "get"); - a.z = 5; - assert(a.z_val, 5, "set"); - - b = Object.create(a); - assert(Object.getPrototypeOf(b), a, "create"); - c = {u:2}; - /* XXX: refcount bug in 'b' instead of 'a' */ - Object.setPrototypeOf(a, c); - assert(Object.getPrototypeOf(a), c, "setPrototypeOf"); - - a = {}; - assert(a.toString(), "[object Object]", "toString"); - - a = {x:1}; - assert(Object.isExtensible(a), true, "extensible"); - Object.preventExtensions(a); - - err = false; - try { - a.y = 2; - } catch(e) { - err = true; - } - assert(Object.isExtensible(a), false, "extensible"); - assert(typeof a.y, "undefined", "extensible"); - assert(err, true, "extensible"); -} - -function test_enum() -{ - var a, tab; - a = {x:1, - "18014398509481984": 1, - "9007199254740992": 1, - "9007199254740991": 1, - "4294967296": 1, - "4294967295": 1, - y:1, - "4294967294": 1, - "1": 2}; - tab = Object.keys(a); -// console.log("tab=" + tab.toString()); - assert(tab, ["1","4294967294","x","18014398509481984","9007199254740992","9007199254740991","4294967296","4294967295","y"], "keys"); -} - -function test_array() -{ - var a, err; - - a = [1, 2, 3]; - assert(a.length, 3, "array"); - assert(a[2], 3, "array1"); - - a = new Array(10); - assert(a.length, 10, "array2"); - - a = new Array(1, 2); - assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array3"); - - a = [1, 2, 3]; - a.length = 2; - assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array4"); - - a = []; - a[1] = 10; - a[4] = 3; - assert(a.length, 5); - - a = [1,2]; - a.length = 5; - a[4] = 1; - a.length = 4; - assert(a[4] !== 1, true, "array5"); - - a = [1,2]; - a.push(3,4); - assert(a.join(), "1,2,3,4", "join"); - - a = [1,2,3,4,5]; - Object.defineProperty(a, "3", { configurable: false }); - err = false; - try { - a.length = 2; - } catch(e) { - err = true; - } - assert(err && a.toString() === "1,2,3,4"); -} - -function test_string() -{ - var a; - a = String("abc"); - assert(a.length, 3, "string"); - assert(a[1], "b", "string"); - assert(a.charCodeAt(1), 0x62, "string"); - assert(String.fromCharCode(65), "A", "string"); - assert(String.fromCharCode.apply(null, [65, 66, 67]), "ABC", "string"); - assert(a.charAt(1), "b"); - assert(a.charAt(-1), ""); - assert(a.charAt(3), ""); - - a = "abcd"; - assert(a.substring(1, 3), "bc", "substring"); - a = String.fromCharCode(0x20ac); - assert(a.charCodeAt(0), 0x20ac, "unicode"); - assert(a, "€", "unicode"); - assert(a, "\u20ac", "unicode"); - assert(a, "\u{20ac}", "unicode"); - assert("a", "\x61", "unicode"); - - a = "\u{10ffff}"; - assert(a.length, 2, "unicode"); - assert(a, "\u{dbff}\u{dfff}", "unicode"); - assert(a.codePointAt(0), 0x10ffff); - assert(String.fromCodePoint(0x10ffff), a); - - assert("a".concat("b", "c"), "abc"); - - assert("abcabc".indexOf("cab"), 2); - assert("abcabc".indexOf("cab2"), -1); - assert("abc".indexOf("c"), 2); - - assert("aaa".indexOf("a"), 0); - assert("aaa".indexOf("a", NaN), 0); - assert("aaa".indexOf("a", -Infinity), 0); - assert("aaa".indexOf("a", -1), 0); - assert("aaa".indexOf("a", -0), 0); - assert("aaa".indexOf("a", 0), 0); - assert("aaa".indexOf("a", 1), 1); - assert("aaa".indexOf("a", 2), 2); - assert("aaa".indexOf("a", 3), -1); - assert("aaa".indexOf("a", 4), -1); - assert("aaa".indexOf("a", Infinity), -1); - - assert("aaa".indexOf(""), 0); - assert("aaa".indexOf("", NaN), 0); - assert("aaa".indexOf("", -Infinity), 0); - assert("aaa".indexOf("", -1), 0); - assert("aaa".indexOf("", -0), 0); - assert("aaa".indexOf("", 0), 0); - assert("aaa".indexOf("", 1), 1); - assert("aaa".indexOf("", 2), 2); - assert("aaa".indexOf("", 3), 3); - assert("aaa".indexOf("", 4), 3); - assert("aaa".indexOf("", Infinity), 3); - - assert("aaa".lastIndexOf("a"), 2); - assert("aaa".lastIndexOf("a", NaN), 2); - assert("aaa".lastIndexOf("a", -Infinity), 0); - assert("aaa".lastIndexOf("a", -1), 0); - assert("aaa".lastIndexOf("a", -0), 0); - assert("aaa".lastIndexOf("a", 0), 0); - assert("aaa".lastIndexOf("a", 1), 1); - assert("aaa".lastIndexOf("a", 2), 2); - assert("aaa".lastIndexOf("a", 3), 2); - assert("aaa".lastIndexOf("a", 4), 2); - assert("aaa".lastIndexOf("a", Infinity), 2); - - assert("aaa".lastIndexOf(""), 3); - assert("aaa".lastIndexOf("", NaN), 3); - assert("aaa".lastIndexOf("", -Infinity), 0); - assert("aaa".lastIndexOf("", -1), 0); - assert("aaa".lastIndexOf("", -0), 0); - assert("aaa".lastIndexOf("", 0), 0); - assert("aaa".lastIndexOf("", 1), 1); - assert("aaa".lastIndexOf("", 2), 2); - assert("aaa".lastIndexOf("", 3), 3); - assert("aaa".lastIndexOf("", 4), 3); - assert("aaa".lastIndexOf("", Infinity), 3); - - assert("a,b,c".split(","), ["a","b","c"]); - assert(",b,c".split(","), ["","b","c"]); - assert("a,b,".split(","), ["a","b",""]); - - assert("aaaa".split(), [ "aaaa" ]); - assert("aaaa".split(undefined, 0), [ ]); - assert("aaaa".split(""), [ "a", "a", "a", "a" ]); - assert("aaaa".split("", 0), [ ]); - assert("aaaa".split("", 1), [ "a" ]); - assert("aaaa".split("", 2), [ "a", "a" ]); - assert("aaaa".split("a"), [ "", "", "", "", "" ]); - assert("aaaa".split("a", 2), [ "", "" ]); - assert("aaaa".split("aa"), [ "", "", "" ]); - assert("aaaa".split("aa", 0), [ ]); - assert("aaaa".split("aa", 1), [ "" ]); - assert("aaaa".split("aa", 2), [ "", "" ]); - assert("aaaa".split("aaa"), [ "", "a" ]); - assert("aaaa".split("aaaa"), [ "", "" ]); - assert("aaaa".split("aaaaa"), [ "aaaa" ]); - assert("aaaa".split("aaaaa", 0), [ ]); - assert("aaaa".split("aaaaa", 1), [ "aaaa" ]); - - assert(eval('"\0"'), "\0"); - - assert("abc".padStart(Infinity, ""), "abc"); -} - -function test_math() -{ - var a; - a = 1.4; - assert(Math.floor(a), 1); - assert(Math.ceil(a), 2); - assert(Math.imul(0x12345678, 123), -1088058456); - assert(Math.imul(0xB505, 0xB504), 2147441940); - assert(Math.imul(0xB505, 0xB505), -2147479015); - assert(Math.imul((-2)**31, (-2)**31), 0); - assert(Math.imul(2**31-1, 2**31-1), 1); - assert(Math.fround(0.1), 0.10000000149011612); - assert(Math.hypot(), 0); - assert(Math.hypot(-2), 2); - assert(Math.hypot(3, 4), 5); - assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15); - assert(Math.sumPrecise([1,Number.EPSILON/2,Number.MIN_VALUE]), 1.0000000000000002); -} - -function test_number() -{ - assert(parseInt("123"), 123); - assert(parseInt(" 123r"), 123); - assert(parseInt("0x123"), 0x123); - assert(parseInt("0o123"), 0); - assert(+" 123 ", 123); - assert(+"0b111", 7); - assert(+"0o123", 83); - assert(parseFloat("2147483647"), 2147483647); - assert(parseFloat("2147483648"), 2147483648); - assert(parseFloat("-2147483647"), -2147483647); - assert(parseFloat("-2147483648"), -2147483648); - assert(parseFloat("0x1234"), 0); - assert(parseFloat("Infinity"), Infinity); - assert(parseFloat("-Infinity"), -Infinity); - assert(parseFloat("123.2"), 123.2); - assert(parseFloat("123.2e3"), 123200); - assert(Number.isNaN(Number("+"))); - assert(Number.isNaN(Number("-"))); - assert(Number.isNaN(Number("\x00a"))); - - assert((1-2**-53).toString(12), "0.bbbbbbbbbbbbbba"); - assert((1000000000000000128).toString(), "1000000000000000100"); - assert((1000000000000000128).toFixed(0), "1000000000000000128"); - assert((25).toExponential(0), "3e+1"); - assert((-25).toExponential(0), "-3e+1"); - assert((2.5).toPrecision(1), "3"); - assert((-2.5).toPrecision(1), "-3"); - assert((25).toPrecision(1) === "3e+1"); - assert((1.125).toFixed(2), "1.13"); - assert((-1.125).toFixed(2), "-1.13"); - assert((0.5).toFixed(0), "1"); - assert((-0.5).toFixed(0), "-1"); - assert((-1e-10).toFixed(0), "-0"); - - assert((1.3).toString(7), "1.2046204620462046205"); - assert((1.3).toString(35), "1.ahhhhhhhhhm"); -} - -function test_eval2() -{ - var g_call_count = 0; - /* force non strict mode for f1 and f2 */ - var f1 = new Function("eval", "eval(1, 2)"); - var f2 = new Function("eval", "eval(...[1, 2])"); - function g(a, b) { - assert(a, 1); - assert(b, 2); - g_call_count++; - } - f1(g); - f2(g); - assert(g_call_count, 2); -} - -function test_eval() -{ - function f(b) { - var x = 1; - return eval(b); - } - var r, a; - - r = eval("1+1;"); - assert(r, 2, "eval"); - - r = eval("var my_var=2; my_var;"); - assert(r, 2, "eval"); - assert(typeof my_var, "undefined"); - - assert(eval("if (1) 2; else 3;"), 2); - assert(eval("if (0) 2; else 3;"), 3); - - assert(f.call(1, "this"), 1); - - a = 2; - assert(eval("a"), 2); - - eval("a = 3"); - assert(a, 3); - - assert(f("arguments.length", 1), 2); - assert(f("arguments[1]", 1), 1); - - a = 4; - assert(f("a"), 4); - f("a=3"); - assert(a, 3); - - test_eval2(); -} - -function test_typed_array() -{ - var buffer, a, i, str; - - a = new Uint8Array(4); - assert(a.length, 4); - for(i = 0; i < a.length; i++) - a[i] = i; - assert(a.join(","), "0,1,2,3"); - a[0] = -1; - assert(a[0], 255); - - a = new Int8Array(3); - a[0] = 255; - assert(a[0], -1); - - a = new Int32Array(3); - a[0] = Math.pow(2, 32) - 1; - assert(a[0], -1); - assert(a.BYTES_PER_ELEMENT, 4); - - a = new Uint8ClampedArray(4); - a[0] = -100; - a[1] = 1.5; - a[2] = 0.5; - a[3] = 1233.5; - assert(a.toString(), "0,2,0,255"); - - buffer = new ArrayBuffer(16); - assert(buffer.byteLength, 16); - a = new Uint32Array(buffer, 12, 1); - assert(a.length, 1); - a[0] = -1; - - a = new Uint16Array(buffer, 2); - a[0] = -1; - - a = new Float16Array(buffer, 8, 1); - a[0] = 1; - - a = new Float32Array(buffer, 8, 1); - a[0] = 1; - - a = new Uint8Array(buffer); - - str = a.toString(); - /* test little and big endian cases */ - if (str !== "0,0,255,255,0,0,0,0,0,0,128,63,255,255,255,255" && - str !== "0,0,255,255,0,0,0,0,63,128,0,0,255,255,255,255") { - assert(false); - } - - assert(a.buffer, buffer); - - a = new Uint8Array([1, 2, 3, 4]); - assert(a.toString(), "1,2,3,4"); - a.set([10, 11], 2); - assert(a.toString(), "1,2,10,11"); - - // https://github.com/quickjs-ng/quickjs/issues/1208 - buffer = new ArrayBuffer(16); - a = new Uint8Array(buffer); - a.fill(42); - assert(a[0], 42); - buffer.transfer(); - assert(a[0], undefined); -} - -/* return [s, line_num, col_num] where line_num and col_num are the - position of the '@' character in 'str'. 's' is str without the '@' - character */ -function get_string_pos(str) -{ - var p, line_num, col_num, s, q, r; - p = str.indexOf('@'); - assert(p >= 0, true); - q = 0; - line_num = 1; - for(;;) { - r = str.indexOf('\n', q); - if (r < 0 || r >= p) - break; - q = r + 1; - line_num++; - } - col_num = p - q + 1; - s = str.slice(0, p) + str.slice(p + 1); - return [s, line_num, col_num]; -} - -function check_error_pos(e, expected_error, line_num, col_num, level) -{ - var expected_pos, tab, line; - level |= 0; - expected_pos = ":" + line_num + ":" + col_num; - tab = e.stack.split("\n"); - line = tab[level]; - if (line.slice(-1) == ')') - line = line.slice(0, -1); - if (line.indexOf(expected_pos) < 0) { - throw_error("unexpected line or column number. error=" + e.message + - ".got |" + line + "|, expected |" + expected_pos + "|"); - } -} - -function assert_json_error(str, line_num, col_num) -{ - var err = false; - var expected_pos, tab; - - tab = get_string_pos(str); - - try { - JSON.parse(tab[0]); - } catch(e) { - err = true; - if (!(e instanceof SyntaxError)) { - throw_error("unexpected exception type"); - return; - } - /* XXX: the way quickjs returns JSON errors is not similar to Node or spiderMonkey */ - check_error_pos(e, SyntaxError, tab[1], tab[2]); - } - if (!err) { - throw_error("expected exception"); - } -} - -function test_json() -{ - var a, s; - s = '{"x":1,"y":true,"z":null,"a":[1,2,3],"s":"str"}'; - a = JSON.parse(s); - assert(a.x, 1); - assert(a.y, true); - assert(a.z, null); - assert(JSON.stringify(a), s); - - /* indentation test */ - assert(JSON.stringify([[{x:1,y:{},z:[]},2,3]],undefined,1), -`[ - [ - { - "x": 1, - "y": {}, - "z": [] - }, - 2, - 3 - ] -]`); - - assert_json_error('\n" \\@x"'); - assert_json_error('\n{ "a": @x }"'); -} - -function test_date() -{ - // Date Time String format is YYYY-MM-DDTHH:mm:ss.sssZ - // accepted date formats are: YYYY, YYYY-MM and YYYY-MM-DD - // accepted time formats are: THH:mm, THH:mm:ss, THH:mm:ss.sss - // expanded years are represented with 6 digits prefixed by + or - - // -000000 is invalid. - // A string containing out-of-bounds or nonconforming elements - // is not a valid instance of this format. - // Hence the fractional part after . should have 3 digits and how - // a different number of digits is handled is implementation defined. - assert(Date.parse(""), NaN); - assert(Date.parse("2000"), 946684800000); - assert(Date.parse("2000-01"), 946684800000); - assert(Date.parse("2000-01-01"), 946684800000); - //assert(Date.parse("2000-01-01T"), NaN); - //assert(Date.parse("2000-01-01T00Z"), NaN); - assert(Date.parse("2000-01-01T00:00Z"), 946684800000); - assert(Date.parse("2000-01-01T00:00:00Z"), 946684800000); - assert(Date.parse("2000-01-01T00:00:00.1Z"), 946684800100); - assert(Date.parse("2000-01-01T00:00:00.10Z"), 946684800100); - assert(Date.parse("2000-01-01T00:00:00.100Z"), 946684800100); - assert(Date.parse("2000-01-01T00:00:00.1000Z"), 946684800100); - assert(Date.parse("2000-01-01T00:00:00+00:00"), 946684800000); - //assert(Date.parse("2000-01-01T00:00:00+00:30"), 946686600000); - var d = new Date("2000T00:00"); // Jan 1st 2000, 0:00:00 local time - assert(typeof d === 'object' && d.toString() != 'Invalid Date'); - assert((new Date('Jan 1 2000')).toISOString(), - d.toISOString()); - assert((new Date('Jan 1 2000 00:00')).toISOString(), - d.toISOString()); - assert((new Date('Jan 1 2000 00:00:00')).toISOString(), - d.toISOString()); - assert((new Date('Jan 1 2000 00:00:00 GMT+0100')).toISOString(), - '1999-12-31T23:00:00.000Z'); - assert((new Date('Jan 1 2000 00:00:00 GMT+0200')).toISOString(), - '1999-12-31T22:00:00.000Z'); - assert((new Date('Sat Jan 1 2000')).toISOString(), - d.toISOString()); - assert((new Date('Sat Jan 1 2000 00:00')).toISOString(), - d.toISOString()); - assert((new Date('Sat Jan 1 2000 00:00:00')).toISOString(), - d.toISOString()); - assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0100')).toISOString(), - '1999-12-31T23:00:00.000Z'); - assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0200')).toISOString(), - '1999-12-31T22:00:00.000Z'); - - var d = new Date(1506098258091); - assert(d.toISOString(), "2017-09-22T16:37:38.091Z"); - d.setUTCHours(18, 10, 11); - assert(d.toISOString(), "2017-09-22T18:10:11.091Z"); - var a = Date.parse(d.toISOString()); - assert((new Date(a)).toISOString(), d.toISOString()); - - assert((new Date("2020-01-01T01:01:01.123Z")).toISOString(), - "2020-01-01T01:01:01.123Z"); - /* implementation defined behavior */ - assert((new Date("2020-01-01T01:01:01.1Z")).toISOString(), - "2020-01-01T01:01:01.100Z"); - assert((new Date("2020-01-01T01:01:01.12Z")).toISOString(), - "2020-01-01T01:01:01.120Z"); - assert((new Date("2020-01-01T01:01:01.1234Z")).toISOString(), - "2020-01-01T01:01:01.123Z"); - assert((new Date("2020-01-01T01:01:01.12345Z")).toISOString(), - "2020-01-01T01:01:01.123Z"); - assert((new Date("2020-01-01T01:01:01.1235Z")).toISOString(), - "2020-01-01T01:01:01.123Z"); - assert((new Date("2020-01-01T01:01:01.9999Z")).toISOString(), - "2020-01-01T01:01:01.999Z"); - - assert(Date.UTC(2017), 1483228800000); - assert(Date.UTC(2017, 9), 1506816000000); - assert(Date.UTC(2017, 9, 22), 1508630400000); - assert(Date.UTC(2017, 9, 22, 18), 1508695200000); - assert(Date.UTC(2017, 9, 22, 18, 10), 1508695800000); - assert(Date.UTC(2017, 9, 22, 18, 10, 11), 1508695811000); - assert(Date.UTC(2017, 9, 22, 18, 10, 11, 91), 1508695811091); - - assert(Date.UTC(NaN), NaN); - assert(Date.UTC(2017, NaN), NaN); - assert(Date.UTC(2017, 9, NaN), NaN); - assert(Date.UTC(2017, 9, 22, NaN), NaN); - assert(Date.UTC(2017, 9, 22, 18, NaN), NaN); - assert(Date.UTC(2017, 9, 22, 18, 10, NaN), NaN); - assert(Date.UTC(2017, 9, 22, 18, 10, 11, NaN), NaN); - assert(Date.UTC(2017, 9, 22, 18, 10, 11, 91, NaN), 1508695811091); - - // TODO: Fix rounding errors on Windows/Cygwin. - if (!(typeof os !== 'undefined' && ['win32', 'cygwin'].includes(os.platform))) { - // from test262/test/built-ins/Date/UTC/fp-evaluation-order.js - assert(Date.UTC(1970, 0, 1, 80063993375, 29, 1, -288230376151711740), 29312, - 'order of operations / precision in MakeTime'); - assert(Date.UTC(1970, 0, 213503982336, 0, 0, 0, -18446744073709552000), 34447360, - 'precision in MakeDate'); - } - //assert(Date.UTC(2017 - 1e9, 9 + 12e9), 1506816000000); // node fails this - assert(Date.UTC(2017, 9, 22 - 1e10, 18 + 24e10), 1508695200000); - assert(Date.UTC(2017, 9, 22, 18 - 1e10, 10 + 60e10), 1508695800000); - assert(Date.UTC(2017, 9, 22, 18, 10 - 1e10, 11 + 60e10), 1508695811000); - assert(Date.UTC(2017, 9, 22, 18, 10, 11 - 1e12, 91 + 1000e12), 1508695811091); -} - -function test_regexp() -{ - var a, str; - str = "abbbbbc"; - a = /(b+)c/.exec(str); - assert(a[0], "bbbbbc"); - assert(a[1], "bbbbb"); - assert(a.index, 1); - assert(a.input, str); - a = /(b+)c/.test(str); - assert(a, true); - assert(/\x61/.exec("a")[0], "a"); - assert(/\u0061/.exec("a")[0], "a"); - assert(/\ca/.exec("\x01")[0], "\x01"); - assert(/\\a/.exec("\\a")[0], "\\a"); - assert(/\c0/.exec("\\c0")[0], "\\c0"); - - a = /(\.(?=com|org)|\/)/.exec("ah.com"); - assert(a.index === 2 && a[0] === "."); - - a = /(\.(?!com|org)|\/)/.exec("ah.com"); - assert(a, null); - - a = /(?=(a+))/.exec("baaabac"); - assert(a.index === 1 && a[0] === "" && a[1] === "aaa"); - - a = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac"); - assert(a, ["zaacbbbcac","z","ac","a",,"c"]); - - a = eval("/\0a/"); - assert(a.toString(), "/\0a/"); - assert(a.exec("\0a")[0], "\0a"); - - assert(/{1a}/.toString(), "/{1a}/"); - a = /a{1+/.exec("a{11"); - assert(a, ["a{11"]); - - /* test zero length matches */ - a = /(?:(?=(abc)))a/.exec("abc"); - assert(a, ["a", "abc"]); - a = /(?:(?=(abc)))?a/.exec("abc"); - assert(a, ["a", undefined]); - a = /(?:(?=(abc))){0,2}a/.exec("abc"); - assert(a, ["a", undefined]); - a = /(?:|[\w])+([0-9])/.exec("123a23"); - assert(a, ["123a23", "3"]); - a = /()*?a/.exec(","); - assert(a, null); - - /* test \b escape */ - assert(/[\q{a\b}]/.test("a\b"), true); - assert(/[\b]/.test("\b"), true); - - /* test case insensitive matching (test262 hardly tests it) */ - assert("aAbBcC#4".replace(/\p{Lower}/gu,"X"), "XAXBXC#4"); - - assert("aAbBcC#4".replace(/\p{Lower}/gui,"X"), "XXXXXX#4"); - assert("aAbBcC#4".replace(/\p{Upper}/gui,"X"), "XXXXXX#4"); - assert("aAbBcC#4".replace(/\P{Lower}/gui,"X"), "XXXXXXXX"); - assert("aAbBcC#4".replace(/\P{Upper}/gui,"X"), "XXXXXXXX"); - assert("aAbBcC".replace(/[^b]/gui, "X"), "XXbBXX"); - assert("aAbBcC".replace(/[^A-B]/gui, "X"), "aAbBXX"); - - assert("aAbBcC#4".replace(/\p{Lower}/gvi,"X"), "XXXXXX#4"); - assert("aAbBcC#4".replace(/\P{Lower}/gvi,"X"), "aAbBcCXX"); - assert("aAbBcC#4".replace(/[^\P{Lower}]/gvi,"X"), "XXXXXX#4"); - assert("aAbBcC#4".replace(/\P{Upper}/gvi,"X"), "aAbBcCXX"); - assert("aAbBcC".replace(/[^b]/gvi, "X"), "XXbBXX"); - assert("aAbBcC".replace(/[^A-B]/gvi, "X"), "aAbBXX"); - assert("aAbBcC".replace(/[[a-c]&&B]/gvi, "X"), "aAXXcC"); - assert("aAbBcC".replace(/[[a-c]--B]/gvi, "X"), "XXbBXX"); - - assert("abcAbC".replace(/[\q{AbC}]/gvi,"X"), "XX"); - /* Note: SpiderMonkey and v8 may not be correct */ - assert("abcAbC".replace(/[\q{BC|A}]/gvi,"X"), "XXXX"); - assert("abcAbC".replace(/[\q{BC|A}--a]/gvi,"X"), "aXAX"); - - /* case where lastIndex points to the second element of a - surrogate pair */ - a = /(?:)/gu; - a.lastIndex = 1; - a.exec("🐱"); - assert(a.lastIndex, 0); - - a.lastIndex = 1; - a.exec("a\udc00"); - assert(a.lastIndex, 1); - - a = /\u{10000}/vgd; - a.lastIndex = 1; - a = a.exec("\u{10000}_\u{10000}"); - assert(a.indices[0][0], 0); - assert(a.indices[0][1], 2); -} - -function test_symbol() -{ - var a, b, obj, c; - a = Symbol("abc"); - obj = {}; - obj[a] = 2; - assert(obj[a], 2); - assert(typeof obj["abc"], "undefined"); - assert(String(a), "Symbol(abc)"); - b = Symbol("abc"); - assert(a == a); - assert(a === a); - assert(a != b); - assert(a !== b); - - b = Symbol.for("abc"); - c = Symbol.for("abc"); - assert(b === c); - assert(b !== a); - - assert(Symbol.keyFor(b), "abc"); - assert(Symbol.keyFor(a), undefined); - - a = Symbol("aaa"); - assert(a.valueOf(), a); - assert(a.toString(), "Symbol(aaa)"); - - b = Object(a); - assert(b.valueOf(), a); - assert(b.toString(), "Symbol(aaa)"); -} - -function test_map1(key_type, n) -{ - var a, i, tab, o, v; - a = new Map(); - tab = []; - for(i = 0; i < n; i++) { - v = { }; - switch(key_type) { - case "small_bigint": - o = BigInt(i); - break; - case "bigint": - o = BigInt(i) + (1n << 128n); - break; - case "object": - o = { id: i }; - break; - default: - assert(false); - } - tab[i] = [o, v]; - a.set(o, v); - } - - assert(a.size, n); - for(i = 0; i < n; i++) { - assert(a.get(tab[i][0]), tab[i][1]); - } - - i = 0; - a.forEach(function (v, o) { - assert(o, tab[i++][0]); - assert(a.has(o)); - assert(a.delete(o)); - assert(!a.has(o)); - }); - - assert(a.size, 0); -} - -function test_map() -{ - var a, i, n, tab, o, v; - n = 1000; - - a = new Map(); - for (var i = 0; i < n; i++) { - a.set(i, i); - } - a.set(-2147483648, 1); - assert(a.get(-2147483648), 1); - assert(a.get(-2147483647 - 1), 1); - assert(a.get(-2147483647.5 - 0.5), 1); - - a.set(1n, 1n); - assert(a.get(1n), 1n); - assert(a.get(2n**1000n - (2n**1000n - 1n)), 1n); - - test_map1("object", n); - test_map1("small_bigint", n); - test_map1("bigint", n); -} - -function test_weak_map() -{ - var a, i, n, tab, o, v, n2; - a = new WeakMap(); - n = 10; - tab = []; - for(i = 0; i < n; i++) { - v = { }; - if (i & 1) - o = Symbol("x" + i); - else - o = { id: i }; - tab[i] = [o, v]; - a.set(o, v); - } - o = null; - - n2 = 5; - for(i = 0; i < n2; i++) { - a.delete(tab[i][0]); - } - for(i = n2; i < n; i++) { - tab[i][0] = null; /* should remove the object from the WeakMap too */ - } - std.gc(); - /* the WeakMap should be empty here */ -} - -function test_weak_map_cycles() -{ - const weak1 = new WeakMap(); - const weak2 = new WeakMap(); - function createCyclicKey() { - const parent = {}; - const child = {parent}; - parent.child = child; - return child; - } - function testWeakMap() { - const cyclicKey = createCyclicKey(); - const valueOfCyclicKey = {}; - weak1.set(cyclicKey, valueOfCyclicKey); - weak2.set(valueOfCyclicKey, 1); - } - testWeakMap(); - // Force to free cyclicKey. - std.gc(); - // Here will cause sigsegv because [cyclicKey] and [valueOfCyclicKey] in [weak1] was free, - // but weak2's map record was not removed, and it's key refers [valueOfCyclicKey] which is free. - weak2.get({}); - std.gc(); -} - -function test_weak_ref() -{ - var w1, w2, o, i; - - for(i = 0; i < 2; i++) { - if (i == 0) - o = { }; - else - o = Symbol("x"); - w1 = new WeakRef(o); - assert(w1.deref(), o); - w2 = new WeakRef(o); - assert(w2.deref(), o); - - o = null; - assert(w1.deref(), undefined); - assert(w2.deref(), undefined); - std.gc(); - assert(w1.deref(), undefined); - assert(w2.deref(), undefined); - } -} - -function test_finalization_registry() -{ - { - let expected = {}; - let actual; - let finrec = new FinalizationRegistry(v => { actual = v }); - finrec.register({}, expected); - os.setTimeout(() => { - assert(actual, expected); - }, 0); - } - { - let expected = 42; - let actual; - let finrec = new FinalizationRegistry(v => { actual = v }); - finrec.register({}, expected); - os.setTimeout(() => { - assert(actual, expected); - }, 0); - } - std.gc(); -} - -function test_generator() -{ - function *f() { - var ret; - yield 1; - ret = yield 2; - assert(ret, "next_arg"); - return 3; - } - function *f2() { - yield 1; - yield 2; - return "ret_val"; - } - function *f1() { - var ret = yield *f2(); - assert(ret, "ret_val"); - return 3; - } - function *f3() { - var ret; - /* test stack consistency with nip_n to handle yield return + - * finally clause */ - try { - ret = 2 + (yield 1); - } catch(e) { - } finally { - ret++; - } - return ret; - } - var g, v; - g = f(); - v = g.next(); - assert(v.value === 1 && v.done === false); - v = g.next(); - assert(v.value === 2 && v.done === false); - v = g.next("next_arg"); - assert(v.value === 3 && v.done === true); - v = g.next(); - assert(v.value === undefined && v.done === true); - - g = f1(); - v = g.next(); - assert(v.value === 1 && v.done === false); - v = g.next(); - assert(v.value === 2 && v.done === false); - v = g.next(); - assert(v.value === 3 && v.done === true); - v = g.next(); - assert(v.value === undefined && v.done === true); - - g = f3(); - v = g.next(); - assert(v.value === 1 && v.done === false); - v = g.next(3); - assert(v.value === 6 && v.done === true); -} - -function rope_concat(n, dir) -{ - var i, s; - s = ""; - if (dir > 0) { - for(i = 0; i < n; i++) - s += String.fromCharCode(i & 0xffff); - } else { - for(i = n - 1; i >= 0; i--) - s = String.fromCharCode(i & 0xffff) + s; - } - - for(i = 0; i < n; i++) { - /* test before the assert to go faster */ - if (s.charCodeAt(i) != (i & 0xffff)) { - assert(s.charCodeAt(i), i & 0xffff); - } - } -} - -function test_rope() -{ - rope_concat(100000, 1); - rope_concat(100000, -1); -} - -function eval_error(eval_str, expected_error, level) -{ - var err = false; - var expected_pos, tab; - - tab = get_string_pos(eval_str); - - try { - eval(tab[0]); - } catch(e) { - err = true; - if (!(e instanceof expected_error)) { - throw_error("unexpected exception type"); - return; - } - check_error_pos(e, expected_error, tab[1], tab[2], level); - } - if (!err) { - throw_error("expected exception"); - } -} - -var poisoned_number = { - valueOf: function() { throw Error("poisoned number") }, -}; - -function test_line_column_numbers() -{ - var f, e, tab; - - /* The '@' character provides the expected position of the - error. It is removed before evaluating the string. */ - - /* parsing */ - eval_error("\n 123 @a ", SyntaxError); - eval_error("\n @/* ", SyntaxError); - eval_error("function f @a", SyntaxError); - /* currently regexp syntax errors point to the start of the regexp */ - eval_error("\n @/aaa]/u", SyntaxError); - - /* function definitions */ - - tab = get_string_pos("\n @function f() { }; f;"); - e = eval(tab[0]); - assert(e.lineNumber, tab[1]); - assert(e.columnNumber, tab[2]); - - /* errors */ - tab = get_string_pos('\n Error@("hello");'); - e = eval(tab[0]); - check_error_pos(e, Error, tab[1], tab[2]); - - eval_error('\n throw Error@("hello");', Error); - - /* operators */ - eval_error('\n 1 + 2 @* poisoned_number;', Error, 1); - eval_error('\n 1 + "café" @* poisoned_number;', Error, 1); - eval_error('\n 1 + 2 @** poisoned_number;', Error, 1); - eval_error('\n 2 * @+ poisoned_number;', Error, 1); - eval_error('\n 2 * @- poisoned_number;', Error, 1); - eval_error('\n 2 * @~ poisoned_number;', Error, 1); - eval_error('\n 2 * @++ poisoned_number;', Error, 1); - eval_error('\n 2 * @-- poisoned_number;', Error, 1); - eval_error('\n 2 * poisoned_number @++;', Error, 1); - eval_error('\n 2 * poisoned_number @--;', Error, 1); - - /* accessors */ - eval_error('\n 1 + null@[0];', TypeError); - eval_error('\n 1 + null @. abcd;', TypeError); - eval_error('\n 1 + null @( 1234 );', TypeError); - eval_error('var obj = { get a() { throw Error("test"); } }\n 1 + obj @. a;', - Error, 1); - eval_error('var obj = { set a(b) { throw Error("test"); } }\n obj @. a = 1;', - Error, 1); - - /* variables reference */ - eval_error('\n 1 + @not_def', ReferenceError, 0); - - /* assignments */ - eval_error('1 + (@not_def = 1)', ReferenceError, 0); - eval_error('1 + (@not_def += 2)', ReferenceError, 0); - eval_error('var a;\n 1 + (a @+= poisoned_number);', Error, 1); -} - -test(); -test_function(); -test_enum(); -test_array(); -test_string(); -test_math(); -test_number(); -test_eval(); -test_typed_array(); -test_json(); -test_date(); -test_regexp(); -test_symbol(); -test_map(); -test_weak_map(); -test_weak_map_cycles(); -test_weak_ref(); -test_finalization_registry(); -test_generator(); -test_rope(); -test_line_column_numbers(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_closure.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_closure.js deleted file mode 100755 index f5d4160e..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_closure.js +++ /dev/null @@ -1,221 +0,0 @@ -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (actual === expected) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -// load more elaborate version of assert if available -try { __loadScript("test_assert.js"); } catch(e) {} - -/*----------------*/ - -var log_str = ""; - -function log(str) -{ - log_str += str + ","; -} - -function f(a, b, c) -{ - var x = 10; - log("a="+a); - function g(d) { - function h() { - log("d=" + d); - log("x=" + x); - } - log("b=" + b); - log("c=" + c); - h(); - } - g(4); - return g; -} - -var g1 = f(1, 2, 3); -g1(5); - -assert(log_str, "a=1,b=2,c=3,d=4,x=10,b=2,c=3,d=5,x=10,", "closure1"); - -function test_closure1() -{ - function f2() - { - var val = 1; - - function set(a) { - val = a; - } - function get(a) { - return val; - } - return { "set": set, "get": get }; - } - - var obj = f2(); - obj.set(10); - var r; - r = obj.get(); - assert(r, 10, "closure2"); -} - -function test_closure2() -{ - var expr_func = function myfunc1(n) { - function myfunc2(n) { - return myfunc1(n - 1); - } - if (n == 0) - return 0; - else - return myfunc2(n); - }; - var r; - r = expr_func(1); - assert(r, 0, "expr_func"); -} - -function test_closure3() -{ - function fib(n) - { - if (n <= 0) - return 0; - else if (n == 1) - return 1; - else - return fib(n - 1) + fib(n - 2); - } - - var fib_func = function fib1(n) - { - if (n <= 0) - return 0; - else if (n == 1) - return 1; - else - return fib1(n - 1) + fib1(n - 2); - }; - - assert(fib(6), 8, "fib"); - assert(fib_func(6), 8, "fib_func"); -} - -function test_arrow_function() -{ - "use strict"; - - function f1() { - return (() => arguments)(); - } - function f2() { - return (() => this)(); - } - function f3() { - return (() => eval("this"))(); - } - function f4() { - return (() => eval("new.target"))(); - } - var a; - - a = f1(1, 2); - assert(a.length, 2); - assert(a[0] === 1 && a[1] === 2); - - assert(f2.call("this_val") === "this_val"); - assert(f3.call("this_val") === "this_val"); - assert(new f4() === f4); - - var o1 = { f() { return this; } }; - var o2 = { f() { - return (() => eval("super.f()"))(); - } }; - o2.__proto__ = o1; - - assert(o2.f() === o2); -} - -function test_with() -{ - var o1 = { x: "o1", y: "o1" }; - var x = "local"; - eval('var z="var_obj";'); - assert(z === "var_obj"); - with (o1) { - assert(x === "o1"); - assert(eval("x") === "o1"); - var f = function () { - o2 = { x: "o2" }; - with (o2) { - assert(x === "o2"); - assert(y === "o1"); - assert(z === "var_obj"); - assert(eval("x") === "o2"); - assert(eval("y") === "o1"); - assert(eval("z") === "var_obj"); - assert(eval('eval("x")') === "o2"); - } - }; - f(); - } -} - -function test_eval_closure() -{ - var tab; - - tab = []; - for(let i = 0; i < 3; i++) { - eval("tab.push(function g1() { return i; })"); - } - for(let i = 0; i < 3; i++) { - assert(tab[i]() === i); - } - - tab = []; - for(let i = 0; i < 3; i++) { - let f = function f() { - eval("tab.push(function g2() { return i; })"); - }; - f(); - } - for(let i = 0; i < 3; i++) { - assert(tab[i]() === i); - } -} - -function test_eval_const() -{ - const a = 1; - var success = false; - var f = function () { - eval("a = 1"); - }; - try { - f(); - } catch(e) { - success = (e instanceof TypeError); - } - assert(success); -} - -test_closure1(); -test_closure2(); -test_closure3(); -test_arrow_function(); -test_with(); -test_eval_closure(); -test_eval_const(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_cyclic_import.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_cyclic_import.js deleted file mode 100755 index bf51d9b1..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_cyclic_import.js +++ /dev/null @@ -1,12 +0,0 @@ -/*--- -negative: - phase: resolution - type: SyntaxError ----*/ -// FIXME(bnoordhuis) shouldn't throw SyntaxError but that's still better -// than segfaulting, see https://github.com/quickjs-ng/quickjs/issues/567 -import {assert} from "./assert.js" -import {f} from "./fixture_cyclic_import.js" -export {f} -export function g(x) { return x + 1 } -assert(f(1), 4) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_language.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_language.js deleted file mode 100755 index 5c51f0df..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_language.js +++ /dev/null @@ -1,692 +0,0 @@ -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (Object.is(actual, expected)) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -function assert_throws(expected_error, func) -{ - var err = false; - try { - func(); - } catch(e) { - err = true; - if (!(e instanceof expected_error)) { - throw Error("unexpected exception type"); - } - } - if (!err) { - throw Error("expected exception"); - } -} - -// load more elaborate version of assert if available -try { __loadScript("test_assert.js"); } catch(e) {} - -/*----------------*/ - -function test_op1() -{ - var r, a; - r = 1 + 2; - assert(r, 3, "1 + 2 === 3"); - - r = 1 - 2; - assert(r, -1, "1 - 2 === -1"); - - r = -1; - assert(r, -1, "-1 === -1"); - - r = +2; - assert(r, 2, "+2 === 2"); - - r = 2 * 3; - assert(r, 6, "2 * 3 === 6"); - - r = 4 / 2; - assert(r, 2, "4 / 2 === 2"); - - r = 4 % 3; - assert(r, 1, "4 % 3 === 3"); - - r = 4 << 2; - assert(r, 16, "4 << 2 === 16"); - - r = 1 << 0; - assert(r, 1, "1 << 0 === 1"); - - r = 1 << 31; - assert(r, -2147483648, "1 << 31 === -2147483648"); - - r = 1 << 32; - assert(r, 1, "1 << 32 === 1"); - - r = (1 << 31) < 0; - assert(r, true, "(1 << 31) < 0 === true"); - - r = -4 >> 1; - assert(r, -2, "-4 >> 1 === -2"); - - r = -4 >>> 1; - assert(r, 0x7ffffffe, "-4 >>> 1 === 0x7ffffffe"); - - r = 1 & 1; - assert(r, 1, "1 & 1 === 1"); - - r = 0 | 1; - assert(r, 1, "0 | 1 === 1"); - - r = 1 ^ 1; - assert(r, 0, "1 ^ 1 === 0"); - - r = ~1; - assert(r, -2, "~1 === -2"); - - r = !1; - assert(r, false, "!1 === false"); - - assert((1 < 2), true, "(1 < 2) === true"); - - assert((2 > 1), true, "(2 > 1) === true"); - - assert(('b' > 'a'), true, "('b' > 'a') === true"); - - assert(2 ** 8, 256, "2 ** 8 === 256"); -} - -function test_cvt() -{ - assert((NaN | 0) === 0); - assert((Infinity | 0) === 0); - assert(((-Infinity) | 0) === 0); - assert(("12345" | 0) === 12345); - assert(("0x12345" | 0) === 0x12345); - assert(((4294967296 * 3 - 4) | 0) === -4); - - assert(("12345" >>> 0) === 12345); - assert(("0x12345" >>> 0) === 0x12345); - assert((NaN >>> 0) === 0); - assert((Infinity >>> 0) === 0); - assert(((-Infinity) >>> 0) === 0); - assert(((4294967296 * 3 - 4) >>> 0) === (4294967296 - 4)); - assert((19686109595169230000).toString() === "19686109595169230000"); -} - -function test_eq() -{ - assert(null == undefined); - assert(undefined == null); - assert(true == 1); - assert(0 == false); - assert("" == 0); - assert("123" == 123); - assert("122" != 123); - assert((new Number(1)) == 1); - assert(2 == (new Number(2))); - assert((new String("abc")) == "abc"); - assert({} != "abc"); -} - -function test_inc_dec() -{ - var a, r; - - a = 1; - r = a++; - assert(r === 1 && a === 2, true, "++"); - - a = 1; - r = ++a; - assert(r === 2 && a === 2, true, "++"); - - a = 1; - r = a--; - assert(r === 1 && a === 0, true, "--"); - - a = 1; - r = --a; - assert(r === 0 && a === 0, true, "--"); - - a = {x:true}; - a.x++; - assert(a.x, 2, "++"); - - a = {x:true}; - a.x--; - assert(a.x, 0, "--"); - - a = [true]; - a[0]++; - assert(a[0], 2, "++"); - - a = {x:true}; - r = a.x++; - assert(r === 1 && a.x === 2, true, "++"); - - a = {x:true}; - r = a.x--; - assert(r === 1 && a.x === 0, true, "--"); - - a = [true]; - r = a[0]++; - assert(r === 1 && a[0] === 2, true, "++"); - - a = [true]; - r = a[0]--; - assert(r === 1 && a[0] === 0, true, "--"); -} - -function F(x) -{ - this.x = x; -} - -function test_op2() -{ - var a, b; - a = new Object; - a.x = 1; - assert(a.x, 1, "new"); - b = new F(2); - assert(b.x, 2, "new"); - - a = {x : 2}; - assert(("x" in a), true, "in"); - assert(("y" in a), false, "in"); - - a = {}; - assert((a instanceof Object), true, "instanceof"); - assert((a instanceof String), false, "instanceof"); - - assert((typeof 1), "number", "typeof"); - assert((typeof Object), "function", "typeof"); - assert((typeof null), "object", "typeof"); - assert((typeof unknown_var), "undefined", "typeof"); - - a = {x: 1, if: 2, async: 3}; - assert(a.if === 2); - assert(a.async === 3); -} - -function test_delete() -{ - var a, err; - - a = {x: 1, y: 1}; - assert((delete a.x), true, "delete"); - assert(("x" in a), false, "delete"); - - /* the following are not tested by test262 */ - assert(delete "abc"[100], true); - - err = false; - try { - delete null.a; - } catch(e) { - err = (e instanceof TypeError); - } - assert(err, true, "delete"); - - err = false; - try { - a = { f() { delete super.a; } }; - a.f(); - } catch(e) { - err = (e instanceof ReferenceError); - } - assert(err, true, "delete"); -} - -function test_constructor() -{ - function *G() {} - let ex - try { new G() } catch (ex_) { ex = ex_ } - assert(ex instanceof TypeError) - assert(ex.message, "G is not a constructor") -} - -function test_prototype() -{ - var f = function f() { }; - assert(f.prototype.constructor, f, "prototype"); - - var g = function g() { }; - /* QuickJS bug */ - Object.defineProperty(g, "prototype", { writable: false }); - assert(g.prototype.constructor, g, "prototype"); -} - -function test_arguments() -{ - function f2() { - assert(arguments.length, 2, "arguments"); - assert(arguments[0], 1, "arguments"); - assert(arguments[1], 3, "arguments"); - } - f2(1, 3); -} - -function test_class() -{ - var o; - class C { - constructor() { - this.x = 10; - } - f() { - return 1; - } - static F() { - return -1; - } - get y() { - return 12; - } - }; - class D extends C { - constructor() { - super(); - this.z = 20; - } - g() { - return 2; - } - static G() { - return -2; - } - h() { - return super.f(); - } - static H() { - return super["F"](); - } - } - - assert(C.F() === -1); - assert(Object.getOwnPropertyDescriptor(C.prototype, "y").get.name === "get y"); - - o = new C(); - assert(o.f() === 1); - assert(o.x === 10); - - assert(D.F() === -1); - assert(D.G() === -2); - assert(D.H() === -1); - - o = new D(); - assert(o.f() === 1); - assert(o.g() === 2); - assert(o.x === 10); - assert(o.z === 20); - assert(o.h() === 1); - - /* test class name scope */ - var E1 = class E { static F() { return E; } }; - assert(E1 === E1.F()); - - class S { - static x = 42; - static y = S.x; - static z = this.x; - } - assert(S.x === 42); - assert(S.y === 42); - assert(S.z === 42); - - class P { - get = () => "123"; - static() { return 42; } - } - assert(new P().get() === "123"); - assert(new P().static() === 42); -}; - -function test_template() -{ - var a, b; - b = 123; - a = `abc${b}d`; - assert(a, "abc123d"); - - a = String.raw `abc${b}d`; - assert(a, "abc123d"); - - a = "aaa"; - b = "bbb"; - assert(`aaa${a, b}ccc`, "aaabbbccc"); -} - -function test_template_skip() -{ - var a = "Bar"; - var { b = `${a + `a${a}` }baz` } = {}; - assert(b, "BaraBarbaz"); -} - -function test_object_literal() -{ - var x = 0, get = 1, set = 2, async = 3; - a = { get: 2, set: 3, async: 4, get a(){ return this.get} }; - assert(JSON.stringify(a), '{"get":2,"set":3,"async":4,"a":2}'); - assert(a.a === 2); - - a = { x, get, set, async }; - assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}'); -} - -function test_regexp_skip() -{ - var a, b; - [a, b = /abc\(/] = [1]; - assert(a === 1); - - [a, b =/abc\(/] = [2]; - assert(a === 2); -} - -function test_labels() -{ - do x: { break x; } while(0); - if (1) - x: { break x; } - else - x: { break x; } - with ({}) x: { break x; }; - while (0) x: { break x; }; -} - -function test_labels2() -{ - while (1) label: break - var i = 0 - while (i < 3) label: { - if (i > 0) - break - i++ - } - assert(i, 1) - for (;;) label: break - for (i = 0; i < 3; i++) label: { - if (i > 0) - break - } - assert(i, 1) -} - -function test_destructuring() -{ - function * g () { return 0; }; - var [x] = g(); - assert(x, void 0); -} - -function test_spread() -{ - var x; - x = [1, 2, ...[3, 4]]; - assert(x.toString(), "1,2,3,4"); - - x = [ ...[ , ] ]; - assert(Object.getOwnPropertyNames(x).toString(), "0,length"); -} - -function test_function_length() -{ - assert( ((a, b = 1, c) => {}).length, 1); - assert( (([a,b]) => {}).length, 1); - assert( (({a,b}) => {}).length, 1); - assert( ((c, [a,b] = 1, d) => {}).length, 1); -} - -function test_argument_scope() -{ - var f; - var c = "global"; - - (function() { - "use strict"; - // XXX: node only throws in strict mode - f = function(a = eval("var arguments")) {}; - assert_throws(SyntaxError, f); - })(); - - f = function(a = eval("1"), b = arguments[0]) { return b; }; - assert(f(12), 12); - - f = function(a, b = arguments[0]) { return b; }; - assert(f(12), 12); - - f = function(a, b = () => arguments) { return b; }; - assert(f(12)()[0], 12); - - f = function(a = eval("1"), b = () => arguments) { return b; }; - assert(f(12)()[0], 12); - - (function() { - "use strict"; - f = function(a = this) { return a; }; - assert(f.call(123), 123); - - f = function f(a = f) { return a; }; - assert(f(), f); - - f = function f(a = eval("f")) { return a; }; - assert(f(), f); - })(); - - f = (a = eval("var c = 1"), probe = () => c) => { - var c = 2; - assert(c, 2); - assert(probe(), 1); - } - f(); - - f = (a = eval("var arguments = 1"), probe = () => arguments) => { - var arguments = 2; - assert(arguments, 2); - assert(probe(), 1); - } - f(); - - f = function f(a = eval("var c = 1"), b = c, probe = () => c) { - assert(b, 1); - assert(c, 1); - assert(probe(), 1) - } - f(); - - assert(c, "global"); - f = function f(a, b = c, probe = () => c) { - eval("var c = 1"); - assert(c, 1); - assert(b, "global"); - assert(probe(), "global") - } - f(); - assert(c, "global"); - - f = function f(a = eval("var c = 1"), probe = (d = eval("c")) => d) { - assert(probe(), 1) - } - f(); -} - -function test_function_expr_name() -{ - var f; - - /* non strict mode test : assignment to the function name silently - fails */ - - f = function myfunc() { - myfunc = 1; - return myfunc; - }; - assert(f(), f); - - f = function myfunc() { - myfunc = 1; - (() => { - myfunc = 1; - })(); - return myfunc; - }; - assert(f(), f); - - f = function myfunc() { - eval("myfunc = 1"); - return myfunc; - }; - assert(f(), f); - - /* strict mode test : assignment to the function name raises a - TypeError exception */ - - f = function myfunc() { - "use strict"; - myfunc = 1; - }; - assert_throws(TypeError, f); - - f = function myfunc() { - "use strict"; - (() => { - myfunc = 1; - })(); - }; - assert_throws(TypeError, f); - - f = function myfunc() { - "use strict"; - eval("myfunc = 1"); - }; - assert_throws(TypeError, f); -} - -function test_parse_semicolon() -{ - /* 'yield' or 'await' may not be considered as a token if the - previous ';' is missing */ - function *f() - { - function func() { - } - yield 1; - var h = x => x + 1 - yield 2; - } - async function g() - { - function func() { - } - await 1; - var h = x => x + 1 - await 2; - } -} - -function test_parse_arrow_function() -{ - assert(typeof eval("() => {}\n() => {}"), "function"); - assert(eval("() => {}\n+1"), 1); - assert(typeof eval("x => {}\n() => {}"), "function"); - assert(typeof eval("async () => {}\n() => {}"), "function"); - assert(typeof eval("async x => {}\n() => {}"), "function"); -} - -/* optional chaining tests not present in test262 */ -function test_optional_chaining() -{ - var a, z; - z = null; - a = { b: { c: 2 } }; - assert(delete z?.b.c, true); - assert(delete a?.b.c, true); - assert(JSON.stringify(a), '{"b":{}}', "optional chaining delete"); - - a = { b: { c: 2 } }; - assert(delete z?.b["c"], true); - assert(delete a?.b["c"], true); - assert(JSON.stringify(a), '{"b":{}}'); - - a = { - b() { return this._b; }, - _b: { c: 42 } - }; - - assert((a?.b)().c, 42); - - assert((a?.["b"])().c, 42); -} - -function test_unicode_ident() -{ - var õ = 3; - assert(typeof õ, "undefined"); -} - -/* check global variable optimization */ -function test_global_var_opt() -{ - var v2; - (1, eval)('var gvar1'); /* create configurable global variables */ - - gvar1 = 1; - Object.defineProperty(globalThis, "gvar1", { writable: false }); - gvar1 = 2; - assert(gvar1, 1); - - Object.defineProperty(globalThis, "gvar1", { get: function() { return "hello" }, - set: function(v) { v2 = v; } }); - assert(gvar1, "hello"); - gvar1 = 3; - assert(v2, 3); - - Object.defineProperty(globalThis, "gvar1", { value: 4, writable: true, configurable: true }); - assert(gvar1, 4); - gvar1 = 6; - - delete gvar1; - assert_throws(ReferenceError, function() { return gvar1 }); - gvar1 = 5; - assert(gvar1, 5); -} - -test_op1(); -test_cvt(); -test_eq(); -test_inc_dec(); -test_op2(); -test_constructor(); -test_delete(); -test_prototype(); -test_arguments(); -test_class(); -test_template(); -test_template_skip(); -test_object_literal(); -test_regexp_skip(); -test_labels(); -test_labels2(); -test_destructuring(); -test_spread(); -test_function_length(); -test_argument_scope(); -test_function_expr_name(); -test_parse_semicolon(); -test_optional_chaining(); -test_parse_arrow_function(); -test_unicode_ident(); -test_global_var_opt(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_loop.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_loop.js deleted file mode 100755 index 8f1934d3..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_loop.js +++ /dev/null @@ -1,402 +0,0 @@ -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (actual === expected) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -// load more elaborate version of assert if available -try { __loadScript("test_assert.js"); } catch(e) {} - -/*----------------*/ - -function test_while() -{ - var i, c; - i = 0; - c = 0; - while (i < 3) { - c++; - i++; - } - assert(c === 3); -} - -function test_while_break() -{ - var i, c; - i = 0; - c = 0; - while (i < 3) { - c++; - if (i == 1) - break; - i++; - } - assert(c === 2 && i === 1); -} - -function test_do_while() -{ - var i, c; - i = 0; - c = 0; - do { - c++; - i++; - } while (i < 3); - assert(c === 3 && i === 3); -} - -function test_for() -{ - var i, c; - c = 0; - for(i = 0; i < 3; i++) { - c++; - } - assert(c === 3 && i === 3); - - c = 0; - for(var j = 0; j < 3; j++) { - c++; - } - assert(c === 3 && j === 3); -} - -function test_for_in() -{ - var i, tab, a, b; - - tab = []; - for(i in {x:1, y: 2}) { - tab.push(i); - } - assert(tab.toString(), "x,y", "for_in"); - - /* prototype chain test */ - a = {x:2, y: 2, "1": 3}; - b = {"4" : 3 }; - Object.setPrototypeOf(a, b); - tab = []; - for(i in a) { - tab.push(i); - } - assert(tab.toString(), "1,x,y,4", "for_in"); - - /* non enumerable properties hide enumerables ones in the - prototype chain */ - a = {y: 2, "1": 3}; - Object.defineProperty(a, "x", { value: 1 }); - b = {"x" : 3 }; - Object.setPrototypeOf(a, b); - tab = []; - for(i in a) { - tab.push(i); - } - assert(tab.toString(), "1,y", "for_in"); - - /* array optimization */ - a = []; - for(i = 0; i < 10; i++) - a.push(i); - tab = []; - for(i in a) { - tab.push(i); - } - assert(tab.toString(), "0,1,2,3,4,5,6,7,8,9", "for_in"); - - /* iterate with a field */ - a={x:0}; - tab = []; - for(a.x in {x:1, y: 2}) { - tab.push(a.x); - } - assert(tab.toString(), "x,y", "for_in"); - - /* iterate with a variable field */ - a=[0]; - tab = []; - for(a[0] in {x:1, y: 2}) { - tab.push(a[0]); - } - assert(tab.toString(), "x,y", "for_in"); - - /* variable definition in the for in */ - tab = []; - for(var j in {x:1, y: 2}) { - tab.push(j); - } - assert(tab.toString(), "x,y", "for_in"); - - /* variable assigment in the for in */ - tab = []; - for(var k = 2 in {x:1, y: 2}) { - tab.push(k); - } - assert(tab.toString(), "x,y", "for_in"); -} - -function test_for_in2() -{ - var i, tab; - tab = []; - for(i in {x:1, y: 2, z:3}) { - if (i === "y") - continue; - tab.push(i); - } - assert(tab.toString() == "x,z"); - - tab = []; - for(i in {x:1, y: 2, z:3}) { - if (i === "z") - break; - tab.push(i); - } - assert(tab.toString() == "x,y"); -} - -function test_for_in_proxy() { - let removed_key = ""; - let target = {} - let proxy = new Proxy(target, { - ownKeys: function() { - return ["a", "b", "c"]; - }, - getOwnPropertyDescriptor: function(target, key) { - if (removed_key != "" && key == removed_key) - return undefined; - else - return { enumerable: true, configurable: true, value: this[key] }; - } - }); - let str = ""; - for(let o in proxy) { - str += " " + o; - if (o == "a") - removed_key = "b"; - } - assert(str == " a c"); -} - -function test_for_break() -{ - var i, c; - c = 0; - L1: for(i = 0; i < 3; i++) { - c++; - if (i == 0) - continue; - while (1) { - break L1; - } - } - assert(c === 2 && i === 1); -} - -function test_switch1() -{ - var i, a, s; - s = ""; - for(i = 0; i < 3; i++) { - a = "?"; - switch(i) { - case 0: - a = "a"; - break; - case 1: - a = "b"; - break; - default: - a = "c"; - break; - } - s += a; - } - assert(s === "abc" && i === 3); -} - -function test_switch2() -{ - var i, a, s; - s = ""; - for(i = 0; i < 4; i++) { - a = "?"; - switch(i) { - case 0: - a = "a"; - break; - case 1: - a = "b"; - break; - case 2: - continue; - default: - a = "" + i; - break; - } - s += a; - } - assert(s === "ab3" && i === 4); -} - -function test_try_catch1() -{ - try { - throw "hello"; - } catch (e) { - assert(e, "hello", "catch"); - return; - } - assert(false, "catch"); -} - -function test_try_catch2() -{ - var a; - try { - a = 1; - } catch (e) { - a = 2; - } - assert(a, 1, "catch"); -} - -function test_try_catch3() -{ - var s; - s = ""; - try { - s += "t"; - } catch (e) { - s += "c"; - } finally { - s += "f"; - } - assert(s, "tf", "catch"); -} - -function test_try_catch4() -{ - var s; - s = ""; - try { - s += "t"; - throw "c"; - } catch (e) { - s += e; - } finally { - s += "f"; - } - assert(s, "tcf", "catch"); -} - -function test_try_catch5() -{ - var s; - s = ""; - for(;;) { - try { - s += "t"; - break; - s += "b"; - } finally { - s += "f"; - } - } - assert(s, "tf", "catch"); -} - -function test_try_catch6() -{ - function f() { - try { - s += 't'; - return 1; - } finally { - s += "f"; - } - } - var s = ""; - assert(f() === 1); - assert(s, "tf", "catch6"); -} - -function test_try_catch7() -{ - var s; - s = ""; - - try { - try { - s += "t"; - throw "a"; - } finally { - s += "f"; - } - } catch(e) { - s += e; - } finally { - s += "g"; - } - assert(s, "tfag", "catch"); -} - -function test_try_catch8() -{ - var i, s; - - s = ""; - for(var i in {x:1, y:2}) { - try { - s += i; - throw "a"; - } catch (e) { - s += e; - } finally { - s += "f"; - } - } - assert(s === "xafyaf"); -} - -function test_cyclic_labels() -{ - /* just check that it compiles without a crash */ - for (;;) { - l: break l; - l: break l; - l: break l; - } -} - -test_while(); -test_while_break(); -test_do_while(); -test_for(); -test_for_break(); -test_switch1(); -test_switch2(); -test_for_in(); -test_for_in2(); -test_for_in_proxy(); - -test_try_catch1(); -test_try_catch2(); -test_try_catch3(); -test_try_catch4(); -test_try_catch5(); -test_try_catch6(); -test_try_catch7(); -test_try_catch8(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_std.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_std.js deleted file mode 100755 index 3debe403..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_std.js +++ /dev/null @@ -1,336 +0,0 @@ -#! (shebang test) -import * as std from "std"; -import * as os from "os"; - -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (Object.is(actual, expected)) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -// load more elaborate version of assert if available -try { std.loadScript("test_assert.js"); } catch(e) {} - -/*----------------*/ - -function test_printf() -{ - assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc"); - assert(std.sprintf("%010d", 123), "0000000123"); - assert(std.sprintf("%x", -2), "fffffffe"); - assert(std.sprintf("%lx", -2), "fffffffffffffffe"); - assert(std.sprintf("%10.1f", 2.1), " 2.1"); - assert(std.sprintf("%*.*f", 10, 2, -2.13), " -2.13"); - assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff"); -} - -function test_file1() -{ - var f, len, str, size, buf, ret, i, str1; - - f = std.tmpfile(); - str = "hello world\n"; - f.puts(str); - - f.seek(0, std.SEEK_SET); - str1 = f.readAsString(); - assert(str1 === str); - - f.seek(0, std.SEEK_END); - size = f.tell(); - assert(size === str.length); - - f.seek(0, std.SEEK_SET); - - buf = new Uint8Array(size); - ret = f.read(buf.buffer, 0, size); - assert(ret === size); - for(i = 0; i < size; i++) - assert(buf[i] === str.charCodeAt(i)); - - f.close(); -} - -function test_file2() -{ - var f, str, i, size; - f = std.tmpfile(); - str = "hello world\n"; - size = str.length; - for(i = 0; i < size; i++) - f.putByte(str.charCodeAt(i)); - f.seek(0, std.SEEK_SET); - for(i = 0; i < size; i++) { - assert(str.charCodeAt(i) === f.getByte()); - } - assert(f.getByte() === -1); - f.close(); -} - -function test_getline() -{ - var f, line, line_count, lines, i; - - lines = ["hello world", "line 1", "line 2" ]; - f = std.tmpfile(); - for(i = 0; i < lines.length; i++) { - f.puts(lines[i], "\n"); - } - - f.seek(0, std.SEEK_SET); - assert(!f.eof()); - line_count = 0; - for(;;) { - line = f.getline(); - if (line === null) - break; - assert(line == lines[line_count]); - line_count++; - } - assert(f.eof()); - assert(line_count === lines.length); - - f.close(); -} - -function test_popen() -{ - var str, f, fname = "tmp_file.txt"; - var content = "hello world"; - - f = std.open(fname, "w"); - f.puts(content); - f.close(); - - /* test loadFile */ - assert(std.loadFile(fname), content); - - /* execute the 'cat' shell command */ - f = std.popen("cat " + fname, "r"); - str = f.readAsString(); - f.close(); - - assert(str, content); - - os.remove(fname); -} - -function test_ext_json() -{ - var expected, input, obj; - expected = '{"x":false,"y":true,"z2":null,"a":[1,8,160],"b":"abc\\u000bd","s":"str"}'; - input = `{ "x":false, /*comments are allowed */ - "y":true, // also a comment - z2:null, // unquoted property names - "a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal - "b": "ab\ -c\\vd", // multi-line strings, '\v' escape - "s":'str',} // trailing comma in objects and arrays, single quoted string - `; - obj = std.parseExtJSON(input); - assert(JSON.stringify(obj), expected); - - obj = std.parseExtJSON('[Infinity, +Infinity, -Infinity, NaN, +NaN, -NaN, .1, -.2]'); - assert(obj[0], Infinity); - assert(obj[1], Infinity); - assert(obj[2], -Infinity); - assert(obj[3], NaN); - assert(obj[4], NaN); - assert(obj[5], NaN); - assert(obj[6], 0.1); - assert(obj[7], -0.2); -} - -function test_os() -{ - var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path; - - const stdinIsTTY = !os.exec(["/bin/sh", "-c", "test -t 0"], { usePath: false }); - - assert(os.isatty(0), stdinIsTTY, `isatty(STDIN)`); - - fdir = "test_tmp_dir"; - fname = "tmp_file.txt"; - fpath = fdir + "/" + fname; - link_path = fdir + "/test_link"; - - os.remove(link_path); - os.remove(fpath); - os.remove(fdir); - - err = os.mkdir(fdir, 0o755); - assert(err === 0); - - fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC); - assert(fd >= 0); - - buf = new Uint8Array(10); - for(i = 0; i < buf.length; i++) - buf[i] = i; - assert(os.write(fd, buf.buffer, 0, buf.length) === buf.length); - - assert(os.seek(fd, 0, std.SEEK_SET) === 0); - buf2 = new Uint8Array(buf.length); - assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length); - - for(i = 0; i < buf.length; i++) - assert(buf[i] == buf2[i]); - - if (typeof BigInt !== "undefined") { - assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6)); - assert(os.read(fd, buf2.buffer, 0, 1) === 1); - assert(buf[6] == buf2[0]); - } - - assert(os.close(fd) === 0); - - [files, err] = os.readdir(fdir); - assert(err, 0); - assert(files.indexOf(fname) >= 0); - - fdate = 10000; - - err = os.utimes(fpath, fdate, fdate); - assert(err, 0); - - [st, err] = os.stat(fpath); - assert(err, 0); - assert(st.mode & os.S_IFMT, os.S_IFREG); - assert(st.mtime, fdate); - - err = os.symlink(fname, link_path); - assert(err === 0); - - [st, err] = os.lstat(link_path); - assert(err, 0); - assert(st.mode & os.S_IFMT, os.S_IFLNK); - - [buf, err] = os.readlink(link_path); - assert(err, 0); - assert(buf, fname); - - assert(os.remove(link_path) === 0); - - [buf, err] = os.getcwd(); - assert(err, 0); - - [buf2, err] = os.realpath("."); - assert(err, 0); - - assert(buf, buf2); - - assert(os.remove(fpath) === 0); - - fd = os.open(fpath, os.O_RDONLY); - assert(fd < 0); - - assert(os.remove(fdir) === 0); -} - -function test_os_exec() -{ - var ret, fds, pid, f, status; - - ret = os.exec(["true"]); - assert(ret, 0); - - ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false }); - assert(ret, 1); - - fds = os.pipe(); - pid = os.exec(["sh", "-c", "echo $FOO"], { - stdout: fds[1], - block: false, - env: { FOO: "hello" }, - } ); - assert(pid >= 0); - os.close(fds[1]); /* close the write end (as it is only in the child) */ - f = std.fdopen(fds[0], "r"); - assert(f.getline(), "hello"); - assert(f.getline(), null); - f.close(); - [ret, status] = os.waitpid(pid, 0); - assert(ret, pid); - assert(status & 0x7f, 0); /* exited */ - assert(status >> 8, 0); /* exit code */ - - pid = os.exec(["cat"], { block: false } ); - assert(pid >= 0); - os.kill(pid, os.SIGTERM); - [ret, status] = os.waitpid(pid, 0); - assert(ret, pid); - assert(status !== 0, true, `expect nonzero exit code (got ${status})`); - assert(status & 0x7f, os.SIGTERM); -} - -function test_timer() -{ - var th, i; - - /* just test that a timer can be inserted and removed */ - th = []; - for(i = 0; i < 3; i++) - th[i] = os.setTimeout(function () { }, 1000); - for(i = 0; i < 3; i++) - os.clearTimeout(th[i]); -} - -/* test closure variable handling when freeing asynchronous - function */ -function test_async_gc() -{ - (async function run () { - let obj = {} - - let done = () => { - obj - std.gc(); - } - - Promise.resolve().then(done) - - const p = new Promise(() => {}) - - await p - })(); -} - -/* check that the promise async rejection handler is not invoked when - the rejection is handled not too late after the promise - rejection. */ -function test_async_promise_rejection() -{ - var counter = 0; - var p1, p2, p3; - p1 = Promise.reject(); - p2 = Promise.reject(); - p3 = Promise.resolve(); - p1.catch(() => counter++); - p2.catch(() => counter++); - p3.then(() => counter++) - os.setTimeout(() => { assert(counter, 3) }, 10); -} - -test_printf(); -test_file1(); -test_file2(); -test_getline(); -test_popen(); -test_os(); -test_os_exec(); -test_timer(); -test_ext_json(); -test_async_gc(); -test_async_promise_rejection(); - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_worker.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_worker.js deleted file mode 100755 index 4b52bf82..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_worker.js +++ /dev/null @@ -1,62 +0,0 @@ -/* os.Worker API test */ -import * as std from "std"; -import * as os from "os"; - -function assert(actual, expected, message) { - if (arguments.length == 1) - expected = true; - - if (actual === expected) - return; - - if (actual !== null && expected !== null - && typeof actual == 'object' && typeof expected == 'object' - && actual.toString() === expected.toString()) - return; - - throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); -} - -var worker; - -function test_worker() -{ - var counter; - - worker = new os.Worker("./test_worker_module.js"); - - counter = 0; - worker.onmessage = function (e) { - var ev = e.data; -// print("recv", JSON.stringify(ev)); - switch(ev.type) { - case "num": - assert(ev.num, counter); - counter++; - if (counter == 10) { - /* test SharedArrayBuffer modification */ - let sab = new SharedArrayBuffer(10); - let buf = new Uint8Array(sab); - worker.postMessage({ type: "sab", buf: buf }); - } - break; - case "sab_done": - { - let buf = ev.buf; - /* check that the SharedArrayBuffer was modified */ - assert(buf[2], 10); - worker.postMessage({ type: "abort" }); - } - break; - case "done": - /* terminate */ - worker.onmessage = null; - break; - } - }; -} - - -test_worker(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_worker_module.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_worker_module.js deleted file mode 100755 index ddf8e400..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests/test_worker_module.js +++ /dev/null @@ -1,32 +0,0 @@ -/* Worker code for test_worker.js */ -import * as std from "std"; -import * as os from "os"; - -var parent = os.Worker.parent; - -function handle_msg(e) { - var ev = e.data; - // print("child_recv", JSON.stringify(ev)); - switch(ev.type) { - case "abort": - parent.postMessage({ type: "done" }); - parent.onmessage = null; /* terminate the worker */ - break; - case "sab": - /* modify the SharedArrayBuffer */ - ev.buf[2] = 10; - parent.postMessage({ type: "sab_done", buf: ev.buf }); - break; - } -} - -function worker_main() { - var i; - - parent.onmessage = handle_msg; - for(i = 0; i < 10; i++) { - parent.postMessage({ type: "num", num: i }); - } -} - -worker_main(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_download.sh b/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_download.sh deleted file mode 100755 index ef8b30d2..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_download.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -set -e - -version="16.0.0" -emoji_version="16.0" -url="ftp://ftp.unicode.org/Public" - -files="CaseFolding.txt DerivedNormalizationProps.txt PropList.txt \ -SpecialCasing.txt CompositionExclusions.txt ScriptExtensions.txt \ -UnicodeData.txt DerivedCoreProperties.txt NormalizationTest.txt Scripts.txt \ -PropertyValueAliases.txt" - -mkdir -p unicode - -for f in $files; do - g="${url}/${version}/ucd/${f}" - wget $g -O unicode/$f -done - -wget "${url}/${version}/ucd/emoji/emoji-data.txt" -O unicode/emoji-data.txt - -wget "${url}/emoji/${emoji_version}/emoji-sequences.txt" -O unicode/emoji-sequences.txt -wget "${url}/emoji/${emoji_version}/emoji-zwj-sequences.txt" -O unicode/emoji-zwj-sequences.txt diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen.c deleted file mode 100755 index c793ba1e..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen.c +++ /dev/null @@ -1,3779 +0,0 @@ -/* - * Generation of Unicode tables - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cutils.h" - -uint32_t total_tables; -uint32_t total_table_bytes; -uint32_t total_index; -uint32_t total_index_bytes; - -/* define it to be able to test unicode.c */ -//#define USE_TEST -/* profile tests */ -//#define PROFILE - -//#define DUMP_CASE_CONV_TABLE -//#define DUMP_TABLE_SIZE -//#define DUMP_CC_TABLE -//#define DUMP_DECOMP_TABLE -//#define DUMP_CASE_FOLDING_SPECIAL_CASES - -/* Ideas: - - Generalize run length encoding + index for all tables - - remove redundant tables for ID_start, ID_continue, Case_Ignorable, Cased - - Case conversion: - - use a single entry for consecutive U/LF runs - - allow EXT runs of length > 1 - - Decomposition: - - Greek lower case (+1f10/1f10) ? - - allow holes in B runs - - suppress more upper / lower case redundancy -*/ - -#ifdef USE_TEST -#include "libunicode.c" -#endif - -#define CHARCODE_MAX 0x10ffff -#define CC_LEN_MAX 3 - -void *mallocz(size_t size) -{ - void *ptr; - ptr = malloc(size); - memset(ptr, 0, size); - return ptr; -} - -const char *get_field(const char *p, int n) -{ - int i; - for(i = 0; i < n; i++) { - while (*p != ';' && *p != '\0') - p++; - if (*p == '\0') - return NULL; - p++; - } - return p; -} - -const char *get_field_buf(char *buf, size_t buf_size, const char *p, int n) -{ - char *q; - p = get_field(p, n); - q = buf; - while (*p != ';' && *p != '\0') { - if ((q - buf) < buf_size - 1) - *q++ = *p; - p++; - } - *q = '\0'; - return buf; -} - -void add_char(int **pbuf, int *psize, int *plen, int c) -{ - int len, size, *buf; - buf = *pbuf; - size = *psize; - len = *plen; - if (len >= size) { - size = *psize; - size = max_int(len + 1, size * 3 / 2); - buf = realloc(buf, sizeof(buf[0]) * size); - *pbuf = buf; - *psize = size; - } - buf[len++] = c; - *plen = len; -} - -int *get_field_str(int *plen, const char *str, int n) -{ - const char *p; - int *buf, len, size; - p = get_field(str, n); - if (!p) { - *plen = 0; - return NULL; - } - len = 0; - size = 0; - buf = NULL; - for(;;) { - while (isspace(*p)) - p++; - if (!isxdigit(*p)) - break; - add_char(&buf, &size, &len, strtoul(p, (char **)&p, 16)); - } - *plen = len; - return buf; -} - -char *get_line(char *buf, int buf_size, FILE *f) -{ - int len; - if (!fgets(buf, buf_size, f)) - return NULL; - len = strlen(buf); - if (len > 0 && buf[len - 1] == '\n') - buf[len - 1] = '\0'; - return buf; -} - -typedef struct REString { - struct REString *next; - uint32_t hash; - uint32_t len; - uint32_t flags; - uint32_t buf[]; -} REString; - -typedef struct { - uint32_t n_strings; - uint32_t hash_size; - int hash_bits; - REString **hash_table; -} REStringList; - -static uint32_t re_string_hash(int len, const uint32_t *buf) -{ - int i; - uint32_t h; - h = 1; - for(i = 0; i < len; i++) - h = h * 263 + buf[i]; - return h * 0x61C88647; -} - -static void re_string_list_init(REStringList *s) -{ - s->n_strings = 0; - s->hash_size = 0; - s->hash_bits = 0; - s->hash_table = NULL; -} - -static __maybe_unused void re_string_list_free(REStringList *s) -{ - REString *p, *p_next; - int i; - for(i = 0; i < s->hash_size; i++) { - for(p = s->hash_table[i]; p != NULL; p = p_next) { - p_next = p->next; - free(p); - } - } - free(s->hash_table); -} - -static void lre_print_char(int c, BOOL is_range) -{ - if (c == '\'' || c == '\\' || - (is_range && (c == '-' || c == ']'))) { - printf("\\%c", c); - } else if (c >= ' ' && c <= 126) { - printf("%c", c); - } else { - printf("\\u{%04x}", c); - } -} - -static __maybe_unused void re_string_list_dump(const char *str, const REStringList *s) -{ - REString *p; - int i, j, k; - - printf("%s:\n", str); - - j = 0; - for(i = 0; i < s->hash_size; i++) { - for(p = s->hash_table[i]; p != NULL; p = p->next) { - printf(" %d/%d: '", j, s->n_strings); - for(k = 0; k < p->len; k++) { - lre_print_char(p->buf[k], FALSE); - } - printf("'\n"); - j++; - } - } -} - -static REString *re_string_find2(REStringList *s, int len, const uint32_t *buf, - uint32_t h0, BOOL add_flag) -{ - uint32_t h = 0; /* avoid warning */ - REString *p; - if (s->n_strings != 0) { - h = h0 >> (32 - s->hash_bits); - for(p = s->hash_table[h]; p != NULL; p = p->next) { - if (p->hash == h0 && p->len == len && - !memcmp(p->buf, buf, len * sizeof(buf[0]))) { - return p; - } - } - } - /* not found */ - if (!add_flag) - return NULL; - /* increase the size of the hash table if needed */ - if (unlikely((s->n_strings + 1) > s->hash_size)) { - REString **new_hash_table, *p_next; - int new_hash_bits, i; - uint32_t new_hash_size; - new_hash_bits = max_int(s->hash_bits + 1, 4); - new_hash_size = 1 << new_hash_bits; - new_hash_table = malloc(sizeof(new_hash_table[0]) * new_hash_size); - if (!new_hash_table) - return NULL; - memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size); - for(i = 0; i < s->hash_size; i++) { - for(p = s->hash_table[i]; p != NULL; p = p_next) { - p_next = p->next; - h = p->hash >> (32 - new_hash_bits); - p->next = new_hash_table[h]; - new_hash_table[h] = p; - } - } - free(s->hash_table); - s->hash_bits = new_hash_bits; - s->hash_size = new_hash_size; - s->hash_table = new_hash_table; - h = h0 >> (32 - s->hash_bits); - } - - p = malloc(sizeof(REString) + len * sizeof(buf[0])); - if (!p) - return NULL; - p->next = s->hash_table[h]; - s->hash_table[h] = p; - s->n_strings++; - p->hash = h0; - p->len = len; - p->flags = 0; - memcpy(p->buf, buf, sizeof(buf[0]) * len); - return p; -} - -static REString *re_string_find(REStringList *s, int len, const uint32_t *buf, - BOOL add_flag) -{ - uint32_t h0; - h0 = re_string_hash(len, buf); - return re_string_find2(s, len, buf, h0, add_flag); -} - -static void re_string_add(REStringList *s, int len, const uint32_t *buf) -{ - re_string_find(s, len, buf, TRUE); -} - -#define UNICODE_GENERAL_CATEGORY - -typedef enum { -#define DEF(id, str) GCAT_ ## id, -#include "unicode_gen_def.h" -#undef DEF - GCAT_COUNT, -} UnicodeGCEnum1; - -static const char *unicode_gc_name[] = { -#define DEF(id, str) #id, -#include "unicode_gen_def.h" -#undef DEF -}; - -static const char *unicode_gc_short_name[] = { -#define DEF(id, str) str, -#include "unicode_gen_def.h" -#undef DEF -}; - -#undef UNICODE_GENERAL_CATEGORY - -#define UNICODE_SCRIPT - -typedef enum { -#define DEF(id, str) SCRIPT_ ## id, -#include "unicode_gen_def.h" -#undef DEF - SCRIPT_COUNT, -} UnicodeScriptEnum1; - -static const char *unicode_script_name[] = { -#define DEF(id, str) #id, -#include "unicode_gen_def.h" -#undef DEF -}; - -const char *unicode_script_short_name[] = { -#define DEF(id, str) str, -#include "unicode_gen_def.h" -#undef DEF -}; - -#undef UNICODE_SCRIPT - -#define UNICODE_PROP_LIST - -typedef enum { -#define DEF(id, str) PROP_ ## id, -#include "unicode_gen_def.h" -#undef DEF - PROP_COUNT, -} UnicodePropEnum1; - -static const char *unicode_prop_name[] = { -#define DEF(id, str) #id, -#include "unicode_gen_def.h" -#undef DEF -}; - -static const char *unicode_prop_short_name[] = { -#define DEF(id, str) str, -#include "unicode_gen_def.h" -#undef DEF -}; - -#undef UNICODE_PROP_LIST - -#define UNICODE_SEQUENCE_PROP_LIST - -typedef enum { -#define DEF(id) SEQUENCE_PROP_ ## id, -#include "unicode_gen_def.h" -#undef DEF - SEQUENCE_PROP_COUNT, -} UnicodeSequencePropEnum1; - -static const char *unicode_sequence_prop_name[] = { -#define DEF(id) #id, -#include "unicode_gen_def.h" -#undef DEF -}; - -#undef UNICODE_SEQUENCE_PROP_LIST - -typedef struct { - /* case conv */ - uint8_t u_len; - uint8_t l_len; - uint8_t f_len; - int u_data[CC_LEN_MAX]; /* to upper case */ - int l_data[CC_LEN_MAX]; /* to lower case */ - int f_data[CC_LEN_MAX]; /* to case folding */ - - uint8_t combining_class; - uint8_t is_compat:1; - uint8_t is_excluded:1; - uint8_t general_category; - uint8_t script; - uint8_t script_ext_len; - uint8_t *script_ext; - uint32_t prop_bitmap_tab[3]; - /* decomposition */ - int decomp_len; - int *decomp_data; -} CCInfo; - -typedef struct { - int count; - int size; - int *tab; -} UnicodeSequenceProperties; - -CCInfo *unicode_db; -REStringList rgi_emoji_zwj_sequence; -DynBuf rgi_emoji_tag_sequence; - -int find_name(const char **tab, int tab_len, const char *name) -{ - int i, len, name_len; - const char *p, *r; - - name_len = strlen(name); - for(i = 0; i < tab_len; i++) { - p = tab[i]; - for(;;) { - r = strchr(p, ','); - if (!r) - len = strlen(p); - else - len = r - p; - if (len == name_len && memcmp(p, name, len) == 0) - return i; - if (!r) - break; - p = r + 1; - } - } - return -1; -} - -static BOOL get_prop(uint32_t c, int prop_idx) -{ - return (unicode_db[c].prop_bitmap_tab[prop_idx >> 5] >> (prop_idx & 0x1f)) & 1; -} - -static void set_prop(uint32_t c, int prop_idx, int val) -{ - uint32_t mask; - mask = 1U << (prop_idx & 0x1f); - if (val) - unicode_db[c].prop_bitmap_tab[prop_idx >> 5] |= mask; - else - unicode_db[c].prop_bitmap_tab[prop_idx >> 5] &= ~mask; -} - -void parse_unicode_data(const char *filename) -{ - FILE *f; - char line[1024]; - char buf1[256]; - const char *p; - int code, lc, uc, last_code; - CCInfo *ci, *tab = unicode_db; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - last_code = 0; - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#') - continue; - - p = get_field(line, 0); - if (!p) - continue; - code = strtoul(p, NULL, 16); - lc = 0; - uc = 0; - - p = get_field(line, 12); - if (p && *p != ';') { - uc = strtoul(p, NULL, 16); - } - - p = get_field(line, 13); - if (p && *p != ';') { - lc = strtoul(p, NULL, 16); - } - ci = &tab[code]; - if (uc > 0 || lc > 0) { - assert(code <= CHARCODE_MAX); - if (uc > 0) { - assert(ci->u_len == 0); - ci->u_len = 1; - ci->u_data[0] = uc; - } - if (lc > 0) { - assert(ci->l_len == 0); - ci->l_len = 1; - ci->l_data[0] = lc; - } - } - - { - int i; - get_field_buf(buf1, sizeof(buf1), line, 2); - i = find_name(unicode_gc_name, countof(unicode_gc_name), buf1); - if (i < 0) { - fprintf(stderr, "General category '%s' not found\n", - buf1); - exit(1); - } - ci->general_category = i; - } - - p = get_field(line, 3); - if (p && *p != ';' && *p != '\0') { - int cc; - cc = strtoul(p, NULL, 0); - if (cc != 0) { - assert(code <= CHARCODE_MAX); - ci->combining_class = cc; - // printf("%05x: %d\n", code, ci->combining_class); - } - } - - p = get_field(line, 5); - if (p && *p != ';' && *p != '\0') { - int size; - assert(code <= CHARCODE_MAX); - ci->is_compat = 0; - if (*p == '<') { - while (*p != '\0' && *p != '>') - p++; - if (*p == '>') - p++; - ci->is_compat = 1; - } - size = 0; - for(;;) { - while (isspace(*p)) - p++; - if (!isxdigit(*p)) - break; - add_char(&ci->decomp_data, &size, &ci->decomp_len, strtoul(p, (char **)&p, 16)); - } -#if 0 - { - int i; - static int count, d_count; - - printf("%05x: %c", code, ci->is_compat ? 'C': ' '); - for(i = 0; i < ci->decomp_len; i++) - printf(" %05x", ci->decomp_data[i]); - printf("\n"); - count++; - d_count += ci->decomp_len; - // printf("%d %d\n", count, d_count); - } -#endif - } - - p = get_field(line, 9); - if (p && *p == 'Y') { - set_prop(code, PROP_Bidi_Mirrored, 1); - } - - /* handle ranges */ - get_field_buf(buf1, sizeof(buf1), line, 1); - if (strstr(buf1, " Last>")) { - int i; - // printf("range: 0x%x-%0x\n", last_code, code); - assert(ci->decomp_len == 0); - assert(ci->script_ext_len == 0); - for(i = last_code + 1; i < code; i++) { - unicode_db[i] = *ci; - } - } - last_code = code; - } - - fclose(f); -} - -void parse_special_casing(CCInfo *tab, const char *filename) -{ - FILE *f; - char line[1024]; - const char *p; - int code; - CCInfo *ci; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#') - continue; - - p = get_field(line, 0); - if (!p) - continue; - code = strtoul(p, NULL, 16); - assert(code <= CHARCODE_MAX); - ci = &tab[code]; - - p = get_field(line, 4); - if (p) { - /* locale dependent casing */ - while (isspace(*p)) - p++; - if (*p != '#' && *p != '\0') - continue; - } - - - p = get_field(line, 1); - if (p && *p != ';') { - ci->l_len = 0; - for(;;) { - while (isspace(*p)) - p++; - if (*p == ';') - break; - assert(ci->l_len < CC_LEN_MAX); - ci->l_data[ci->l_len++] = strtoul(p, (char **)&p, 16); - } - - if (ci->l_len == 1 && ci->l_data[0] == code) - ci->l_len = 0; - } - - p = get_field(line, 3); - if (p && *p != ';') { - ci->u_len = 0; - for(;;) { - while (isspace(*p)) - p++; - if (*p == ';') - break; - assert(ci->u_len < CC_LEN_MAX); - ci->u_data[ci->u_len++] = strtoul(p, (char **)&p, 16); - } - - if (ci->u_len == 1 && ci->u_data[0] == code) - ci->u_len = 0; - } - } - - fclose(f); -} - -void parse_case_folding(CCInfo *tab, const char *filename) -{ - FILE *f; - char line[1024]; - const char *p; - int code, status; - CCInfo *ci; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#') - continue; - - p = get_field(line, 0); - if (!p) - continue; - code = strtoul(p, NULL, 16); - assert(code <= CHARCODE_MAX); - ci = &tab[code]; - - p = get_field(line, 1); - if (!p) - continue; - /* locale dependent casing */ - while (isspace(*p)) - p++; - status = *p; - if (status != 'C' && status != 'S' && status != 'F') - continue; - - p = get_field(line, 2); - assert(p != NULL); - if (status == 'S') { - /* we always select the simple case folding and assume it - * comes after the full case folding case */ - assert(ci->f_len >= 2); - ci->f_len = 0; - } else { - assert(ci->f_len == 0); - } - for(;;) { - while (isspace(*p)) - p++; - if (*p == ';') - break; - assert(ci->l_len < CC_LEN_MAX); - ci->f_data[ci->f_len++] = strtoul(p, (char **)&p, 16); - } - } - - fclose(f); -} - -void parse_composition_exclusions(const char *filename) -{ - FILE *f; - char line[4096], *p; - uint32_t c0; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - c0 = strtoul(p, (char **)&p, 16); - assert(c0 > 0 && c0 <= CHARCODE_MAX); - unicode_db[c0].is_excluded = TRUE; - } - fclose(f); -} - -void parse_derived_core_properties(const char *filename) -{ - FILE *f; - char line[4096], *p, buf[256], *q; - uint32_t c0, c1, c; - int i; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - c0 = strtoul(p, (char **)&p, 16); - if (*p == '.' && p[1] == '.') { - p += 2; - c1 = strtoul(p, (char **)&p, 16); - } else { - c1 = c0; - } - assert(c1 <= CHARCODE_MAX); - p += strspn(p, " \t"); - if (*p == ';') { - p++; - p += strspn(p, " \t"); - q = buf; - while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t' && *p != ';') { - if ((q - buf) < sizeof(buf) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - i = find_name(unicode_prop_name, - countof(unicode_prop_name), buf); - if (i < 0) { - if (!strcmp(buf, "Grapheme_Link")) - goto next; - fprintf(stderr, "Property not found: %s\n", buf); - exit(1); - } - for(c = c0; c <= c1; c++) { - set_prop(c, i, 1); - } -next: ; - } - } - fclose(f); -} - -void parse_derived_norm_properties(const char *filename) -{ - FILE *f; - char line[4096], *p, buf[256], *q; - uint32_t c0, c1, c; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - c0 = strtoul(p, (char **)&p, 16); - if (*p == '.' && p[1] == '.') { - p += 2; - c1 = strtoul(p, (char **)&p, 16); - } else { - c1 = c0; - } - assert(c1 <= CHARCODE_MAX); - p += strspn(p, " \t"); - if (*p == ';') { - p++; - p += strspn(p, " \t"); - q = buf; - while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { - if ((q - buf) < sizeof(buf) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - if (!strcmp(buf, "Changes_When_NFKC_Casefolded")) { - for(c = c0; c <= c1; c++) { - set_prop(c, PROP_Changes_When_NFKC_Casefolded, 1); - } - } - } - } - fclose(f); -} - -void parse_prop_list(const char *filename) -{ - FILE *f; - char line[4096], *p, buf[256], *q; - uint32_t c0, c1, c; - int i; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - c0 = strtoul(p, (char **)&p, 16); - if (*p == '.' && p[1] == '.') { - p += 2; - c1 = strtoul(p, (char **)&p, 16); - } else { - c1 = c0; - } - assert(c1 <= CHARCODE_MAX); - p += strspn(p, " \t"); - if (*p == ';') { - p++; - p += strspn(p, " \t"); - q = buf; - while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { - if ((q - buf) < sizeof(buf) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - i = find_name(unicode_prop_name, - countof(unicode_prop_name), buf); - if (i < 0) { - fprintf(stderr, "Property not found: %s\n", buf); - exit(1); - } - for(c = c0; c <= c1; c++) { - set_prop(c, i, 1); - } - } - } - fclose(f); -} - -#define SEQ_MAX_LEN 16 - -static BOOL is_emoji_modifier(uint32_t c) -{ - return (c >= 0x1f3fb && c <= 0x1f3ff); -} - -static void add_sequence_prop(int idx, int seq_len, int *seq) -{ - int i; - - assert(idx < SEQUENCE_PROP_COUNT); - switch(idx) { - case SEQUENCE_PROP_Basic_Emoji: - /* convert to 2 properties lists */ - if (seq_len == 1) { - set_prop(seq[0], PROP_Basic_Emoji1, 1); - } else if (seq_len == 2 && seq[1] == 0xfe0f) { - set_prop(seq[0], PROP_Basic_Emoji2, 1); - } else { - abort(); - } - break; - case SEQUENCE_PROP_RGI_Emoji_Modifier_Sequence: - assert(seq_len == 2); - assert(is_emoji_modifier(seq[1])); - assert(get_prop(seq[0], PROP_Emoji_Modifier_Base)); - set_prop(seq[0], PROP_RGI_Emoji_Modifier_Sequence, 1); - break; - case SEQUENCE_PROP_RGI_Emoji_Flag_Sequence: - { - int code; - assert(seq_len == 2); - assert(seq[0] >= 0x1F1E6 && seq[0] <= 0x1F1FF); - assert(seq[1] >= 0x1F1E6 && seq[1] <= 0x1F1FF); - code = (seq[0] - 0x1F1E6) * 26 + (seq[1] - 0x1F1E6); - /* XXX: would be more compact with a simple bitmap -> 676 bits */ - set_prop(code, PROP_RGI_Emoji_Flag_Sequence, 1); - } - break; - case SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence: - re_string_add(&rgi_emoji_zwj_sequence, seq_len, (uint32_t *)seq); - break; - case SEQUENCE_PROP_RGI_Emoji_Tag_Sequence: - { - assert(seq_len >= 3); - assert(seq[0] == 0x1F3F4); - assert(seq[seq_len - 1] == 0xE007F); - for(i = 1; i < seq_len - 1; i++) { - assert(seq[i] >= 0xe0001 && seq[i] <= 0xe007e); - dbuf_putc(&rgi_emoji_tag_sequence, seq[i] - 0xe0000); - } - dbuf_putc(&rgi_emoji_tag_sequence, 0); - } - break; - case SEQUENCE_PROP_Emoji_Keycap_Sequence: - assert(seq_len == 3); - assert(seq[1] == 0xfe0f); - assert(seq[2] == 0x20e3); - set_prop(seq[0], PROP_Emoji_Keycap_Sequence, 1); - break; - default: - assert(0); - } -} - -void parse_sequence_prop_list(const char *filename) -{ - FILE *f; - char line[4096], *p, buf[256], *q, *p_start; - uint32_t c0, c1, c; - int idx, seq_len; - int seq[SEQ_MAX_LEN]; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - p_start = p; - - /* find the sequence property name */ - p = strchr(p, ';'); - if (!p) - continue; - p++; - p += strspn(p, " \t"); - q = buf; - while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t' && *p != ';') { - if ((q - buf) < sizeof(buf) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - idx = find_name(unicode_sequence_prop_name, - countof(unicode_sequence_prop_name), buf); - if (idx < 0) { - fprintf(stderr, "Property not found: %s\n", buf); - exit(1); - } - - p = p_start; - c0 = strtoul(p, (char **)&p, 16); - assert(c0 <= CHARCODE_MAX); - - if (*p == '.' && p[1] == '.') { - p += 2; - c1 = strtoul(p, (char **)&p, 16); - assert(c1 <= CHARCODE_MAX); - for(c = c0; c <= c1; c++) { - seq[0] = c; - add_sequence_prop(idx, 1, seq); - } - } else { - seq_len = 0; - seq[seq_len++] = c0; - for(;;) { - while (isspace(*p)) - p++; - if (*p == ';' || *p == '\0') - break; - c0 = strtoul(p, (char **)&p, 16); - assert(c0 <= CHARCODE_MAX); - assert(seq_len < countof(seq)); - seq[seq_len++] = c0; - } - add_sequence_prop(idx, seq_len, seq); - } - } - fclose(f); -} - -void parse_scripts(const char *filename) -{ - FILE *f; - char line[4096], *p, buf[256], *q; - uint32_t c0, c1, c; - int i; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - c0 = strtoul(p, (char **)&p, 16); - if (*p == '.' && p[1] == '.') { - p += 2; - c1 = strtoul(p, (char **)&p, 16); - } else { - c1 = c0; - } - assert(c1 <= CHARCODE_MAX); - p += strspn(p, " \t"); - if (*p == ';') { - p++; - p += strspn(p, " \t"); - q = buf; - while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { - if ((q - buf) < sizeof(buf) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - i = find_name(unicode_script_name, - countof(unicode_script_name), buf); - if (i < 0) { - fprintf(stderr, "Unknown script: '%s'\n", buf); - exit(1); - } - for(c = c0; c <= c1; c++) - unicode_db[c].script = i; - } - } - fclose(f); -} - -void parse_script_extensions(const char *filename) -{ - FILE *f; - char line[4096], *p, buf[256], *q; - uint32_t c0, c1, c; - int i; - uint8_t script_ext[255]; - int script_ext_len; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@' || *p == '\0') - continue; - c0 = strtoul(p, (char **)&p, 16); - if (*p == '.' && p[1] == '.') { - p += 2; - c1 = strtoul(p, (char **)&p, 16); - } else { - c1 = c0; - } - assert(c1 <= CHARCODE_MAX); - p += strspn(p, " \t"); - script_ext_len = 0; - if (*p == ';') { - p++; - for(;;) { - p += strspn(p, " \t"); - q = buf; - while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { - if ((q - buf) < sizeof(buf) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - if (buf[0] == '\0') - break; - i = find_name(unicode_script_short_name, - countof(unicode_script_short_name), buf); - if (i < 0) { - fprintf(stderr, "Script not found: %s\n", buf); - exit(1); - } - assert(script_ext_len < sizeof(script_ext)); - script_ext[script_ext_len++] = i; - } - for(c = c0; c <= c1; c++) { - CCInfo *ci = &unicode_db[c]; - ci->script_ext_len = script_ext_len; - ci->script_ext = malloc(sizeof(ci->script_ext[0]) * script_ext_len); - for(i = 0; i < script_ext_len; i++) - ci->script_ext[i] = script_ext[i]; - } - } - } - fclose(f); -} - -void dump_cc_info(CCInfo *ci, int i) -{ - int j; - printf("%05x:", i); - if (ci->u_len != 0) { - printf(" U:"); - for(j = 0; j < ci->u_len; j++) - printf(" %05x", ci->u_data[j]); - } - if (ci->l_len != 0) { - printf(" L:"); - for(j = 0; j < ci->l_len; j++) - printf(" %05x", ci->l_data[j]); - } - if (ci->f_len != 0) { - printf(" F:"); - for(j = 0; j < ci->f_len; j++) - printf(" %05x", ci->f_data[j]); - } - printf("\n"); -} - -void dump_unicode_data(CCInfo *tab) -{ - int i; - CCInfo *ci; - for(i = 0; i <= CHARCODE_MAX; i++) { - ci = &tab[i]; - if (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0) { - dump_cc_info(ci, i); - } - } -} - -BOOL is_complicated_case(const CCInfo *ci) -{ - return (ci->u_len > 1 || ci->l_len > 1 || - (ci->u_len > 0 && ci->l_len > 0) || - (ci->f_len != ci->l_len) || - (memcmp(ci->f_data, ci->l_data, ci->f_len * sizeof(ci->f_data[0])) != 0)); -} - -#ifndef USE_TEST -enum { - RUN_TYPE_U, - RUN_TYPE_L, - RUN_TYPE_UF, - RUN_TYPE_LF, - RUN_TYPE_UL, - RUN_TYPE_LSU, - RUN_TYPE_U2L_399_EXT2, - RUN_TYPE_UF_D20, - RUN_TYPE_UF_D1_EXT, - RUN_TYPE_U_EXT, - RUN_TYPE_LF_EXT, - RUN_TYPE_UF_EXT2, - RUN_TYPE_LF_EXT2, - RUN_TYPE_UF_EXT3, -}; -#endif - -const char *run_type_str[] = { - "U", - "L", - "UF", - "LF", - "UL", - "LSU", - "U2L_399_EXT2", - "UF_D20", - "UF_D1_EXT", - "U_EXT", - "LF_EXT", - "UF_EXT2", - "LF_EXT2", - "UF_EXT3", -}; - -typedef struct { - int code; - int len; - int type; - int data; - int ext_len; - int ext_data[3]; - int data_index; /* 'data' coming from the table */ -} TableEntry; - -static int simple_to_lower(CCInfo *tab, int c) -{ - if (tab[c].l_len != 1) - return c; - return tab[c].l_data[0]; -} - -/* code (17), len (7), type (4) */ - -void find_run_type(TableEntry *te, CCInfo *tab, int code) -{ - int is_lower, len; - CCInfo *ci, *ci1, *ci2; - - ci = &tab[code]; - ci1 = &tab[code + 1]; - ci2 = &tab[code + 2]; - te->code = code; - - if (ci->l_len == 1 && ci->l_data[0] == code + 2 && - ci->f_len == 1 && ci->f_data[0] == ci->l_data[0] && - ci->u_len == 0 && - - ci1->l_len == 1 && ci1->l_data[0] == code + 2 && - ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0] && - ci1->u_len == 1 && ci1->u_data[0] == code && - - ci2->l_len == 0 && - ci2->f_len == 0 && - ci2->u_len == 1 && ci2->u_data[0] == code) { - te->len = 3; - te->data = 0; - te->type = RUN_TYPE_LSU; - return; - } - - if (is_complicated_case(ci)) { - len = 1; - while (code + len <= CHARCODE_MAX) { - ci1 = &tab[code + len]; - if (ci1->u_len != 1 || - ci1->u_data[0] != ci->u_data[0] + len || - ci1->l_len != 0 || - ci1->f_len != 1 || ci1->f_data[0] != ci1->u_data[0]) - break; - len++; - } - if (len > 1) { - te->len = len; - te->type = RUN_TYPE_UF; - te->data = ci->u_data[0]; - return; - } - - if (ci->l_len == 0 && - ci->u_len == 2 && ci->u_data[1] == 0x399 && - ci->f_len == 2 && ci->f_data[1] == 0x3B9 && - ci->f_data[0] == simple_to_lower(tab, ci->u_data[0])) { - len = 1; - while (code + len <= CHARCODE_MAX) { - ci1 = &tab[code + len]; - if (!(ci1->u_len == 2 && - ci1->u_data[1] == ci->u_data[1] && - ci1->u_data[0] == ci->u_data[0] + len && - ci1->f_len == 2 && - ci1->f_data[1] == ci->f_data[1] && - ci1->f_data[0] == ci->f_data[0] + len && - ci1->l_len == 0)) - break; - len++; - } - te->len = len; - te->type = RUN_TYPE_UF_EXT2; - te->ext_data[0] = ci->u_data[0]; - te->ext_data[1] = ci->u_data[1]; - te->ext_len = 2; - return; - } - - if (ci->u_len == 2 && ci->u_data[1] == 0x399 && - ci->l_len == 1 && - ci->f_len == 1 && ci->f_data[0] == ci->l_data[0]) { - len = 1; - while (code + len <= CHARCODE_MAX) { - ci1 = &tab[code + len]; - if (!(ci1->u_len == 2 && - ci1->u_data[1] == 0x399 && - ci1->u_data[0] == ci->u_data[0] + len && - ci1->l_len == 1 && - ci1->l_data[0] == ci->l_data[0] + len && - ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0])) - break; - len++; - } - te->len = len; - te->type = RUN_TYPE_U2L_399_EXT2; - te->ext_data[0] = ci->u_data[0]; - te->ext_data[1] = ci->l_data[0]; - te->ext_len = 2; - return; - } - - if (ci->l_len == 1 && ci->u_len == 0 && ci->f_len == 0) { - len = 1; - while (code + len <= CHARCODE_MAX) { - ci1 = &tab[code + len]; - if (!(ci1->l_len == 1 && - ci1->l_data[0] == ci->l_data[0] + len && - ci1->u_len == 0 && ci1->f_len == 0)) - break; - len++; - } - te->len = len; - te->type = RUN_TYPE_L; - te->data = ci->l_data[0]; - return; - } - - if (ci->l_len == 0 && - ci->u_len == 1 && - ci->u_data[0] < 0x1000 && - ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 0x20) { - te->len = 1; - te->type = RUN_TYPE_UF_D20; - te->data = ci->u_data[0]; - } else if (ci->l_len == 0 && - ci->u_len == 1 && - ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 1) { - te->len = 1; - te->type = RUN_TYPE_UF_D1_EXT; - te->ext_data[0] = ci->u_data[0]; - te->ext_len = 1; - } else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_len == 2 && - ci->l_data[0] == ci->f_data[0] && - ci->l_data[1] == ci->f_data[1]) { - te->len = 1; - te->type = RUN_TYPE_LF_EXT2; - te->ext_data[0] = ci->l_data[0]; - te->ext_data[1] = ci->l_data[1]; - te->ext_len = 2; - } else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 2 && - ci->f_data[0] == simple_to_lower(tab, ci->u_data[0]) && - ci->f_data[1] == simple_to_lower(tab, ci->u_data[1])) { - te->len = 1; - te->type = RUN_TYPE_UF_EXT2; - te->ext_data[0] = ci->u_data[0]; - te->ext_data[1] = ci->u_data[1]; - te->ext_len = 2; - } else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 3 && - ci->f_data[0] == simple_to_lower(tab, ci->u_data[0]) && - ci->f_data[1] == simple_to_lower(tab, ci->u_data[1]) && - ci->f_data[2] == simple_to_lower(tab, ci->u_data[2])) { - te->len = 1; - te->type = RUN_TYPE_UF_EXT3; - te->ext_data[0] = ci->u_data[0]; - te->ext_data[1] = ci->u_data[1]; - te->ext_data[2] = ci->u_data[2]; - te->ext_len = 3; - } else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 1) { - // U+FB05 LATIN SMALL LIGATURE LONG S T - assert(code == 0xFB05); - te->len = 1; - te->type = RUN_TYPE_UF_EXT2; - te->ext_data[0] = ci->u_data[0]; - te->ext_data[1] = ci->u_data[1]; - te->ext_len = 2; - } else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 1) { - // U+1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA or - // U+1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA - assert(code == 0x1FD3 || code == 0x1FE3); - te->len = 1; - te->type = RUN_TYPE_UF_EXT3; - te->ext_data[0] = ci->u_data[0]; - te->ext_data[1] = ci->u_data[1]; - te->ext_data[2] = ci->u_data[2]; - te->ext_len = 3; - } else { - printf("unsupported encoding case:\n"); - dump_cc_info(ci, code); - abort(); - } - } else { - /* look for a run of identical conversions */ - len = 0; - for(;;) { - if (code >= CHARCODE_MAX || len >= 126) - break; - ci = &tab[code + len]; - ci1 = &tab[code + len + 1]; - if (is_complicated_case(ci) || is_complicated_case(ci1)) { - break; - } - if (ci->l_len != 1 || ci->l_data[0] != code + len + 1) - break; - if (ci1->u_len != 1 || ci1->u_data[0] != code + len) - break; - len += 2; - } - if (len > 0) { - te->len = len; - te->type = RUN_TYPE_UL; - te->data = 0; - return; - } - - ci = &tab[code]; - is_lower = ci->l_len > 0; - len = 1; - while (code + len <= CHARCODE_MAX) { - ci1 = &tab[code + len]; - if (is_complicated_case(ci1)) - break; - if (is_lower) { - if (ci1->l_len != 1 || - ci1->l_data[0] != ci->l_data[0] + len) - break; - } else { - if (ci1->u_len != 1 || - ci1->u_data[0] != ci->u_data[0] + len) - break; - } - len++; - } - te->len = len; - if (is_lower) { - te->type = RUN_TYPE_LF; - te->data = ci->l_data[0]; - } else { - te->type = RUN_TYPE_U; - te->data = ci->u_data[0]; - } - } -} - -TableEntry conv_table[1000]; -int conv_table_len; -int ext_data[1000]; -int ext_data_len; - -void dump_case_conv_table1(void) -{ - int i, j; - const TableEntry *te; - - for(i = 0; i < conv_table_len; i++) { - te = &conv_table[i]; - printf("%05x %02x %-10s %05x", - te->code, te->len, run_type_str[te->type], te->data); - for(j = 0; j < te->ext_len; j++) { - printf(" %05x", te->ext_data[j]); - } - printf("\n"); - } - printf("table_len=%d ext_len=%d\n", conv_table_len, ext_data_len); -} - -int find_data_index(const TableEntry *conv_table, int len, int data) -{ - int i; - const TableEntry *te; - for(i = 0; i < len; i++) { - te = &conv_table[i]; - if (te->code == data) - return i; - } - return -1; -} - -int find_ext_data_index(int data) -{ - int i; - for(i = 0; i < ext_data_len; i++) { - if (ext_data[i] == data) - return i; - } - assert(ext_data_len < countof(ext_data)); - ext_data[ext_data_len++] = data; - return ext_data_len - 1; -} - -void build_conv_table(CCInfo *tab) -{ - int code, i, j; - CCInfo *ci; - TableEntry *te; - - te = conv_table; - for(code = 0; code <= CHARCODE_MAX; code++) { - ci = &tab[code]; - if (ci->u_len == 0 && ci->l_len == 0 && ci->f_len == 0) - continue; - assert(te - conv_table < countof(conv_table)); - find_run_type(te, tab, code); -#if 0 - if (te->type == RUN_TYPE_TODO) { - printf("TODO: "); - dump_cc_info(ci, code); - } -#endif - assert(te->len <= 127); - code += te->len - 1; - te++; - } - conv_table_len = te - conv_table; - - /* find the data index */ - for(i = 0; i < conv_table_len; i++) { - int data_index; - te = &conv_table[i]; - - switch(te->type) { - case RUN_TYPE_U: - case RUN_TYPE_L: - case RUN_TYPE_UF: - case RUN_TYPE_LF: - data_index = find_data_index(conv_table, conv_table_len, te->data); - if (data_index < 0) { - switch(te->type) { - case RUN_TYPE_U: - te->type = RUN_TYPE_U_EXT; - te->ext_len = 1; - te->ext_data[0] = te->data; - break; - case RUN_TYPE_LF: - te->type = RUN_TYPE_LF_EXT; - te->ext_len = 1; - te->ext_data[0] = te->data; - break; - default: - printf("%05x: index not found\n", te->code); - exit(1); - } - } else { - te->data_index = data_index; - } - break; - case RUN_TYPE_UF_D20: - te->data_index = te->data; - break; - } - } - - /* find the data index for ext_data */ - for(i = 0; i < conv_table_len; i++) { - te = &conv_table[i]; - if (te->type == RUN_TYPE_UF_EXT3) { - int p, v; - v = 0; - for(j = 0; j < 3; j++) { - p = find_ext_data_index(te->ext_data[j]); - assert(p < 16); - v = (v << 4) | p; - } - te->data_index = v; - } - } - - for(i = 0; i < conv_table_len; i++) { - te = &conv_table[i]; - if (te->type == RUN_TYPE_LF_EXT2 || - te->type == RUN_TYPE_UF_EXT2 || - te->type == RUN_TYPE_U2L_399_EXT2) { - int p, v; - v = 0; - for(j = 0; j < 2; j++) { - p = find_ext_data_index(te->ext_data[j]); - assert(p < 64); - v = (v << 6) | p; - } - te->data_index = v; - } - } - - for(i = 0; i < conv_table_len; i++) { - te = &conv_table[i]; - if (te->type == RUN_TYPE_UF_D1_EXT || - te->type == RUN_TYPE_U_EXT || - te->type == RUN_TYPE_LF_EXT) { - te->data_index = find_ext_data_index(te->ext_data[0]); - } - } -#ifdef DUMP_CASE_CONV_TABLE - dump_case_conv_table1(); -#endif -} - -void dump_case_conv_table(FILE *f) -{ - int i; - uint32_t v; - const TableEntry *te; - - total_tables++; - total_table_bytes += conv_table_len * sizeof(uint32_t); - fprintf(f, "static const uint32_t case_conv_table1[%d] = {", conv_table_len); - for(i = 0; i < conv_table_len; i++) { - if (i % 4 == 0) - fprintf(f, "\n "); - te = &conv_table[i]; - v = te->code << (32 - 17); - v |= te->len << (32 - 17 - 7); - v |= te->type << (32 - 17 - 7 - 4); - v |= te->data_index >> 8; - fprintf(f, " 0x%08x,", v); - } - fprintf(f, "\n};\n\n"); - - total_tables++; - total_table_bytes += conv_table_len; - fprintf(f, "static const uint8_t case_conv_table2[%d] = {", conv_table_len); - for(i = 0; i < conv_table_len; i++) { - if (i % 8 == 0) - fprintf(f, "\n "); - te = &conv_table[i]; - fprintf(f, " 0x%02x,", te->data_index & 0xff); - } - fprintf(f, "\n};\n\n"); - - total_tables++; - total_table_bytes += ext_data_len * sizeof(uint16_t); - fprintf(f, "static const uint16_t case_conv_ext[%d] = {", ext_data_len); - for(i = 0; i < ext_data_len; i++) { - if (i % 8 == 0) - fprintf(f, "\n "); - fprintf(f, " 0x%04x,", ext_data[i]); - } - fprintf(f, "\n};\n\n"); -} - - -static CCInfo *global_tab; - -static int sp_cc_cmp(const void *p1, const void *p2) -{ - CCInfo *c1 = &global_tab[*(const int *)p1]; - CCInfo *c2 = &global_tab[*(const int *)p2]; - if (c1->f_len < c2->f_len) { - return -1; - } else if (c2->f_len < c1->f_len) { - return 1; - } else { - return memcmp(c1->f_data, c2->f_data, sizeof(c1->f_data[0]) * c1->f_len); - } -} - -/* dump the case special cases (multi character results which are - identical and need specific handling in lre_canonicalize() */ -void dump_case_folding_special_cases(CCInfo *tab) -{ - int i, len, j; - int *perm; - - perm = malloc(sizeof(perm[0]) * (CHARCODE_MAX + 1)); - for(i = 0; i <= CHARCODE_MAX; i++) - perm[i] = i; - global_tab = tab; - qsort(perm, CHARCODE_MAX + 1, sizeof(perm[0]), sp_cc_cmp); - for(i = 0; i <= CHARCODE_MAX;) { - if (tab[perm[i]].f_len <= 1) { - i++; - } else { - len = 1; - while ((i + len) <= CHARCODE_MAX && !sp_cc_cmp(&perm[i], &perm[i + len])) - len++; - - if (len > 1) { - for(j = i; j < i + len; j++) - dump_cc_info(&tab[perm[j]], perm[j]); - } - i += len; - } - } - free(perm); - global_tab = NULL; -} - - -int tabcmp(const int *tab1, const int *tab2, int n) -{ - int i; - for(i = 0; i < n; i++) { - if (tab1[i] != tab2[i]) - return -1; - } - return 0; -} - -void dump_str(const char *str, const int *buf, int len) -{ - int i; - printf("%s=", str); - for(i = 0; i < len; i++) - printf(" %05x", buf[i]); - printf("\n"); -} - -void compute_internal_props(void) -{ - int i; - BOOL has_ul; - - for(i = 0; i <= CHARCODE_MAX; i++) { - CCInfo *ci = &unicode_db[i]; - has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0); - if (has_ul) { - assert(get_prop(i, PROP_Cased)); - } else { - set_prop(i, PROP_Cased1, get_prop(i, PROP_Cased)); - } - set_prop(i, PROP_ID_Continue1, - get_prop(i, PROP_ID_Continue) & (get_prop(i, PROP_ID_Start) ^ 1)); - set_prop(i, PROP_XID_Start1, - get_prop(i, PROP_ID_Start) ^ get_prop(i, PROP_XID_Start)); - set_prop(i, PROP_XID_Continue1, - get_prop(i, PROP_ID_Continue) ^ get_prop(i, PROP_XID_Continue)); - set_prop(i, PROP_Changes_When_Titlecased1, - get_prop(i, PROP_Changes_When_Titlecased) ^ (ci->u_len != 0)); - set_prop(i, PROP_Changes_When_Casefolded1, - get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_len != 0)); - /* XXX: reduce table size (438 bytes) */ - set_prop(i, PROP_Changes_When_NFKC_Casefolded1, - get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_len != 0)); -#if 0 - /* TEST */ -#define M(x) (1U << GCAT_ ## x) - { - int b; - b = ((M(Mn) | M(Cf) | M(Lm) | M(Sk)) >> - unicode_db[i].general_category) & 1; - set_prop(i, PROP_Cased1, - get_prop(i, PROP_Case_Ignorable) ^ b); - } -#undef M -#endif - } -} - -void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len) -{ - int i; - - total_tables++; - total_table_bytes += len; - fprintf(f, "static const uint8_t %s[%d] = {", cname, len); - for(i = 0; i < len; i++) { - if (i % 8 == 0) - fprintf(f, "\n "); - fprintf(f, " 0x%02x,", tab[i]); - } - fprintf(f, "\n};\n\n"); -} - -void dump_index_table(FILE *f, const char *cname, const uint8_t *tab, int len) -{ - int i, code, offset; - - total_index++; - total_index_bytes += len; - fprintf(f, "static const uint8_t %s[%d] = {\n", cname, len); - for(i = 0; i < len; i += 3) { - code = tab[i] + (tab[i+1] << 8) + ((tab[i+2] & 0x1f) << 16); - offset = ((i / 3) + 1) * 32 + (tab[i+2] >> 5); - fprintf(f, " 0x%02x, 0x%02x, 0x%02x,", tab[i], tab[i+1], tab[i+2]); - fprintf(f, " // %6.5X at %d%s\n", code, offset, - i == len - 3 ? " (upper bound)" : ""); - } - fprintf(f, "};\n\n"); -} - -#define PROP_BLOCK_LEN 32 - -void build_prop_table(FILE *f, const char *name, int prop_index, BOOL add_index) -{ - int i, j, n, v, offset, code; - DynBuf dbuf_s, *dbuf = &dbuf_s; - DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; - DynBuf dbuf2_s, *dbuf2 = &dbuf2_s; - const uint32_t *buf; - int buf_len, block_end_pos, bit; - char cname[128]; - - dbuf_init(dbuf1); - - for(i = 0; i <= CHARCODE_MAX;) { - v = get_prop(i, prop_index); - j = i + 1; - while (j <= CHARCODE_MAX && get_prop(j, prop_index) == v) { - j++; - } - n = j - i; - if (j == (CHARCODE_MAX + 1) && v == 0) - break; /* no need to encode last zero run */ - //printf("%05x: %d %d\n", i, n, v); - dbuf_put_u32(dbuf1, n - 1); - i += n; - } - - dbuf_init(dbuf); - dbuf_init(dbuf2); - buf = (uint32_t *)dbuf1->buf; - buf_len = dbuf1->size / sizeof(buf[0]); - - /* the first value is assumed to be 0 */ - assert(get_prop(0, prop_index) == 0); - - block_end_pos = PROP_BLOCK_LEN; - i = 0; - code = 0; - bit = 0; - while (i < buf_len) { - if (add_index && dbuf->size >= block_end_pos && bit == 0) { - offset = (dbuf->size - block_end_pos); - /* XXX: offset could be larger in case of runs of small - lengths. Could add code to change the encoding to - prevent it at the expense of one byte loss */ - assert(offset <= 7); - v = code | (offset << 21); - dbuf_putc(dbuf2, v); - dbuf_putc(dbuf2, v >> 8); - dbuf_putc(dbuf2, v >> 16); - block_end_pos += PROP_BLOCK_LEN; - } - - /* Compressed byte encoding: - 00..3F: 2 packed lengths: 3-bit + 3-bit - 40..5F: 5-bits plus extra byte for length - 60..7F: 5-bits plus 2 extra bytes for length - 80..FF: 7-bit length - lengths must be incremented to get character count - Ranges alternate between false and true return value. - */ - v = buf[i]; - code += v + 1; - bit ^= 1; - if (v < 8 && (i + 1) < buf_len && buf[i + 1] < 8) { - code += buf[i + 1] + 1; - bit ^= 1; - dbuf_putc(dbuf, (v << 3) | buf[i + 1]); - i += 2; - } else if (v < 128) { - dbuf_putc(dbuf, 0x80 + v); - i++; - } else if (v < (1 << 13)) { - dbuf_putc(dbuf, 0x40 + (v >> 8)); - dbuf_putc(dbuf, v); - i++; - } else { - assert(v < (1 << 21)); - dbuf_putc(dbuf, 0x60 + (v >> 16)); - dbuf_putc(dbuf, v >> 8); - dbuf_putc(dbuf, v); - i++; - } - } - - if (add_index) { - /* last index entry */ - v = code; - dbuf_putc(dbuf2, v); - dbuf_putc(dbuf2, v >> 8); - dbuf_putc(dbuf2, v >> 16); - } - -#ifdef DUMP_TABLE_SIZE - printf("prop %s: length=%d bytes\n", unicode_prop_name[prop_index], - (int)(dbuf->size + dbuf2->size)); -#endif - snprintf(cname, sizeof(cname), "unicode_prop_%s_table", unicode_prop_name[prop_index]); - dump_byte_table(f, cname, dbuf->buf, dbuf->size); - if (add_index) { - snprintf(cname, sizeof(cname), "unicode_prop_%s_index", unicode_prop_name[prop_index]); - dump_index_table(f, cname, dbuf2->buf, dbuf2->size); - } - - dbuf_free(dbuf); - dbuf_free(dbuf1); - dbuf_free(dbuf2); -} - -void build_flags_tables(FILE *f) -{ - build_prop_table(f, "Cased1", PROP_Cased1, TRUE); - build_prop_table(f, "Case_Ignorable", PROP_Case_Ignorable, TRUE); - build_prop_table(f, "ID_Start", PROP_ID_Start, TRUE); - build_prop_table(f, "ID_Continue1", PROP_ID_Continue1, TRUE); -} - -void dump_name_table(FILE *f, const char *cname, const char **tab_name, int len, - const char **tab_short_name) -{ - int i, w, maxw; - - maxw = 0; - for(i = 0; i < len; i++) { - w = strlen(tab_name[i]); - if (tab_short_name && tab_short_name[i][0] != '\0') { - w += 1 + strlen(tab_short_name[i]); - } - if (maxw < w) - maxw = w; - } - - /* generate a sequence of strings terminated by an empty string */ - fprintf(f, "static const char %s[] =\n", cname); - for(i = 0; i < len; i++) { - fprintf(f, " \""); - w = fprintf(f, "%s", tab_name[i]); - if (tab_short_name && tab_short_name[i][0] != '\0') { - w += fprintf(f, ",%s", tab_short_name[i]); - } - fprintf(f, "\"%*s\"\\0\"\n", 1 + maxw - w, ""); - } - fprintf(f, ";\n\n"); -} - -void build_general_category_table(FILE *f) -{ - int i, v, j, n, n1; - DynBuf dbuf_s, *dbuf = &dbuf_s; -#ifdef DUMP_TABLE_SIZE - int cw_count, cw_len_count[4], cw_start; -#endif - - fprintf(f, "typedef enum {\n"); - for(i = 0; i < GCAT_COUNT; i++) - fprintf(f, " UNICODE_GC_%s,\n", unicode_gc_name[i]); - fprintf(f, " UNICODE_GC_COUNT,\n"); - fprintf(f, "} UnicodeGCEnum;\n\n"); - - dump_name_table(f, "unicode_gc_name_table", - unicode_gc_name, GCAT_COUNT, - unicode_gc_short_name); - - - dbuf_init(dbuf); -#ifdef DUMP_TABLE_SIZE - cw_count = 0; - for(i = 0; i < 4; i++) - cw_len_count[i] = 0; -#endif - for(i = 0; i <= CHARCODE_MAX;) { - v = unicode_db[i].general_category; - j = i + 1; - while (j <= CHARCODE_MAX && unicode_db[j].general_category == v) - j++; - n = j - i; - /* compress Lu/Ll runs */ - if (v == GCAT_Lu) { - n1 = 1; - while ((i + n1) <= CHARCODE_MAX && unicode_db[i + n1].general_category == (v + (n1 & 1))) { - n1++; - } - if (n1 > n) { - v = 31; - n = n1; - } - } - // printf("%05x %05x %d\n", i, n, v); - n--; -#ifdef DUMP_TABLE_SIZE - cw_count++; - cw_start = dbuf->size; -#endif - if (n < 7) { - dbuf_putc(dbuf, (n << 5) | v); - } else if (n < 7 + 128) { - n1 = n - 7; - assert(n1 < 128); - dbuf_putc(dbuf, (0xf << 5) | v); - dbuf_putc(dbuf, n1); - } else if (n < 7 + 128 + (1 << 14)) { - n1 = n - (7 + 128); - assert(n1 < (1 << 14)); - dbuf_putc(dbuf, (0xf << 5) | v); - dbuf_putc(dbuf, (n1 >> 8) + 128); - dbuf_putc(dbuf, n1); - } else { - n1 = n - (7 + 128 + (1 << 14)); - assert(n1 < (1 << 22)); - dbuf_putc(dbuf, (0xf << 5) | v); - dbuf_putc(dbuf, (n1 >> 16) + 128 + 64); - dbuf_putc(dbuf, n1 >> 8); - dbuf_putc(dbuf, n1); - } -#ifdef DUMP_TABLE_SIZE - cw_len_count[dbuf->size - cw_start - 1]++; -#endif - i += n + 1; - } -#ifdef DUMP_TABLE_SIZE - printf("general category: %d entries [", cw_count); - for(i = 0; i < 4; i++) - printf(" %d", cw_len_count[i]); - printf(" ], length=%d bytes\n", (int)dbuf->size); -#endif - - dump_byte_table(f, "unicode_gc_table", dbuf->buf, dbuf->size); - - dbuf_free(dbuf); -} - -void build_script_table(FILE *f) -{ - int i, v, j, n, n1, type; - DynBuf dbuf_s, *dbuf = &dbuf_s; -#ifdef DUMP_TABLE_SIZE - int cw_count, cw_len_count[4], cw_start; -#endif - - fprintf(f, "typedef enum {\n"); - for(i = 0; i < SCRIPT_COUNT; i++) - fprintf(f, " UNICODE_SCRIPT_%s,\n", unicode_script_name[i]); - fprintf(f, " UNICODE_SCRIPT_COUNT,\n"); - fprintf(f, "} UnicodeScriptEnum;\n\n"); - - dump_name_table(f, "unicode_script_name_table", - unicode_script_name, SCRIPT_COUNT, - unicode_script_short_name); - - dbuf_init(dbuf); -#ifdef DUMP_TABLE_SIZE - cw_count = 0; - for(i = 0; i < 4; i++) - cw_len_count[i] = 0; -#endif - for(i = 0; i <= CHARCODE_MAX;) { - v = unicode_db[i].script; - j = i + 1; - while (j <= CHARCODE_MAX && unicode_db[j].script == v) - j++; - n = j - i; - if (v == 0 && j == (CHARCODE_MAX + 1)) - break; - // printf("%05x %05x %d\n", i, n, v); - n--; -#ifdef DUMP_TABLE_SIZE - cw_count++; - cw_start = dbuf->size; -#endif - if (v == 0) - type = 0; - else - type = 1; - if (n < 96) { - dbuf_putc(dbuf, n | (type << 7)); - } else if (n < 96 + (1 << 12)) { - n1 = n - 96; - assert(n1 < (1 << 12)); - dbuf_putc(dbuf, ((n1 >> 8) + 96) | (type << 7)); - dbuf_putc(dbuf, n1); - } else { - n1 = n - (96 + (1 << 12)); - assert(n1 < (1 << 20)); - dbuf_putc(dbuf, ((n1 >> 16) + 112) | (type << 7)); - dbuf_putc(dbuf, n1 >> 8); - dbuf_putc(dbuf, n1); - } - if (type != 0) - dbuf_putc(dbuf, v); - -#ifdef DUMP_TABLE_SIZE - cw_len_count[dbuf->size - cw_start - 1]++; -#endif - i += n + 1; - } -#ifdef DUMP_TABLE_SIZE - printf("script: %d entries [", cw_count); - for(i = 0; i < 4; i++) - printf(" %d", cw_len_count[i]); - printf(" ], length=%d bytes\n", (int)dbuf->size); -#endif - - dump_byte_table(f, "unicode_script_table", dbuf->buf, dbuf->size); - - dbuf_free(dbuf); -} - -void build_script_ext_table(FILE *f) -{ - int i, j, n, n1, script_ext_len; - DynBuf dbuf_s, *dbuf = &dbuf_s; -#if defined(DUMP_TABLE_SIZE) - int cw_count = 0; -#endif - - dbuf_init(dbuf); - for(i = 0; i <= CHARCODE_MAX;) { - script_ext_len = unicode_db[i].script_ext_len; - j = i + 1; - while (j <= CHARCODE_MAX && - unicode_db[j].script_ext_len == script_ext_len && - !memcmp(unicode_db[j].script_ext, unicode_db[i].script_ext, - script_ext_len)) { - j++; - } - n = j - i; -#if defined(DUMP_TABLE_SIZE) - cw_count++; -#endif - n--; - if (n < 128) { - dbuf_putc(dbuf, n); - } else if (n < 128 + (1 << 14)) { - n1 = n - 128; - assert(n1 < (1 << 14)); - dbuf_putc(dbuf, (n1 >> 8) + 128); - dbuf_putc(dbuf, n1); - } else { - n1 = n - (128 + (1 << 14)); - assert(n1 < (1 << 22)); - dbuf_putc(dbuf, (n1 >> 16) + 128 + 64); - dbuf_putc(dbuf, n1 >> 8); - dbuf_putc(dbuf, n1); - } - dbuf_putc(dbuf, script_ext_len); - for(j = 0; j < script_ext_len; j++) - dbuf_putc(dbuf, unicode_db[i].script_ext[j]); - i += n + 1; - } -#ifdef DUMP_TABLE_SIZE - printf("script_ext: %d entries", cw_count); - printf(", length=%d bytes\n", (int)dbuf->size); -#endif - - dump_byte_table(f, "unicode_script_ext_table", dbuf->buf, dbuf->size); - - dbuf_free(dbuf); -} - -/* the following properties are synthetized so no table is necessary */ -#define PROP_TABLE_COUNT PROP_ASCII - -void build_prop_list_table(FILE *f) -{ - int i; - - for(i = 0; i < PROP_TABLE_COUNT; i++) { - if (i == PROP_ID_Start || - i == PROP_Case_Ignorable || - i == PROP_ID_Continue1) { - /* already generated */ - } else { - build_prop_table(f, unicode_prop_name[i], i, FALSE); - } - } - - fprintf(f, "typedef enum {\n"); - for(i = 0; i < PROP_COUNT; i++) - fprintf(f, " UNICODE_PROP_%s,\n", unicode_prop_name[i]); - fprintf(f, " UNICODE_PROP_COUNT,\n"); - fprintf(f, "} UnicodePropertyEnum;\n\n"); - - i = PROP_ASCII_Hex_Digit; - dump_name_table(f, "unicode_prop_name_table", - unicode_prop_name + i, PROP_XID_Start - i + 1, - unicode_prop_short_name + i); - - fprintf(f, "static const uint8_t * const unicode_prop_table[] = {\n"); - for(i = 0; i < PROP_TABLE_COUNT; i++) { - fprintf(f, " unicode_prop_%s_table,\n", unicode_prop_name[i]); - } - fprintf(f, "};\n\n"); - - fprintf(f, "static const uint16_t unicode_prop_len_table[] = {\n"); - for(i = 0; i < PROP_TABLE_COUNT; i++) { - fprintf(f, " countof(unicode_prop_%s_table),\n", unicode_prop_name[i]); - } - fprintf(f, "};\n\n"); -} - -static BOOL is_emoji_hair_color(uint32_t c) -{ - return (c >= 0x1F9B0 && c <= 0x1F9B3); -} - -#define EMOJI_MOD_NONE 0 -#define EMOJI_MOD_TYPE1 1 -#define EMOJI_MOD_TYPE2 2 -#define EMOJI_MOD_TYPE2D 3 - -static BOOL mark_zwj_string(REStringList *sl, uint32_t *buf, int len, int mod_type, int *mod_pos, - int hc_pos, BOOL mark_flag) -{ - REString *p; - int i, n_mod, i0, i1, hc_count, j; - -#if 0 - if (mark_flag) - printf("mod_type=%d\n", mod_type); -#endif - - switch(mod_type) { - case EMOJI_MOD_NONE: - n_mod = 1; - break; - case EMOJI_MOD_TYPE1: - n_mod = 5; - break; - case EMOJI_MOD_TYPE2: - n_mod = 25; - break; - case EMOJI_MOD_TYPE2D: - n_mod = 20; - break; - default: - assert(0); - } - if (hc_pos >= 0) - hc_count = 4; - else - hc_count = 1; - /* check that all the related strings are present */ - for(j = 0; j < hc_count; j++) { - for(i = 0; i < n_mod; i++) { - switch(mod_type) { - case EMOJI_MOD_NONE: - break; - case EMOJI_MOD_TYPE1: - buf[mod_pos[0]] = 0x1f3fb + i; - break; - case EMOJI_MOD_TYPE2: - case EMOJI_MOD_TYPE2D: - i0 = i / 5; - i1 = i % 5; - /* avoid identical values */ - if (mod_type == EMOJI_MOD_TYPE2D && i0 >= i1) - i0++; - buf[mod_pos[0]] = 0x1f3fb + i0; - buf[mod_pos[1]] = 0x1f3fb + i1; - break; - default: - assert(0); - } - - if (hc_pos >= 0) - buf[hc_pos] = 0x1F9B0 + j; - - p = re_string_find(sl, len, buf, FALSE); - if (!p) - return FALSE; - if (mark_flag) - p->flags |= 1; - } - } - return TRUE; -} - -static void zwj_encode_string(DynBuf *dbuf, const uint32_t *buf, int len, int mod_type, int *mod_pos, - int hc_pos) -{ - int i, j; - int c, code; - uint32_t buf1[SEQ_MAX_LEN]; - - j = 0; - for(i = 0; i < len;) { - c = buf[i++]; - if (c >= 0x2000 && c <= 0x2fff) { - code = c - 0x2000; - } else if (c >= 0x1f000 && c <= 0x1ffff) { - code = c - 0x1f000 + 0x1000; - } else { - assert(0); - } - if (i < len && is_emoji_modifier(buf[i])) { - /* modifier */ - code |= (mod_type << 13); - i++; - } - if (i < len && buf[i] == 0xfe0f) { - /* presentation selector present */ - code |= 0x8000; - i++; - } - if (i < len) { - /* zero width join */ - assert(buf[i] == 0x200d); - i++; - } - buf1[j++] = code; - } - dbuf_putc(dbuf, j); - for(i = 0; i < j; i++) { - dbuf_putc(dbuf, buf1[i]); - dbuf_putc(dbuf, buf1[i] >> 8); - } -} - -static void build_rgi_emoji_zwj_sequence(FILE *f, REStringList *sl) -{ - int mod_pos[2], mod_count, hair_color_pos, j, h; - REString *p; - uint32_t buf[SEQ_MAX_LEN]; - DynBuf dbuf; - -#if 0 - { - for(h = 0; h < sl->hash_size; h++) { - for(p = sl->hash_table[h]; p != NULL; p = p->next) { - for(j = 0; j < p->len; j++) - printf(" %04x", p->buf[j]); - printf("\n"); - } - } - exit(0); - } -#endif - // printf("rgi_emoji_zwj_sequence: n=%d\n", sl->n_strings); - - dbuf_init(&dbuf); - - /* avoid duplicating strings with emoji modifiers or hair colors */ - for(h = 0; h < sl->hash_size; h++) { - for(p = sl->hash_table[h]; p != NULL; p = p->next) { - if (p->flags) /* already examined */ - continue; - mod_count = 0; - hair_color_pos = -1; - for(j = 0; j < p->len; j++) { - if (is_emoji_modifier(p->buf[j])) { - assert(mod_count < 2); - mod_pos[mod_count++] = j; - } else if (is_emoji_hair_color(p->buf[j])) { - hair_color_pos = j; - } - buf[j] = p->buf[j]; - } - - if (mod_count != 0 || hair_color_pos >= 0) { - int mod_type; - if (mod_count == 0) - mod_type = EMOJI_MOD_NONE; - else if (mod_count == 1) - mod_type = EMOJI_MOD_TYPE1; - else - mod_type = EMOJI_MOD_TYPE2; - - if (mark_zwj_string(sl, buf, p->len, mod_type, mod_pos, hair_color_pos, FALSE)) { - mark_zwj_string(sl, buf, p->len, mod_type, mod_pos, hair_color_pos, TRUE); - } else if (mod_type == EMOJI_MOD_TYPE2) { - mod_type = EMOJI_MOD_TYPE2D; - if (mark_zwj_string(sl, buf, p->len, mod_type, mod_pos, hair_color_pos, FALSE)) { - mark_zwj_string(sl, buf, p->len, mod_type, mod_pos, hair_color_pos, TRUE); - } else { - dump_str("not_found", (int *)p->buf, p->len); - goto keep; - } - } - if (hair_color_pos >= 0) - buf[hair_color_pos] = 0x1f9b0; - /* encode the string */ - zwj_encode_string(&dbuf, buf, p->len, mod_type, mod_pos, hair_color_pos); - } else { - keep: - zwj_encode_string(&dbuf, buf, p->len, EMOJI_MOD_NONE, NULL, -1); - } - } - } - - /* Encode */ - dump_byte_table(f, "unicode_rgi_emoji_zwj_sequence", dbuf.buf, dbuf.size); - - dbuf_free(&dbuf); -} - -void build_sequence_prop_list_table(FILE *f) -{ - int i; - fprintf(f, "typedef enum {\n"); - for(i = 0; i < SEQUENCE_PROP_COUNT; i++) - fprintf(f, " UNICODE_SEQUENCE_PROP_%s,\n", unicode_sequence_prop_name[i]); - fprintf(f, " UNICODE_SEQUENCE_PROP_COUNT,\n"); - fprintf(f, "} UnicodeSequencePropertyEnum;\n\n"); - - dump_name_table(f, "unicode_sequence_prop_name_table", - unicode_sequence_prop_name, SEQUENCE_PROP_COUNT, NULL); - - dump_byte_table(f, "unicode_rgi_emoji_tag_sequence", rgi_emoji_tag_sequence.buf, rgi_emoji_tag_sequence.size); - - build_rgi_emoji_zwj_sequence(f, &rgi_emoji_zwj_sequence); -} - -#ifdef USE_TEST -int check_conv(uint32_t *res, uint32_t c, int conv_type) -{ - return lre_case_conv(res, c, conv_type); -} - -void check_case_conv(void) -{ - CCInfo *tab = unicode_db; - uint32_t res[3]; - int l, error; - CCInfo ci_s, *ci1, *ci = &ci_s; - int code; - - for(code = 0; code <= CHARCODE_MAX; code++) { - ci1 = &tab[code]; - *ci = *ci1; - if (ci->l_len == 0) { - ci->l_len = 1; - ci->l_data[0] = code; - } - if (ci->u_len == 0) { - ci->u_len = 1; - ci->u_data[0] = code; - } - if (ci->f_len == 0) { - ci->f_len = 1; - ci->f_data[0] = code; - } - - error = 0; - l = check_conv(res, code, 0); - if (l != ci->u_len || tabcmp((int *)res, ci->u_data, l)) { - printf("ERROR: L\n"); - error++; - } - l = check_conv(res, code, 1); - if (l != ci->l_len || tabcmp((int *)res, ci->l_data, l)) { - printf("ERROR: U\n"); - error++; - } - l = check_conv(res, code, 2); - if (l != ci->f_len || tabcmp((int *)res, ci->f_data, l)) { - printf("ERROR: F\n"); - error++; - } - if (error) { - dump_cc_info(ci, code); - exit(1); - } - } -} - -#ifdef PROFILE -static int64_t get_time_ns(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; -} -#endif - - -void check_flags(void) -{ - int c; - BOOL flag_ref, flag; - for(c = 0; c <= CHARCODE_MAX; c++) { - flag_ref = get_prop(c, PROP_Cased); - flag = !!lre_is_cased(c); - if (flag != flag_ref) { - printf("ERROR: c=%05x cased=%d ref=%d\n", - c, flag, flag_ref); - exit(1); - } - - flag_ref = get_prop(c, PROP_Case_Ignorable); - flag = !!lre_is_case_ignorable(c); - if (flag != flag_ref) { - printf("ERROR: c=%05x case_ignorable=%d ref=%d\n", - c, flag, flag_ref); - exit(1); - } - - flag_ref = get_prop(c, PROP_ID_Start); - flag = !!lre_is_id_start(c); - if (flag != flag_ref) { - printf("ERROR: c=%05x id_start=%d ref=%d\n", - c, flag, flag_ref); - exit(1); - } - - flag_ref = get_prop(c, PROP_ID_Continue); - flag = !!lre_is_id_continue(c); - if (flag != flag_ref) { - printf("ERROR: c=%05x id_cont=%d ref=%d\n", - c, flag, flag_ref); - exit(1); - } - } -#ifdef PROFILE - { - int64_t ti, count; - ti = get_time_ns(); - count = 0; - for(c = 0x20; c <= 0xffff; c++) { - flag_ref = get_prop(c, PROP_ID_Start); - flag = !!lre_is_id_start(c); - assert(flag == flag_ref); - count++; - } - ti = get_time_ns() - ti; - printf("flags time=%0.1f ns/char\n", - (double)ti / count); - } -#endif -} - -#endif - -#define CC_BLOCK_LEN 32 - -void build_cc_table(FILE *f) -{ - // Compress combining class table - // see: https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values - int i, cc, n, type, n1, block_end_pos; - DynBuf dbuf_s, *dbuf = &dbuf_s; - DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; -#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) - int cw_len_tab[3], cw_start, cc_table_len; -#endif - uint32_t v; - - dbuf_init(dbuf); - dbuf_init(dbuf1); -#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) - cc_table_len = 0; - for(i = 0; i < countof(cw_len_tab); i++) - cw_len_tab[i] = 0; -#endif - block_end_pos = CC_BLOCK_LEN; - for(i = 0; i <= CHARCODE_MAX;) { - cc = unicode_db[i].combining_class; - assert(cc <= 255); - /* check increasing values */ - n = 1; - while ((i + n) <= CHARCODE_MAX && - unicode_db[i + n].combining_class == (cc + n)) - n++; - if (n >= 2) { - type = 1; - } else { - type = 0; - n = 1; - while ((i + n) <= CHARCODE_MAX && - unicode_db[i + n].combining_class == cc) - n++; - } - /* no need to encode the last run */ - if (cc == 0 && (i + n - 1) == CHARCODE_MAX) - break; -#ifdef DUMP_CC_TABLE - printf("%05x %6d %d %d\n", i, n, type, cc); -#endif - if (type == 0) { - if (cc == 0) - type = 2; - else if (cc == 230) - type = 3; - } - n1 = n - 1; - - /* add an entry to the index if necessary */ - if (dbuf->size >= block_end_pos) { - v = i | ((dbuf->size - block_end_pos) << 21); - dbuf_putc(dbuf1, v); - dbuf_putc(dbuf1, v >> 8); - dbuf_putc(dbuf1, v >> 16); - block_end_pos += CC_BLOCK_LEN; - } -#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) - cw_start = dbuf->size; -#endif - /* Compressed run length encoding: - - 2 high order bits are combining class type - - 0:0, 1:230, 2:extra byte linear progression, 3:extra byte - - 00..2F: range length (add 1) - - 30..37: 3-bit range-length + 1 extra byte - - 38..3F: 3-bit range-length + 2 extra byte - */ - if (n1 < 48) { - dbuf_putc(dbuf, n1 | (type << 6)); - } else if (n1 < 48 + (1 << 11)) { - n1 -= 48; - dbuf_putc(dbuf, ((n1 >> 8) + 48) | (type << 6)); - dbuf_putc(dbuf, n1); - } else { - n1 -= 48 + (1 << 11); - assert(n1 < (1 << 20)); - dbuf_putc(dbuf, ((n1 >> 16) + 56) | (type << 6)); - dbuf_putc(dbuf, n1 >> 8); - dbuf_putc(dbuf, n1); - } -#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) - cw_len_tab[dbuf->size - cw_start - 1]++; - cc_table_len++; -#endif - if (type == 0 || type == 1) - dbuf_putc(dbuf, cc); - i += n; - } - - /* last index entry */ - v = i; - dbuf_putc(dbuf1, v); - dbuf_putc(dbuf1, v >> 8); - dbuf_putc(dbuf1, v >> 16); - - dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size); - dump_index_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size); - -#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) - printf("CC table: size=%d (%d entries) [", - (int)(dbuf->size + dbuf1->size), - cc_table_len); - for(i = 0; i < countof(cw_len_tab); i++) - printf(" %d", cw_len_tab[i]); - printf(" ]\n"); -#endif - dbuf_free(dbuf); - dbuf_free(dbuf1); -} - -/* maximum length of decomposition: 18 chars (1), then 8 */ -#ifndef USE_TEST -typedef enum { - DECOMP_TYPE_C1, /* 16 bit char */ - DECOMP_TYPE_L1, /* 16 bit char table */ - DECOMP_TYPE_L2, - DECOMP_TYPE_L3, - DECOMP_TYPE_L4, - DECOMP_TYPE_L5, /* XXX: not used */ - DECOMP_TYPE_L6, /* XXX: could remove */ - DECOMP_TYPE_L7, /* XXX: could remove */ - DECOMP_TYPE_LL1, /* 18 bit char table */ - DECOMP_TYPE_LL2, - DECOMP_TYPE_S1, /* 8 bit char table */ - DECOMP_TYPE_S2, - DECOMP_TYPE_S3, - DECOMP_TYPE_S4, - DECOMP_TYPE_S5, - DECOMP_TYPE_I1, /* increment 16 bit char value */ - DECOMP_TYPE_I2_0, - DECOMP_TYPE_I2_1, - DECOMP_TYPE_I3_1, - DECOMP_TYPE_I3_2, - DECOMP_TYPE_I4_1, - DECOMP_TYPE_I4_2, - DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ - DECOMP_TYPE_B2, - DECOMP_TYPE_B3, - DECOMP_TYPE_B4, - DECOMP_TYPE_B5, - DECOMP_TYPE_B6, - DECOMP_TYPE_B7, - DECOMP_TYPE_B8, - DECOMP_TYPE_B18, - DECOMP_TYPE_LS2, - DECOMP_TYPE_PAT3, - DECOMP_TYPE_S2_UL, - DECOMP_TYPE_LS2_UL, -} DecompTypeEnum; -#endif - -const char *decomp_type_str[] = { - "C1", - "L1", - "L2", - "L3", - "L4", - "L5", - "L6", - "L7", - "LL1", - "LL2", - "S1", - "S2", - "S3", - "S4", - "S5", - "I1", - "I2_0", - "I2_1", - "I3_1", - "I3_2", - "I4_1", - "I4_2", - "B1", - "B2", - "B3", - "B4", - "B5", - "B6", - "B7", - "B8", - "B18", - "LS2", - "PAT3", - "S2_UL", - "LS2_UL", -}; - -const int decomp_incr_tab[4][4] = { - { DECOMP_TYPE_I1, 0, -1 }, - { DECOMP_TYPE_I2_0, 0, 1, -1 }, - { DECOMP_TYPE_I3_1, 1, 2, -1 }, - { DECOMP_TYPE_I4_1, 1, 2, -1 }, -}; - -/* - entry size: - type bits - code 18 - len 7 - compat 1 - type 5 - index 16 - total 47 -*/ - -typedef struct { - int code; - uint8_t len; - uint8_t type; - uint8_t c_len; - uint16_t c_min; - uint16_t data_index; - int cost; /* size in bytes from this entry to the end */ -} DecompEntry; - -int get_decomp_run_size(const DecompEntry *de) -{ - int s; - s = 6; - if (de->type <= DECOMP_TYPE_C1) { - /* nothing more */ - } else if (de->type <= DECOMP_TYPE_L7) { - s += de->len * de->c_len * 2; - } else if (de->type <= DECOMP_TYPE_LL2) { - /* 18 bits per char */ - s += (de->len * de->c_len * 18 + 7) / 8; - } else if (de->type <= DECOMP_TYPE_S5) { - s += de->len * de->c_len; - } else if (de->type <= DECOMP_TYPE_I4_2) { - s += de->c_len * 2; - } else if (de->type <= DECOMP_TYPE_B18) { - s += 2 + de->len * de->c_len; - } else if (de->type <= DECOMP_TYPE_LS2) { - s += de->len * 3; - } else if (de->type <= DECOMP_TYPE_PAT3) { - s += 4 + de->len * 2; - } else if (de->type <= DECOMP_TYPE_S2_UL) { - s += de->len; - } else if (de->type <= DECOMP_TYPE_LS2_UL) { - s += (de->len / 2) * 3; - } else { - abort(); - } - return s; -} - -static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; - -/* return -1 if not found */ -int get_short_code(int c) -{ - int i; - if (c < 0x80) { - return c; - } else if (c >= 0x300 && c < 0x350) { - return c - 0x300 + 0x80; - } else { - for(i = 0; i < countof(unicode_short_table); i++) { - if (c == unicode_short_table[i]) - return i + 0x80 + 0x50; - } - return -1; - } -} - -static BOOL is_short(int code) -{ - return get_short_code(code) >= 0; -} - -static BOOL is_short_tab(const int *tab, int len) -{ - int i; - for(i = 0; i < len; i++) { - if (!is_short(tab[i])) - return FALSE; - } - return TRUE; -} - -static BOOL is_16bit(const int *tab, int len) -{ - int i; - for(i = 0; i < len; i++) { - if (tab[i] > 0xffff) - return FALSE; - } - return TRUE; -} - -static uint32_t to_lower_simple(uint32_t c) -{ - /* Latin1 and Cyrillic */ - if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) - c += 0x20; - else - c++; - return c; -} - -/* select best encoding with dynamic programming */ -void find_decomp_run(DecompEntry *tab_de, int i) -{ - DecompEntry de_s, *de = &de_s; - CCInfo *ci, *ci1, *ci2; - int l, j, n, len_max; - - ci = &unicode_db[i]; - l = ci->decomp_len; - if (l == 0) { - tab_de[i].cost = tab_de[i + 1].cost; - return; - } - - /* the offset for the compose table has only 6 bits, so we must - limit if it can be used by the compose table */ - if (!ci->is_compat && !ci->is_excluded && l == 2) - len_max = 64; - else - len_max = 127; - - tab_de[i].cost = 0x7fffffff; - - if (!is_16bit(ci->decomp_data, l)) { - assert(l <= 2); - - n = 1; - for(;;) { - de->code = i; - de->len = n; - de->type = DECOMP_TYPE_LL1 + l - 1; - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - /* Note: we accept a hole */ - if (!(ci1->decomp_len == 0 || - (ci1->decomp_len == l && - ci1->is_compat == ci->is_compat))) - break; - n++; - } - return; - } - - if (l <= 7) { - n = 1; - for(;;) { - de->code = i; - de->len = n; - if (l == 1 && n == 1) { - de->type = DECOMP_TYPE_C1; - } else { - assert(l <= 8); - de->type = DECOMP_TYPE_L1 + l - 1; - } - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - /* Note: we accept a hole */ - if (!(ci1->decomp_len == 0 || - (ci1->decomp_len == l && - ci1->is_compat == ci->is_compat && - is_16bit(ci1->decomp_data, l)))) - break; - n++; - } - } - - if (l <= 8 || l == 18) { - int c_min, c_max, c; - c_min = c_max = -1; - n = 1; - for(;;) { - ci1 = &unicode_db[i + n - 1]; - for(j = 0; j < l; j++) { - c = ci1->decomp_data[j]; - if (c == 0x20) { - /* we accept space for Arabic */ - } else if (c_min == -1) { - c_min = c_max = c; - } else { - c_min = min_int(c_min, c); - c_max = max_int(c_max, c); - } - } - if ((c_max - c_min) > 254) - break; - de->code = i; - de->len = n; - if (l == 18) - de->type = DECOMP_TYPE_B18; - else - de->type = DECOMP_TYPE_B1 + l - 1; - de->c_len = l; - de->c_min = c_min; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - if (!(ci1->decomp_len == l && - ci1->is_compat == ci->is_compat)) - break; - n++; - } - } - - /* find an ascii run */ - if (l <= 5 && is_short_tab(ci->decomp_data, l)) { - n = 1; - for(;;) { - de->code = i; - de->len = n; - de->type = DECOMP_TYPE_S1 + l - 1; - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - /* Note: we accept a hole */ - if (!(ci1->decomp_len == 0 || - (ci1->decomp_len == l && - ci1->is_compat == ci->is_compat && - is_short_tab(ci1->decomp_data, l)))) - break; - n++; - } - } - - /* check if a single char is increasing */ - if (l <= 4) { - int idx1, idx; - - for(idx1 = 1; (idx = decomp_incr_tab[l - 1][idx1]) >= 0; idx1++) { - n = 1; - for(;;) { - de->code = i; - de->len = n; - de->type = decomp_incr_tab[l - 1][0] + idx1 - 1; - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - if (!(ci1->decomp_len == l && - ci1->is_compat == ci->is_compat)) - goto next1; - for(j = 0; j < l; j++) { - if (j == idx) { - if (ci1->decomp_data[j] != ci->decomp_data[j] + n) - goto next1; - } else { - if (ci1->decomp_data[j] != ci->decomp_data[j]) - goto next1; - } - } - n++; - } - next1: ; - } - } - - if (l == 3) { - n = 1; - for(;;) { - de->code = i; - de->len = n; - de->type = DECOMP_TYPE_PAT3; - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - if (!(ci1->decomp_len == l && - ci1->is_compat == ci->is_compat && - ci1->decomp_data[1] <= 0xffff && - ci1->decomp_data[0] == ci->decomp_data[0] && - ci1->decomp_data[l - 1] == ci->decomp_data[l - 1])) - break; - n++; - } - } - - if (l == 2 && is_short(ci->decomp_data[1])) { - n = 1; - for(;;) { - de->code = i; - de->len = n; - de->type = DECOMP_TYPE_LS2; - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - if (!((i + n) <= CHARCODE_MAX && n < len_max)) - break; - ci1 = &unicode_db[i + n]; - if (!(ci1->decomp_len == 0 || - (ci1->decomp_len == l && - ci1->is_compat == ci->is_compat && - ci1->decomp_data[0] <= 0xffff && - is_short(ci1->decomp_data[1])))) - break; - n++; - } - } - - if (l == 2) { - BOOL is_16bit; - - n = 0; - is_16bit = FALSE; - for(;;) { - if (!((i + n + 1) <= CHARCODE_MAX && n + 2 <= len_max)) - break; - ci1 = &unicode_db[i + n]; - if (!(ci1->decomp_len == l && - ci1->is_compat == ci->is_compat && - is_short(ci1->decomp_data[1]))) - break; - if (!is_16bit && !is_short(ci1->decomp_data[0])) - is_16bit = TRUE; - ci2 = &unicode_db[i + n + 1]; - if (!(ci2->decomp_len == l && - ci2->is_compat == ci->is_compat && - ci2->decomp_data[0] == to_lower_simple(ci1->decomp_data[0]) && - ci2->decomp_data[1] == ci1->decomp_data[1])) - break; - n += 2; - de->code = i; - de->len = n; - de->type = DECOMP_TYPE_S2_UL + is_16bit; - de->c_len = l; - de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; - if (de->cost < tab_de[i].cost) { - tab_de[i] = *de; - } - } - } -} - -void put16(uint8_t *data_buf, int *pidx, uint16_t c) -{ - int idx; - idx = *pidx; - data_buf[idx++] = c; - data_buf[idx++] = c >> 8; - *pidx = idx; -} - -void add_decomp_data(uint8_t *data_buf, int *pidx, DecompEntry *de) -{ - int i, j, idx, c; - CCInfo *ci; - - idx = *pidx; - de->data_index = idx; - if (de->type <= DECOMP_TYPE_C1) { - ci = &unicode_db[de->code]; - assert(ci->decomp_len == 1); - de->data_index = ci->decomp_data[0]; - } else if (de->type <= DECOMP_TYPE_L7) { - for(i = 0; i < de->len; i++) { - ci = &unicode_db[de->code + i]; - for(j = 0; j < de->c_len; j++) { - if (ci->decomp_len == 0) - c = 0; - else - c = ci->decomp_data[j]; - put16(data_buf, &idx, c); - } - } - } else if (de->type <= DECOMP_TYPE_LL2) { - int n, p, k; - n = (de->len * de->c_len * 18 + 7) / 8; - p = de->len * de->c_len * 2; - memset(data_buf + idx, 0, n); - k = 0; - for(i = 0; i < de->len; i++) { - ci = &unicode_db[de->code + i]; - for(j = 0; j < de->c_len; j++) { - if (ci->decomp_len == 0) - c = 0; - else - c = ci->decomp_data[j]; - data_buf[idx + k * 2] = c; - data_buf[idx + k * 2 + 1] = c >> 8; - data_buf[idx + p + (k / 4)] |= (c >> 16) << ((k % 4) * 2); - k++; - } - } - idx += n; - } else if (de->type <= DECOMP_TYPE_S5) { - for(i = 0; i < de->len; i++) { - ci = &unicode_db[de->code + i]; - for(j = 0; j < de->c_len; j++) { - if (ci->decomp_len == 0) - c = 0; - else - c = ci->decomp_data[j]; - c = get_short_code(c); - assert(c >= 0); - data_buf[idx++] = c; - } - } - } else if (de->type <= DECOMP_TYPE_I4_2) { - ci = &unicode_db[de->code]; - assert(ci->decomp_len == de->c_len); - for(j = 0; j < de->c_len; j++) - put16(data_buf, &idx, ci->decomp_data[j]); - } else if (de->type <= DECOMP_TYPE_B18) { - c = de->c_min; - data_buf[idx++] = c; - data_buf[idx++] = c >> 8; - for(i = 0; i < de->len; i++) { - ci = &unicode_db[de->code + i]; - for(j = 0; j < de->c_len; j++) { - assert(ci->decomp_len == de->c_len); - c = ci->decomp_data[j]; - if (c == 0x20) { - c = 0xff; - } else { - c -= de->c_min; - assert((uint32_t)c <= 254); - } - data_buf[idx++] = c; - } - } - } else if (de->type <= DECOMP_TYPE_LS2) { - assert(de->c_len == 2); - for(i = 0; i < de->len; i++) { - ci = &unicode_db[de->code + i]; - if (ci->decomp_len == 0) - c = 0; - else - c = ci->decomp_data[0]; - put16(data_buf, &idx, c); - - if (ci->decomp_len == 0) - c = 0; - else - c = ci->decomp_data[1]; - c = get_short_code(c); - assert(c >= 0); - data_buf[idx++] = c; - } - } else if (de->type <= DECOMP_TYPE_PAT3) { - ci = &unicode_db[de->code]; - assert(ci->decomp_len == 3); - put16(data_buf, &idx, ci->decomp_data[0]); - put16(data_buf, &idx, ci->decomp_data[2]); - for(i = 0; i < de->len; i++) { - ci = &unicode_db[de->code + i]; - assert(ci->decomp_len == 3); - put16(data_buf, &idx, ci->decomp_data[1]); - } - } else if (de->type <= DECOMP_TYPE_S2_UL) { - for(i = 0; i < de->len; i += 2) { - ci = &unicode_db[de->code + i]; - c = ci->decomp_data[0]; - c = get_short_code(c); - assert(c >= 0); - data_buf[idx++] = c; - c = ci->decomp_data[1]; - c = get_short_code(c); - assert(c >= 0); - data_buf[idx++] = c; - } - } else if (de->type <= DECOMP_TYPE_LS2_UL) { - for(i = 0; i < de->len; i += 2) { - ci = &unicode_db[de->code + i]; - c = ci->decomp_data[0]; - put16(data_buf, &idx, c); - c = ci->decomp_data[1]; - c = get_short_code(c); - assert(c >= 0); - data_buf[idx++] = c; - } - } else { - abort(); - } - *pidx = idx; -} - -#if 0 -void dump_large_char(void) -{ - int i, j; - for(i = 0; i <= CHARCODE_MAX; i++) { - CCInfo *ci = &unicode_db[i]; - for(j = 0; j < ci->decomp_len; j++) { - if (ci->decomp_data[j] > 0xffff) - printf("%05x\n", ci->decomp_data[j]); - } - } -} -#endif - -void build_compose_table(FILE *f, const DecompEntry *tab_de); - -void build_decompose_table(FILE *f) -{ - int i, array_len, code_max, data_len, count; - DecompEntry *tab_de, de_s, *de = &de_s; - uint8_t *data_buf; - - code_max = CHARCODE_MAX; - - tab_de = mallocz((code_max + 2) * sizeof(*tab_de)); - - for(i = code_max; i >= 0; i--) { - find_decomp_run(tab_de, i); - } - - /* build the data buffer */ - data_buf = malloc(100000); - data_len = 0; - array_len = 0; - for(i = 0; i <= code_max; i++) { - de = &tab_de[i]; - if (de->len != 0) { - add_decomp_data(data_buf, &data_len, de); - i += de->len - 1; - array_len++; - } - } - -#ifdef DUMP_DECOMP_TABLE - /* dump */ - { - int size, size1; - - printf("START LEN TYPE L C SIZE\n"); - size = 0; - for(i = 0; i <= code_max; i++) { - de = &tab_de[i]; - if (de->len != 0) { - size1 = get_decomp_run_size(de); - printf("%05x %3d %6s %2d %1d %4d\n", i, de->len, - decomp_type_str[de->type], de->c_len, - unicode_db[i].is_compat, size1); - i += de->len - 1; - size += size1; - } - } - - printf("array_len=%d estimated size=%d bytes actual=%d bytes\n", - array_len, size, array_len * 6 + data_len); - } -#endif - - total_tables++; - total_table_bytes += array_len * sizeof(uint32_t); - fprintf(f, "static const uint32_t unicode_decomp_table1[%d] = {", array_len); - count = 0; - for(i = 0; i <= code_max; i++) { - de = &tab_de[i]; - if (de->len != 0) { - uint32_t v; - if (count++ % 4 == 0) - fprintf(f, "\n "); - v = (de->code << (32 - 18)) | - (de->len << (32 - 18 - 7)) | - (de->type << (32 - 18 - 7 - 6)) | - unicode_db[de->code].is_compat; - fprintf(f, " 0x%08x,", v); - i += de->len - 1; - } - } - fprintf(f, "\n};\n\n"); - - total_tables++; - total_table_bytes += array_len * sizeof(uint16_t); - fprintf(f, "static const uint16_t unicode_decomp_table2[%d] = {", array_len); - count = 0; - for(i = 0; i <= code_max; i++) { - de = &tab_de[i]; - if (de->len != 0) { - if (count++ % 8 == 0) - fprintf(f, "\n "); - fprintf(f, " 0x%04x,", de->data_index); - i += de->len - 1; - } - } - fprintf(f, "\n};\n\n"); - - total_tables++; - total_table_bytes += data_len; - fprintf(f, "static const uint8_t unicode_decomp_data[%d] = {", data_len); - for(i = 0; i < data_len; i++) { - if (i % 8 == 0) - fprintf(f, "\n "); - fprintf(f, " 0x%02x,", data_buf[i]); - } - fprintf(f, "\n};\n\n"); - - build_compose_table(f, tab_de); - - free(data_buf); - - free(tab_de); -} - -typedef struct { - uint32_t c[2]; - uint32_t p; -} ComposeEntry; - -#define COMPOSE_LEN_MAX 10000 - -static int ce_cmp(const void *p1, const void *p2) -{ - const ComposeEntry *ce1 = p1; - const ComposeEntry *ce2 = p2; - int i; - - for(i = 0; i < 2; i++) { - if (ce1->c[i] < ce2->c[i]) - return -1; - else if (ce1->c[i] > ce2->c[i]) - return 1; - } - return 0; -} - - -static int get_decomp_pos(const DecompEntry *tab_de, int c) -{ - int i, v, k; - const DecompEntry *de; - - k = 0; - for(i = 0; i <= CHARCODE_MAX; i++) { - de = &tab_de[i]; - if (de->len != 0) { - if (c >= de->code && c < de->code + de->len) { - v = c - de->code; - assert(v < 64); - v |= k << 6; - assert(v < 65536); - return v; - } - i += de->len - 1; - k++; - } - } - return -1; -} - -void build_compose_table(FILE *f, const DecompEntry *tab_de) -{ - int i, v, tab_ce_len; - ComposeEntry *ce, *tab_ce; - - tab_ce = malloc(sizeof(*tab_ce) * COMPOSE_LEN_MAX); - tab_ce_len = 0; - for(i = 0; i <= CHARCODE_MAX; i++) { - CCInfo *ci = &unicode_db[i]; - if (ci->decomp_len == 2 && !ci->is_compat && - !ci->is_excluded) { - assert(tab_ce_len < COMPOSE_LEN_MAX); - ce = &tab_ce[tab_ce_len++]; - ce->c[0] = ci->decomp_data[0]; - ce->c[1] = ci->decomp_data[1]; - ce->p = i; - } - } - qsort(tab_ce, tab_ce_len, sizeof(*tab_ce), ce_cmp); - -#if 0 - { - printf("tab_ce_len=%d\n", tab_ce_len); - for(i = 0; i < tab_ce_len; i++) { - ce = &tab_ce[i]; - printf("%05x %05x %05x\n", ce->c[0], ce->c[1], ce->p); - } - } -#endif - - total_tables++; - total_table_bytes += tab_ce_len * sizeof(uint16_t); - fprintf(f, "static const uint16_t unicode_comp_table[%u] = {", tab_ce_len); - for(i = 0; i < tab_ce_len; i++) { - if (i % 8 == 0) - fprintf(f, "\n "); - v = get_decomp_pos(tab_de, tab_ce[i].p); - if (v < 0) { - printf("ERROR: entry for c=%04x not found\n", - tab_ce[i].p); - exit(1); - } - fprintf(f, " 0x%04x,", v); - } - fprintf(f, "\n};\n\n"); - - free(tab_ce); -} - -#ifdef USE_TEST -void check_decompose_table(void) -{ - int c; - CCInfo *ci; - int res[UNICODE_DECOMP_LEN_MAX], *ref; - int len, ref_len, is_compat; - - for(is_compat = 0; is_compat <= 1; is_compat++) { - for(c = 0; c < CHARCODE_MAX; c++) { - ci = &unicode_db[c]; - ref_len = ci->decomp_len; - ref = ci->decomp_data; - if (!is_compat && ci->is_compat) { - ref_len = 0; - } - len = unicode_decomp_char((uint32_t *)res, c, is_compat); - if (len != ref_len || - tabcmp(res, ref, ref_len) != 0) { - printf("ERROR c=%05x compat=%d\n", c, is_compat); - dump_str("res", res, len); - dump_str("ref", ref, ref_len); - exit(1); - } - } - } -} - -void check_compose_table(void) -{ - int i, p; - /* XXX: we don't test all the cases */ - - for(i = 0; i <= CHARCODE_MAX; i++) { - CCInfo *ci = &unicode_db[i]; - if (ci->decomp_len == 2 && !ci->is_compat && - !ci->is_excluded) { - p = unicode_compose_pair(ci->decomp_data[0], ci->decomp_data[1]); - if (p != i) { - printf("ERROR compose: c=%05x %05x -> %05x ref=%05x\n", - ci->decomp_data[0], ci->decomp_data[1], p, i); - exit(1); - } - } - } - - - -} - -#endif - - - -#ifdef USE_TEST - -void check_str(const char *msg, int num, const int *in_buf, int in_len, - const int *buf1, int len1, - const int *buf2, int len2) -{ - if (len1 != len2 || tabcmp(buf1, buf2, len1) != 0) { - printf("%d: ERROR %s:\n", num, msg); - dump_str(" in", in_buf, in_len); - dump_str("res", buf1, len1); - dump_str("ref", buf2, len2); - exit(1); - } -} - -void check_cc_table(void) -{ - int cc, cc_ref, c; - - for(c = 0; c <= CHARCODE_MAX; c++) { - cc_ref = unicode_db[c].combining_class; - cc = unicode_get_cc(c); - if (cc != cc_ref) { - printf("ERROR: c=%04x cc=%d cc_ref=%d\n", - c, cc, cc_ref); - exit(1); - } - } -#ifdef PROFILE - { - int64_t ti, count; - - ti = get_time_ns(); - count = 0; - /* only do it on meaningful chars */ - for(c = 0x20; c <= 0xffff; c++) { - cc_ref = unicode_db[c].combining_class; - cc = unicode_get_cc(c); - count++; - } - ti = get_time_ns() - ti; - printf("cc time=%0.1f ns/char\n", - (double)ti / count); - } -#endif -} - -void normalization_test(const char *filename) -{ - FILE *f; - char line[4096], *p; - int *in_str, *nfc_str, *nfd_str, *nfkc_str, *nfkd_str; - int in_len, nfc_len, nfd_len, nfkc_len, nfkd_len; - int *buf, buf_len, pos; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - pos = 0; - for(;;) { - if (!get_line(line, sizeof(line), f)) - break; - pos++; - p = line; - while (isspace(*p)) - p++; - if (*p == '#' || *p == '@') - continue; - in_str = get_field_str(&in_len, p, 0); - nfc_str = get_field_str(&nfc_len, p, 1); - nfd_str = get_field_str(&nfd_len, p, 2); - nfkc_str = get_field_str(&nfkc_len, p, 3); - nfkd_str = get_field_str(&nfkd_len, p, 4); - - // dump_str("in", in_str, in_len); - - buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFD, NULL, NULL); - check_str("nfd", pos, in_str, in_len, buf, buf_len, nfd_str, nfd_len); - free(buf); - - buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKD, NULL, NULL); - check_str("nfkd", pos, in_str, in_len, buf, buf_len, nfkd_str, nfkd_len); - free(buf); - - buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFC, NULL, NULL); - check_str("nfc", pos, in_str, in_len, buf, buf_len, nfc_str, nfc_len); - free(buf); - - buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKC, NULL, NULL); - check_str("nfkc", pos, in_str, in_len, buf, buf_len, nfkc_str, nfkc_len); - free(buf); - - free(in_str); - free(nfc_str); - free(nfd_str); - free(nfkc_str); - free(nfkd_str); - } - fclose(f); -} -#endif - -int main(int argc, char *argv[]) -{ - const char *unicode_db_path, *outfilename; - char filename[1024]; - int arg = 1; - - if (arg >= argc || (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "--help"))) { - printf("usage: %s PATH [OUTPUT]\n" - " PATH path to the Unicode database directory\n" - " OUTPUT name of the output file. If omitted, a self test is performed\n" - " using the files from the Unicode library\n" - , argv[0]); - return 1; - } - unicode_db_path = argv[arg++]; - outfilename = NULL; - if (arg < argc) - outfilename = argv[arg++]; - - unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1)); - re_string_list_init(&rgi_emoji_zwj_sequence); - dbuf_init(&rgi_emoji_tag_sequence); - - snprintf(filename, sizeof(filename), "%s/UnicodeData.txt", unicode_db_path); - - parse_unicode_data(filename); - - snprintf(filename, sizeof(filename), "%s/SpecialCasing.txt", unicode_db_path); - parse_special_casing(unicode_db, filename); - - snprintf(filename, sizeof(filename), "%s/CaseFolding.txt", unicode_db_path); - parse_case_folding(unicode_db, filename); - - snprintf(filename, sizeof(filename), "%s/CompositionExclusions.txt", unicode_db_path); - parse_composition_exclusions(filename); - - snprintf(filename, sizeof(filename), "%s/DerivedCoreProperties.txt", unicode_db_path); - parse_derived_core_properties(filename); - - snprintf(filename, sizeof(filename), "%s/DerivedNormalizationProps.txt", unicode_db_path); - parse_derived_norm_properties(filename); - - snprintf(filename, sizeof(filename), "%s/PropList.txt", unicode_db_path); - parse_prop_list(filename); - - snprintf(filename, sizeof(filename), "%s/Scripts.txt", unicode_db_path); - parse_scripts(filename); - - snprintf(filename, sizeof(filename), "%s/ScriptExtensions.txt", - unicode_db_path); - parse_script_extensions(filename); - - snprintf(filename, sizeof(filename), "%s/emoji-data.txt", - unicode_db_path); - parse_prop_list(filename); - - snprintf(filename, sizeof(filename), "%s/emoji-sequences.txt", - unicode_db_path); - parse_sequence_prop_list(filename); - - snprintf(filename, sizeof(filename), "%s/emoji-zwj-sequences.txt", - unicode_db_path); - parse_sequence_prop_list(filename); - - // dump_unicode_data(unicode_db); - build_conv_table(unicode_db); - -#ifdef DUMP_CASE_FOLDING_SPECIAL_CASES - dump_case_folding_special_cases(unicode_db); -#endif - - if (!outfilename) { -#ifdef USE_TEST - check_case_conv(); - check_flags(); - check_decompose_table(); - check_compose_table(); - check_cc_table(); - snprintf(filename, sizeof(filename), "%s/NormalizationTest.txt", unicode_db_path); - normalization_test(filename); -#else - fprintf(stderr, "Tests are not compiled\n"); - exit(1); -#endif - } else - { - FILE *fo = fopen(outfilename, "wb"); - - if (!fo) { - perror(outfilename); - exit(1); - } - fprintf(fo, - "/* Compressed unicode tables */\n" - "/* Automatically generated file - do not edit */\n" - "\n" - "#include \n" - "\n"); - dump_case_conv_table(fo); - compute_internal_props(); - build_flags_tables(fo); - fprintf(fo, "#ifdef CONFIG_ALL_UNICODE\n\n"); - build_cc_table(fo); - build_decompose_table(fo); - build_general_category_table(fo); - build_script_table(fo); - build_script_ext_table(fo); - build_prop_list_table(fo); - build_sequence_prop_list_table(fo); - fprintf(fo, "#endif /* CONFIG_ALL_UNICODE */\n"); - fprintf(fo, "/* %u tables / %u bytes, %u index / %u bytes */\n", - total_tables, total_table_bytes, total_index, total_index_bytes); - fclose(fo); - } - re_string_list_free(&rgi_emoji_zwj_sequence); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen_def.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen_def.h deleted file mode 100755 index 95c369fa..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen_def.h +++ /dev/null @@ -1,318 +0,0 @@ -#ifdef UNICODE_GENERAL_CATEGORY -DEF(Cn, "Unassigned") /* must be zero */ -DEF(Lu, "Uppercase_Letter") -DEF(Ll, "Lowercase_Letter") -DEF(Lt, "Titlecase_Letter") -DEF(Lm, "Modifier_Letter") -DEF(Lo, "Other_Letter") -DEF(Mn, "Nonspacing_Mark") -DEF(Mc, "Spacing_Mark") -DEF(Me, "Enclosing_Mark") -DEF(Nd, "Decimal_Number,digit") -DEF(Nl, "Letter_Number") -DEF(No, "Other_Number") -DEF(Sm, "Math_Symbol") -DEF(Sc, "Currency_Symbol") -DEF(Sk, "Modifier_Symbol") -DEF(So, "Other_Symbol") -DEF(Pc, "Connector_Punctuation") -DEF(Pd, "Dash_Punctuation") -DEF(Ps, "Open_Punctuation") -DEF(Pe, "Close_Punctuation") -DEF(Pi, "Initial_Punctuation") -DEF(Pf, "Final_Punctuation") -DEF(Po, "Other_Punctuation") -DEF(Zs, "Space_Separator") -DEF(Zl, "Line_Separator") -DEF(Zp, "Paragraph_Separator") -DEF(Cc, "Control,cntrl") -DEF(Cf, "Format") -DEF(Cs, "Surrogate") -DEF(Co, "Private_Use") -/* synthetic properties */ -DEF(LC, "Cased_Letter") -DEF(L, "Letter") -DEF(M, "Mark,Combining_Mark") -DEF(N, "Number") -DEF(S, "Symbol") -DEF(P, "Punctuation,punct") -DEF(Z, "Separator") -DEF(C, "Other") -#endif - -#ifdef UNICODE_SCRIPT -/* scripts aliases names in PropertyValueAliases.txt */ -DEF(Unknown, "Zzzz") -DEF(Adlam, "Adlm") -DEF(Ahom, "Ahom") -DEF(Anatolian_Hieroglyphs, "Hluw") -DEF(Arabic, "Arab") -DEF(Armenian, "Armn") -DEF(Avestan, "Avst") -DEF(Balinese, "Bali") -DEF(Bamum, "Bamu") -DEF(Bassa_Vah, "Bass") -DEF(Batak, "Batk") -DEF(Bengali, "Beng") -DEF(Bhaiksuki, "Bhks") -DEF(Bopomofo, "Bopo") -DEF(Brahmi, "Brah") -DEF(Braille, "Brai") -DEF(Buginese, "Bugi") -DEF(Buhid, "Buhd") -DEF(Canadian_Aboriginal, "Cans") -DEF(Carian, "Cari") -DEF(Caucasian_Albanian, "Aghb") -DEF(Chakma, "Cakm") -DEF(Cham, "Cham") -DEF(Cherokee, "Cher") -DEF(Chorasmian, "Chrs") -DEF(Common, "Zyyy") -DEF(Coptic, "Copt,Qaac") -DEF(Cuneiform, "Xsux") -DEF(Cypriot, "Cprt") -DEF(Cyrillic, "Cyrl") -DEF(Cypro_Minoan, "Cpmn") -DEF(Deseret, "Dsrt") -DEF(Devanagari, "Deva") -DEF(Dives_Akuru, "Diak") -DEF(Dogra, "Dogr") -DEF(Duployan, "Dupl") -DEF(Egyptian_Hieroglyphs, "Egyp") -DEF(Elbasan, "Elba") -DEF(Elymaic, "Elym") -DEF(Ethiopic, "Ethi") -DEF(Garay, "Gara") -DEF(Georgian, "Geor") -DEF(Glagolitic, "Glag") -DEF(Gothic, "Goth") -DEF(Grantha, "Gran") -DEF(Greek, "Grek") -DEF(Gujarati, "Gujr") -DEF(Gunjala_Gondi, "Gong") -DEF(Gurmukhi, "Guru") -DEF(Gurung_Khema, "Gukh") -DEF(Han, "Hani") -DEF(Hangul, "Hang") -DEF(Hanifi_Rohingya, "Rohg") -DEF(Hanunoo, "Hano") -DEF(Hatran, "Hatr") -DEF(Hebrew, "Hebr") -DEF(Hiragana, "Hira") -DEF(Imperial_Aramaic, "Armi") -DEF(Inherited, "Zinh,Qaai") -DEF(Inscriptional_Pahlavi, "Phli") -DEF(Inscriptional_Parthian, "Prti") -DEF(Javanese, "Java") -DEF(Kaithi, "Kthi") -DEF(Kannada, "Knda") -DEF(Katakana, "Kana") -DEF(Kawi, "Kawi") -DEF(Kayah_Li, "Kali") -DEF(Kharoshthi, "Khar") -DEF(Khmer, "Khmr") -DEF(Khojki, "Khoj") -DEF(Khitan_Small_Script, "Kits") -DEF(Khudawadi, "Sind") -DEF(Kirat_Rai, "Krai") -DEF(Lao, "Laoo") -DEF(Latin, "Latn") -DEF(Lepcha, "Lepc") -DEF(Limbu, "Limb") -DEF(Linear_A, "Lina") -DEF(Linear_B, "Linb") -DEF(Lisu, "Lisu") -DEF(Lycian, "Lyci") -DEF(Lydian, "Lydi") -DEF(Makasar, "Maka") -DEF(Mahajani, "Mahj") -DEF(Malayalam, "Mlym") -DEF(Mandaic, "Mand") -DEF(Manichaean, "Mani") -DEF(Marchen, "Marc") -DEF(Masaram_Gondi, "Gonm") -DEF(Medefaidrin, "Medf") -DEF(Meetei_Mayek, "Mtei") -DEF(Mende_Kikakui, "Mend") -DEF(Meroitic_Cursive, "Merc") -DEF(Meroitic_Hieroglyphs, "Mero") -DEF(Miao, "Plrd") -DEF(Modi, "Modi") -DEF(Mongolian, "Mong") -DEF(Mro, "Mroo") -DEF(Multani, "Mult") -DEF(Myanmar, "Mymr") -DEF(Nabataean, "Nbat") -DEF(Nag_Mundari, "Nagm") -DEF(Nandinagari, "Nand") -DEF(New_Tai_Lue, "Talu") -DEF(Newa, "Newa") -DEF(Nko, "Nkoo") -DEF(Nushu, "Nshu") -DEF(Nyiakeng_Puachue_Hmong, "Hmnp") -DEF(Ogham, "Ogam") -DEF(Ol_Chiki, "Olck") -DEF(Ol_Onal, "Onao") -DEF(Old_Hungarian, "Hung") -DEF(Old_Italic, "Ital") -DEF(Old_North_Arabian, "Narb") -DEF(Old_Permic, "Perm") -DEF(Old_Persian, "Xpeo") -DEF(Old_Sogdian, "Sogo") -DEF(Old_South_Arabian, "Sarb") -DEF(Old_Turkic, "Orkh") -DEF(Old_Uyghur, "Ougr") -DEF(Oriya, "Orya") -DEF(Osage, "Osge") -DEF(Osmanya, "Osma") -DEF(Pahawh_Hmong, "Hmng") -DEF(Palmyrene, "Palm") -DEF(Pau_Cin_Hau, "Pauc") -DEF(Phags_Pa, "Phag") -DEF(Phoenician, "Phnx") -DEF(Psalter_Pahlavi, "Phlp") -DEF(Rejang, "Rjng") -DEF(Runic, "Runr") -DEF(Samaritan, "Samr") -DEF(Saurashtra, "Saur") -DEF(Sharada, "Shrd") -DEF(Shavian, "Shaw") -DEF(Siddham, "Sidd") -DEF(SignWriting, "Sgnw") -DEF(Sinhala, "Sinh") -DEF(Sogdian, "Sogd") -DEF(Sora_Sompeng, "Sora") -DEF(Soyombo, "Soyo") -DEF(Sundanese, "Sund") -DEF(Sunuwar, "Sunu") -DEF(Syloti_Nagri, "Sylo") -DEF(Syriac, "Syrc") -DEF(Tagalog, "Tglg") -DEF(Tagbanwa, "Tagb") -DEF(Tai_Le, "Tale") -DEF(Tai_Tham, "Lana") -DEF(Tai_Viet, "Tavt") -DEF(Takri, "Takr") -DEF(Tamil, "Taml") -DEF(Tangut, "Tang") -DEF(Telugu, "Telu") -DEF(Thaana, "Thaa") -DEF(Thai, "Thai") -DEF(Tibetan, "Tibt") -DEF(Tifinagh, "Tfng") -DEF(Tirhuta, "Tirh") -DEF(Tangsa, "Tnsa") -DEF(Todhri, "Todr") -DEF(Toto, "Toto") -DEF(Tulu_Tigalari, "Tutg") -DEF(Ugaritic, "Ugar") -DEF(Vai, "Vaii") -DEF(Vithkuqi, "Vith") -DEF(Wancho, "Wcho") -DEF(Warang_Citi, "Wara") -DEF(Yezidi, "Yezi") -DEF(Yi, "Yiii") -DEF(Zanabazar_Square, "Zanb") -#endif - -#ifdef UNICODE_PROP_LIST -/* Prop list not exported to regexp */ -DEF(Hyphen, "") -DEF(Other_Math, "") -DEF(Other_Alphabetic, "") -DEF(Other_Lowercase, "") -DEF(Other_Uppercase, "") -DEF(Other_Grapheme_Extend, "") -DEF(Other_Default_Ignorable_Code_Point, "") -DEF(Other_ID_Start, "") -DEF(Other_ID_Continue, "") -DEF(Prepended_Concatenation_Mark, "") -/* additional computed properties for smaller tables */ -DEF(ID_Continue1, "") -DEF(XID_Start1, "") -DEF(XID_Continue1, "") -DEF(Changes_When_Titlecased1, "") -DEF(Changes_When_Casefolded1, "") -DEF(Changes_When_NFKC_Casefolded1, "") -DEF(Basic_Emoji1, "") -DEF(Basic_Emoji2, "") -DEF(RGI_Emoji_Modifier_Sequence, "") -DEF(RGI_Emoji_Flag_Sequence, "") -DEF(Emoji_Keycap_Sequence, "") - -/* Prop list exported to JS */ -DEF(ASCII_Hex_Digit, "AHex") -DEF(Bidi_Control, "Bidi_C") -DEF(Dash, "") -DEF(Deprecated, "Dep") -DEF(Diacritic, "Dia") -DEF(Extender, "Ext") -DEF(Hex_Digit, "Hex") -DEF(IDS_Unary_Operator, "IDSU") -DEF(IDS_Binary_Operator, "IDSB") -DEF(IDS_Trinary_Operator, "IDST") -DEF(Ideographic, "Ideo") -DEF(Join_Control, "Join_C") -DEF(Logical_Order_Exception, "LOE") -DEF(Modifier_Combining_Mark, "MCM") -DEF(Noncharacter_Code_Point, "NChar") -DEF(Pattern_Syntax, "Pat_Syn") -DEF(Pattern_White_Space, "Pat_WS") -DEF(Quotation_Mark, "QMark") -DEF(Radical, "") -DEF(Regional_Indicator, "RI") -DEF(Sentence_Terminal, "STerm") -DEF(Soft_Dotted, "SD") -DEF(Terminal_Punctuation, "Term") -DEF(Unified_Ideograph, "UIdeo") -DEF(Variation_Selector, "VS") -DEF(White_Space, "space") -DEF(Bidi_Mirrored, "Bidi_M") -DEF(Emoji, "") -DEF(Emoji_Component, "EComp") -DEF(Emoji_Modifier, "EMod") -DEF(Emoji_Modifier_Base, "EBase") -DEF(Emoji_Presentation, "EPres") -DEF(Extended_Pictographic, "ExtPict") -DEF(Default_Ignorable_Code_Point, "DI") -DEF(ID_Start, "IDS") -DEF(Case_Ignorable, "CI") - -/* other binary properties */ -DEF(ASCII,"") -DEF(Alphabetic, "Alpha") -DEF(Any, "") -DEF(Assigned,"") -DEF(Cased, "") -DEF(Changes_When_Casefolded, "CWCF") -DEF(Changes_When_Casemapped, "CWCM") -DEF(Changes_When_Lowercased, "CWL") -DEF(Changes_When_NFKC_Casefolded, "CWKCF") -DEF(Changes_When_Titlecased, "CWT") -DEF(Changes_When_Uppercased, "CWU") -DEF(Grapheme_Base, "Gr_Base") -DEF(Grapheme_Extend, "Gr_Ext") -DEF(ID_Continue, "IDC") -DEF(ID_Compat_Math_Start, "") -DEF(ID_Compat_Math_Continue, "") -DEF(InCB, "") -DEF(Lowercase, "Lower") -DEF(Math, "") -DEF(Uppercase, "Upper") -DEF(XID_Continue, "XIDC") -DEF(XID_Start, "XIDS") - -/* internal tables with index */ -DEF(Cased1, "") - -#endif - -#ifdef UNICODE_SEQUENCE_PROP_LIST -DEF(Basic_Emoji) -DEF(Emoji_Keycap_Sequence) -DEF(RGI_Emoji_Modifier_Sequence) -DEF(RGI_Emoji_Flag_Sequence) -DEF(RGI_Emoji_Tag_Sequence) -DEF(RGI_Emoji_ZWJ_Sequence) -DEF(RGI_Emoji) -#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng new file mode 160000 index 00000000..d950d55e --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng @@ -0,0 +1 @@ +Subproject commit d950d55e950dd7994a96c669c31efa967b8b79f3 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/CMakeLists.txt b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/CMakeLists.txt deleted file mode 100755 index f2ada27e..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/CMakeLists.txt +++ /dev/null @@ -1,461 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -project(quickjs LANGUAGES C) - -include(CheckCCompilerFlag) -include(GNUInstallDirs) - -set(CMAKE_C_VISIBILITY_PRESET hidden) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_C_EXTENSIONS ON) -set(CMAKE_C_STANDARD 11) - -# MINGW doesn't exist in older cmake versions, newer versions don't know -# about CMAKE_COMPILER_IS_MINGW, and there is no unique CMAKE_C_COMPILER_ID -# for mingw-based compilers... -if(MINGW) - # do nothing -elseif(CMAKE_C_COMPILER MATCHES "mingw") - set(MINGW TRUE) -else() - set(MINGW FALSE) -endif() - -if(CMAKE_SYSTEM_NAME STREQUAL "tvOS") - set(TVOS TRUE) -else() - set(TVOS FALSE) -endif() -if(CMAKE_SYSTEM_NAME STREQUAL "watchOS") - set(WATCHOS TRUE) -else() - set(WATCHOS FALSE) -endif() - -if(NOT CMAKE_BUILD_TYPE) - message(STATUS "No build type selected, default to Release") - set(CMAKE_BUILD_TYPE "Release") -endif() - -message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode") -message(STATUS "Building with ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} on ${CMAKE_SYSTEM}") - -macro(xcheck_add_c_compiler_flag FLAG) - string(REPLACE "-" "" FLAG_NO_HYPHEN ${FLAG}) - check_c_compiler_flag(${FLAG} COMPILER_SUPPORTS_${FLAG_NO_HYPHEN}) - if(COMPILER_SUPPORTS_${FLAG_NO_HYPHEN}) - add_compile_options(${FLAG}) - endif() -endmacro() - -xcheck_add_c_compiler_flag(-Wall) -if(NOT MSVC AND NOT IOS AND NOT TVOS AND NOT WATCHOS) - xcheck_add_c_compiler_flag(-Werror) - xcheck_add_c_compiler_flag(-Wextra) -endif() -xcheck_add_c_compiler_flag(-Wformat=2) -xcheck_add_c_compiler_flag(-Wno-implicit-fallthrough) -xcheck_add_c_compiler_flag(-Wno-sign-compare) -xcheck_add_c_compiler_flag(-Wno-missing-field-initializers) -xcheck_add_c_compiler_flag(-Wno-unused-parameter) -xcheck_add_c_compiler_flag(-Wno-unused-but-set-variable) -xcheck_add_c_compiler_flag(-Wno-unused-result) -xcheck_add_c_compiler_flag(-Wno-stringop-truncation) -xcheck_add_c_compiler_flag(-Wno-array-bounds) -xcheck_add_c_compiler_flag(-funsigned-char) - -# ClangCL is command line compatible with MSVC, so 'MSVC' is set. -if(MSVC) - xcheck_add_c_compiler_flag(-Wno-unsafe-buffer-usage) - xcheck_add_c_compiler_flag(-Wno-sign-conversion) - xcheck_add_c_compiler_flag(-Wno-nonportable-system-include-path) - xcheck_add_c_compiler_flag(-Wno-implicit-int-conversion) - xcheck_add_c_compiler_flag(-Wno-shorten-64-to-32) - xcheck_add_c_compiler_flag(-Wno-reserved-macro-identifier) - xcheck_add_c_compiler_flag(-Wno-reserved-identifier) - xcheck_add_c_compiler_flag(-Wdeprecated-declarations) - xcheck_add_c_compiler_flag(/experimental:c11atomics) - xcheck_add_c_compiler_flag(/wd4018) # -Wno-sign-conversion - xcheck_add_c_compiler_flag(/wd4061) # -Wno-implicit-fallthrough - xcheck_add_c_compiler_flag(/wd4100) # -Wno-unused-parameter - xcheck_add_c_compiler_flag(/wd4200) # -Wno-zero-length-array - xcheck_add_c_compiler_flag(/wd4242) # -Wno-shorten-64-to-32 - xcheck_add_c_compiler_flag(/wd4244) # -Wno-shorten-64-to-32 - xcheck_add_c_compiler_flag(/wd4245) # -Wno-sign-compare - xcheck_add_c_compiler_flag(/wd4267) # -Wno-shorten-64-to-32 - xcheck_add_c_compiler_flag(/wd4388) # -Wno-sign-compare - xcheck_add_c_compiler_flag(/wd4389) # -Wno-sign-compare - xcheck_add_c_compiler_flag(/wd4456) # Hides previous local declaration - xcheck_add_c_compiler_flag(/wd4457) # Hides function parameter - xcheck_add_c_compiler_flag(/wd4710) # Function not inlined - xcheck_add_c_compiler_flag(/wd4711) # Function was inlined - xcheck_add_c_compiler_flag(/wd4820) # Padding added after construct - xcheck_add_c_compiler_flag(/wd4996) # -Wdeprecated-declarations - xcheck_add_c_compiler_flag(/wd5045) # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified -endif() - -# Set a 8MB default stack size on Windows. -# It defaults to 1MB on MSVC, which is the same as our current JS stack size, -# so it will overflow and crash otherwise. -# On MinGW it defaults to 2MB. -if(WIN32) - if(MSVC) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") - else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608") - endif() -endif() - -# MacOS and GCC 11 or later need -Wno-maybe-uninitialized -# https://github.com/quickjs-ng/quickjs/issues/453 -if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11) - xcheck_add_c_compiler_flag(-Wno-maybe-uninitialized) -endif() - -if(CMAKE_SYSTEM_NAME STREQUAL "WASI") - add_compile_definitions( - _WASI_EMULATED_PROCESS_CLOCKS - _WASI_EMULATED_SIGNAL - ) - add_link_options( - -lwasi-emulated-process-clocks - -lwasi-emulated-signal - ) -endif() - -if(CMAKE_BUILD_TYPE MATCHES "Debug") - xcheck_add_c_compiler_flag(/Od) - xcheck_add_c_compiler_flag(-O0) - xcheck_add_c_compiler_flag(-ggdb) - xcheck_add_c_compiler_flag(-fno-omit-frame-pointer) -endif() - -macro(xoption OPTION_NAME OPTION_TEXT OPTION_DEFAULT) - option(${OPTION_NAME} ${OPTION_TEXT} ${OPTION_DEFAULT}) - if(DEFINED ENV{${OPTION_NAME}}) - # Allow setting the option through an environment variable. - set(${OPTION_NAME} $ENV{${OPTION_NAME}}) - endif() - if(${OPTION_NAME}) - add_definitions(-D${OPTION_NAME}) - endif() - message(STATUS " ${OPTION_NAME}: ${${OPTION_NAME}}") -endmacro() - -xoption(BUILD_SHARED_LIBS "Build a shared library" OFF) -if(BUILD_SHARED_LIBS) - message(STATUS "Building a shared library") -endif() - -# note: QJS_ENABLE_TSAN is currently incompatible with the other sanitizers but we -# don't explicitly check for that because who knows what the future will bring? -# QJS_ENABLE_MSAN only works with clang at the time of writing; also not checked -# for the same reason -xoption(QJS_BUILD_EXAMPLES "Build examples" OFF) -xoption(QJS_BUILD_CLI_STATIC "Build a static qjs executable" OFF) -xoption(QJS_BUILD_CLI_WITH_MIMALLOC "Build the qjs executable with mimalloc" OFF) -xoption(QJS_BUILD_CLI_WITH_STATIC_MIMALLOC "Build the qjs executable with mimalloc (statically linked)" OFF) -xoption(QJS_DISABLE_PARSER "Disable JS source code parser" OFF) -xoption(QJS_ENABLE_ASAN "Enable AddressSanitizer (ASan)" OFF) -xoption(QJS_ENABLE_MSAN "Enable MemorySanitizer (MSan)" OFF) -xoption(QJS_ENABLE_TSAN "Enable ThreadSanitizer (TSan)" OFF) -xoption(QJS_ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan)" OFF) - -if(QJS_ENABLE_ASAN) -message(STATUS "Building with ASan") -add_compile_options( - -fsanitize=address - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -add_link_options( - -fsanitize=address - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -endif() - -if(QJS_ENABLE_MSAN) -message(STATUS "Building with MSan") -add_compile_options( - -fsanitize=memory - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -add_link_options( - -fsanitize=memory - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -endif() - -if(QJS_ENABLE_TSAN) -message(STATUS "Building with TSan") -add_compile_options( - -fsanitize=thread - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -add_link_options( - -fsanitize=thread - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -endif() - -if(QJS_ENABLE_UBSAN) -message(STATUS "Building with UBSan") -add_compile_options( - -fsanitize=undefined - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -add_link_options( - -fsanitize=undefined - -fno-sanitize-recover=all - -fno-omit-frame-pointer -) -endif() - - -# QuickJS library -# - -xoption(QJS_BUILD_LIBC "Build standard library modules as part of the library" OFF) -macro(add_qjs_libc_if_needed target) - if(NOT QJS_BUILD_LIBC) - target_sources(${target} PRIVATE quickjs-libc.c) - endif() -endmacro() -macro(add_static_if_needed target) - if(QJS_BUILD_CLI_STATIC OR MINGW) - target_link_options(${target} PRIVATE -static) - if(MINGW) - target_link_options(${target} PRIVATE -static-libgcc) - endif() - endif() -endmacro() - -set(qjs_sources - cutils.c - dtoa.c - libregexp.c - libunicode.c - quickjs.c -) - -if(QJS_BUILD_LIBC) - list(APPEND qjs_sources quickjs-libc.c) -endif() -list(APPEND qjs_defines _GNU_SOURCE) -if(WIN32) - # NB: Windows 7 is EOL and we are only supporting in so far as it doesn't interfere with progress. - list(APPEND qjs_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0601) -endif() -if(TVOS) - list(APPEND qjs_defines _TVOS) -endif() -if(WATCHOS) - list(APPEND qjs_defines _WATCHOS) -endif() -list(APPEND qjs_libs ${CMAKE_DL_LIBS}) -find_package(Threads) -if(NOT CMAKE_SYSTEM_NAME STREQUAL "WASI") - list(APPEND qjs_libs ${CMAKE_THREAD_LIBS_INIT}) -endif() - -# try to find libm -find_library(M_LIBRARIES m) -if(M_LIBRARIES OR CMAKE_C_COMPILER_ID STREQUAL "TinyCC") - list(APPEND qjs_libs m) -endif() - -add_library(qjs ${qjs_sources}) -target_compile_definitions(qjs PRIVATE ${qjs_defines}) -target_include_directories(qjs PUBLIC - $ - $ -) -target_link_libraries(qjs PUBLIC ${qjs_libs}) - -if(EMSCRIPTEN) - add_executable(qjs_wasm ${qjs_sources}) - target_link_options(qjs_wasm PRIVATE - # in emscripten 3.x, this will be set to 16k which is too small for quickjs. #write sth. to force github rebuild - -sSTACK_SIZE=2097152 # let it be 2m = 2 * 1024 * 1024 = 2097152, otherwise, stack overflow may be occured at bootstrap - -sNO_INVOKE_RUN - -sNO_EXIT_RUNTIME - -sMODULARIZE # do not mess the global - -sEXPORT_ES6 # export js file to morden es module - -sEXPORT_NAME=getQuickJs # give a name - -sTEXTDECODER=1 # it will be 2 if we use -Oz, and that will cause js -> c string convertion fail - -sNO_DEFAULT_TO_CXX # this project is pure c project, no need for c plus plus handle - -sEXPORTED_RUNTIME_METHODS=ccall,cwrap - ) - target_compile_definitions(qjs_wasm PRIVATE ${qjs_defines}) - target_link_libraries(qjs_wasm m) -endif() - - -# QuickJS bytecode compiler -# - -add_executable(qjsc - qjsc.c -) -add_qjs_libc_if_needed(qjsc) -add_static_if_needed(qjsc) -target_compile_definitions(qjsc PRIVATE ${qjs_defines}) -target_link_libraries(qjsc qjs) - - -# QuickJS CLI -# - -add_executable(qjs_exe - gen/repl.c - gen/standalone.c - qjs.c -) -add_qjs_libc_if_needed(qjs_exe) -add_static_if_needed(qjs_exe) -set_target_properties(qjs_exe PROPERTIES - OUTPUT_NAME "qjs" -) -target_compile_definitions(qjs_exe PRIVATE ${qjs_defines}) -target_link_libraries(qjs_exe qjs) -if(NOT WIN32) - set_target_properties(qjs_exe PROPERTIES ENABLE_EXPORTS TRUE) -endif() -if(QJS_BUILD_CLI_WITH_MIMALLOC OR QJS_BUILD_CLI_WITH_STATIC_MIMALLOC) - find_package(mimalloc REQUIRED) - # Upstream mimalloc doesn't provide a way to know if both libraries are supported. - if(QJS_BUILD_CLI_WITH_STATIC_MIMALLOC) - target_link_libraries(qjs_exe mimalloc-static) - else() - target_link_libraries(qjs_exe mimalloc) - endif() -endif() - -# Test262 runner -# - -if(NOT EMSCRIPTEN) - add_executable(run-test262 - run-test262.c - ) - add_qjs_libc_if_needed(run-test262) - target_compile_definitions(run-test262 PRIVATE ${qjs_defines}) - target_link_libraries(run-test262 qjs) -endif() - -# Interrupt test -# - -add_executable(api-test - api-test.c -) -target_compile_definitions(api-test PRIVATE ${qjs_defines}) -target_link_libraries(api-test qjs) - -# Unicode generator -# - -add_executable(unicode_gen EXCLUDE_FROM_ALL - cutils.c - libunicode.c - unicode_gen.c -) -target_compile_definitions(unicode_gen PRIVATE ${qjs_defines}) - -add_executable(function_source - gen/function_source.c -) -add_qjs_libc_if_needed(function_source) -target_include_directories(function_source PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_definitions(function_source PRIVATE ${qjs_defines}) -target_link_libraries(function_source qjs) - -# Examples -# - -if(QJS_BUILD_EXAMPLES) - add_executable(hello - gen/hello.c - ) - add_qjs_libc_if_needed(hello) - target_include_directories(hello PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - target_compile_definitions(hello PRIVATE ${qjs_defines}) - target_link_libraries(hello qjs) - - add_executable(hello_module - gen/hello_module.c - ) - add_qjs_libc_if_needed(hello_module) - target_include_directories(hello_module PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - target_compile_definitions(hello_module PRIVATE ${qjs_defines}) - target_link_libraries(hello_module qjs) - - add_library(fib MODULE examples/fib.c) - set_target_properties(fib PROPERTIES - PREFIX "" - C_VISIBILITY_PRESET default - ) - target_compile_definitions(fib PRIVATE JS_SHARED_LIBRARY) - if(WIN32) - target_link_libraries(fib qjs) - elseif(APPLE) - target_link_options(fib PRIVATE -undefined dynamic_lookup) - endif() - - add_library(point MODULE examples/point.c) - set_target_properties(point PROPERTIES - PREFIX "" - C_VISIBILITY_PRESET default - ) - target_compile_definitions(point PRIVATE JS_SHARED_LIBRARY) - if(WIN32) - target_link_libraries(point qjs) - elseif(APPLE) - target_link_options(point PRIVATE -undefined dynamic_lookup) - endif() - - add_executable(test_fib - examples/fib.c - gen/test_fib.c - ) - add_qjs_libc_if_needed(test_fib) - target_include_directories(test_fib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - target_compile_definitions(test_fib PRIVATE ${qjs_defines}) - target_link_libraries(test_fib qjs) -endif() - -# Install target -# - -file(STRINGS quickjs.h quickjs_h REGEX QJS_VERSION) -string(REGEX MATCH "QJS_VERSION_MAJOR ([0-9]*)" _ "${quickjs_h}") -set(QJS_VERSION_MAJOR ${CMAKE_MATCH_1}) -string(REGEX MATCH "QJS_VERSION_MINOR ([0-9]*)" _ "${quickjs_h}") -set(QJS_VERSION_MINOR ${CMAKE_MATCH_1}) -string(REGEX MATCH "QJS_VERSION_PATCH ([0-9]*)" _ "${quickjs_h}") -set(QJS_VERSION_PATCH ${CMAKE_MATCH_1}) -set_target_properties(qjs PROPERTIES - VERSION ${QJS_VERSION_MAJOR}.${QJS_VERSION_MINOR}.${QJS_VERSION_PATCH} - SOVERSION ${QJS_VERSION_MAJOR} -) -install(FILES quickjs.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -if(QJS_BUILD_LIBC) - install(FILES quickjs-libc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -endif() -if(NOT IOS AND NOT TVOS AND NOT WATCHOS) - install(TARGETS qjs_exe RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - install(TARGETS qjsc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -endif() -install(TARGETS qjs EXPORT qjsConfig - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(EXPORT qjsConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/quickjs) -install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) -install(DIRECTORY examples DESTINATION ${CMAKE_INSTALL_DOCDIR}) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/LICENSE b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/LICENSE deleted file mode 100755 index 3c78b692..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -The MIT License (MIT) - -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. diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/Makefile b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/Makefile deleted file mode 100755 index 4ff8ca17..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/Makefile +++ /dev/null @@ -1,147 +0,0 @@ -# -# 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. - -BUILD_DIR=build -BUILD_TYPE?=Release -INSTALL_PREFIX?=/usr/local - -QJS=$(BUILD_DIR)/qjs -QJSC=$(BUILD_DIR)/qjsc -RUN262=$(BUILD_DIR)/run-test262 - -JOBS?=$(shell getconf _NPROCESSORS_ONLN) -ifeq ($(JOBS),) -JOBS := $(shell sysctl -n hw.ncpu) -endif -ifeq ($(JOBS),) -JOBS := $(shell nproc) -endif -ifeq ($(JOBS),) -JOBS := 4 -endif - -all: $(QJS) - -amalgam: TEMP := $(shell mktemp -d) -amalgam: $(QJS) - $(QJS) amalgam.js $(TEMP)/quickjs-amalgam.c - cp quickjs.h quickjs-libc.h $(TEMP) - cd $(TEMP) && zip -9 quickjs-amalgam.zip quickjs-amalgam.c quickjs.h quickjs-libc.h - cp $(TEMP)/quickjs-amalgam.zip $(BUILD_DIR) - cd $(TEMP) && $(RM) quickjs-amalgam.zip quickjs-amalgam.c quickjs.h quickjs-libc.h - $(RM) -d $(TEMP) - -fuzz: - clang -g -O1 -fsanitize=address,undefined,fuzzer -o fuzz fuzz.c - ./fuzz - -$(BUILD_DIR): - cmake -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DCMAKE_INSTALL_PREFIX=$(INSTALL_PREFIX) - -$(QJS): $(BUILD_DIR) - cmake --build $(BUILD_DIR) -j $(JOBS) - -$(QJSC): $(BUILD_DIR) - cmake --build $(BUILD_DIR) --target qjsc -j $(JOBS) - -install: $(QJS) $(QJSC) - cmake --build $(BUILD_DIR) --target install - -clean: - cmake --build $(BUILD_DIR) --target clean - -codegen: $(QJSC) - $(QJSC) -ss -o gen/repl.c -m repl.js - $(QJSC) -ss -o gen/standalone.c -m standalone.js - $(QJSC) -e -o gen/function_source.c tests/function_source.js - $(QJSC) -e -o gen/hello.c examples/hello.js - $(QJSC) -e -o gen/hello_module.c -m examples/hello_module.js - $(QJSC) -e -o gen/test_fib.c -m examples/test_fib.js - $(QJSC) -C -ss -o builtin-array-fromasync.h builtin-array-fromasync.js - -debug: - BUILD_TYPE=Debug $(MAKE) - -distclean: - @rm -rf $(BUILD_DIR) - -stats: $(QJS) - $(QJS) -qd - -jscheck: CFLAGS=-I. -D_GNU_SOURCE -DJS_CHECK_JSVALUE -Wall -Werror -fsyntax-only -c -o /dev/null -jscheck: - $(CC) $(CFLAGS) api-test.c - $(CC) $(CFLAGS) ctest.c - $(CC) $(CFLAGS) fuzz.c - $(CC) $(CFLAGS) gen/function_source.c - $(CC) $(CFLAGS) gen/hello.c - $(CC) $(CFLAGS) gen/hello_module.c - $(CC) $(CFLAGS) gen/repl.c - $(CC) $(CFLAGS) gen/standalone.c - $(CC) $(CFLAGS) gen/test_fib.c - $(CC) $(CFLAGS) qjs.c - $(CC) $(CFLAGS) qjsc.c - $(CC) $(CFLAGS) quickjs-libc.c - $(CC) $(CFLAGS) quickjs.c - $(CC) $(CFLAGS) run-test262.c - -# effectively .PHONY because it doesn't generate output -ctest: CFLAGS=-std=c11 -fsyntax-only -Wall -Wextra -Werror -pedantic -ctest: ctest.c quickjs.h - $(CC) $(CFLAGS) -DJS_NAN_BOXING=0 $< - $(CC) $(CFLAGS) -DJS_NAN_BOXING=1 $< - -# effectively .PHONY because it doesn't generate output -cxxtest: CXXFLAGS=-std=c++11 -fsyntax-only -Wall -Wextra -Werror -pedantic -cxxtest: cxxtest.cc quickjs.h - $(CXX) $(CXXFLAGS) -DJS_NAN_BOXING=0 $< - $(CXX) $(CXXFLAGS) -DJS_NAN_BOXING=1 $< - -test: $(QJS) - $(RUN262) -c tests.conf - -test262: $(QJS) - $(RUN262) -m -c test262.conf -a - -test262-fast: $(QJS) - $(RUN262) -m -c test262.conf -c test262-fast.conf -a - -test262-update: $(QJS) - $(RUN262) -u -c test262.conf -a -t 1 - -test262-check: $(QJS) - $(RUN262) -m -c test262.conf -E -a - -microbench: $(QJS) - $(QJS) tests/microbench.js - -unicode_gen: $(BUILD_DIR) - cmake --build $(BUILD_DIR) --target unicode_gen - -libunicode-table.h: unicode_gen - $(BUILD_DIR)/unicode_gen unicode $@ - -.PHONY: all amalgam ctest cxxtest debug fuzz jscheck install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen $(QJS) $(QJSC) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/README.md b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/README.md deleted file mode 100755 index e4b0e34c..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# ⚡️ QuickJS - A mighty JavaScript engine - -## Overview - -QuickJS is a small and embeddable JavaScript engine. It aims to support the latest -[ECMAScript] specification. - -This project is a _fork_ of the [original QuickJS project] by Fabrice Bellard and Charlie Gordon, after it went dormant, with the intent of reigniting its development. - -## Getting started - -Head over to the [project website] for instructions on how to get started and more -documentation. - -## Authors - -[@bnoordhuis], [@saghul], and many more [contributors]. - -[ECMAScript]: https://tc39.es/ecma262/ -[original QuickJS project]: https://bellard.org/quickjs -[@bnoordhuis]: https://github.com/bnoordhuis -[@saghul]: https://github.com/saghul -[contributors]: https://github.com/quickjs-ng/quickjs/graphs/contributors -[project website]: https://quickjs-ng.github.io/quickjs/ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/amalgam.js b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/amalgam.js deleted file mode 100755 index 028d7798..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/amalgam.js +++ /dev/null @@ -1,49 +0,0 @@ -import {loadFile, writeFile} from "qjs:std" - -const cutils_c = loadFile("cutils.c") -const cutils_h = loadFile("cutils.h") -const dtoa_c = loadFile("dtoa.c") -const dtoa_h = loadFile("dtoa.h") -const libregexp_c = loadFile("libregexp.c") -const libregexp_h = loadFile("libregexp.h") -const libregexp_opcode_h = loadFile("libregexp-opcode.h") -const libunicode_c = loadFile("libunicode.c") -const libunicode_h = loadFile("libunicode.h") -const libunicode_table_h = loadFile("libunicode-table.h") -const list_h = loadFile("list.h") -const quickjs_atom_h = loadFile("quickjs-atom.h") -const quickjs_c = loadFile("quickjs.c") -const quickjs_c_atomics_h = loadFile("quickjs-c-atomics.h") -const quickjs_h = loadFile("quickjs.h") -const quickjs_libc_c = loadFile("quickjs-libc.c") -const quickjs_libc_h = loadFile("quickjs-libc.h") -const quickjs_opcode_h = loadFile("quickjs-opcode.h") -const gen_builtin_array_fromasync_h = loadFile("builtin-array-fromasync.h") - -let source = "#if defined(QJS_BUILD_LIBC) && defined(__linux__) && !defined(_GNU_SOURCE)\n" - + "#define _GNU_SOURCE\n" - + "#endif\n" - + quickjs_c_atomics_h - + cutils_h - + dtoa_h - + list_h - + libunicode_h // exports lre_is_id_start, used by libregexp.h - + libregexp_h - + libunicode_table_h - + quickjs_h - + quickjs_c - + cutils_c - + dtoa_c - + libregexp_c - + libunicode_c - + "#ifdef QJS_BUILD_LIBC\n" - + quickjs_libc_h - + quickjs_libc_c - + "#endif // QJS_BUILD_LIBC\n" -source = source.replace(/#include "quickjs-atom.h"/g, quickjs_atom_h) -source = source.replace(/#include "quickjs-opcode.h"/g, quickjs_opcode_h) -source = source.replace(/#include "libregexp-opcode.h"/g, libregexp_opcode_h) -source = source.replace(/#include "builtin-array-fromasync.h"/g, - gen_builtin_array_fromasync_h) -source = source.replace(/#include "[^"]+"/g, "") -writeFile(execArgv[2] ?? "quickjs-amalgam.c", source) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/api-test.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/api-test.c deleted file mode 100755 index ef28a4f5..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/api-test.c +++ /dev/null @@ -1,763 +0,0 @@ -#ifdef NDEBUG -#undef NDEBUG -#endif -#include -#include -#include -#include "quickjs.h" -#include "cutils.h" - -static JSValue eval(JSContext *ctx, const char *code) -{ - return JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_GLOBAL); -} - -static JSValue cfunc_callback(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ThrowTypeError(ctx, "from cfunc"); -} - -static JSValue cfuncdata_callback(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValueConst *func_data) -{ - return JS_ThrowTypeError(ctx, "from cfuncdata"); -} - -static void cfunctions(void) -{ - uint32_t length; - const char *s; - JSValue ret, stack; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - JSValue cfunc = JS_NewCFunction(ctx, cfunc_callback, "cfunc", 42); - JSValue cfuncdata = - JS_NewCFunctionData2(ctx, cfuncdata_callback, "cfuncdata", - /*length*/1337, /*magic*/0, /*data_len*/0, NULL); - JSValue global = JS_GetGlobalObject(ctx); - JS_SetPropertyStr(ctx, global, "cfunc", cfunc); - JS_SetPropertyStr(ctx, global, "cfuncdata", cfuncdata); - JS_FreeValue(ctx, global); - - ret = eval(ctx, "cfunc.name"); - assert(!JS_IsException(ret)); - assert(JS_IsString(ret)); - s = JS_ToCString(ctx, ret); - JS_FreeValue(ctx, ret); - assert(s); - assert(!strcmp(s, "cfunc")); - JS_FreeCString(ctx, s); - ret = eval(ctx, "cfunc.length"); - assert(!JS_IsException(ret)); - assert(JS_IsNumber(ret)); - assert(0 == JS_ToUint32(ctx, &length, ret)); - assert(length == 42); - - ret = eval(ctx, "cfuncdata.name"); - assert(!JS_IsException(ret)); - assert(JS_IsString(ret)); - s = JS_ToCString(ctx, ret); - JS_FreeValue(ctx, ret); - assert(s); - assert(!strcmp(s, "cfuncdata")); - JS_FreeCString(ctx, s); - ret = eval(ctx, "cfuncdata.length"); - assert(!JS_IsException(ret)); - assert(JS_IsNumber(ret)); - assert(0 == JS_ToUint32(ctx, &length, ret)); - assert(length == 1337); - - ret = eval(ctx, "cfunc()"); - assert(JS_IsException(ret)); - ret = JS_GetException(ctx); - assert(JS_IsError(ret)); - stack = JS_GetPropertyStr(ctx, ret, "stack"); - assert(JS_IsString(stack)); - s = JS_ToCString(ctx, stack); - JS_FreeValue(ctx, stack); - assert(s); - assert(!strcmp(s, " at cfunc (native)\n at (:1:1)\n")); - JS_FreeCString(ctx, s); - s = JS_ToCString(ctx, ret); - JS_FreeValue(ctx, ret); - assert(s); - assert(!strcmp(s, "TypeError: from cfunc")); - JS_FreeCString(ctx, s); - - ret = eval(ctx, "cfuncdata()"); - assert(JS_IsException(ret)); - ret = JS_GetException(ctx); - assert(JS_IsError(ret)); - stack = JS_GetPropertyStr(ctx, ret, "stack"); - assert(JS_IsString(stack)); - s = JS_ToCString(ctx, stack); - JS_FreeValue(ctx, stack); - assert(s); - assert(!strcmp(s, " at cfuncdata (native)\n at (:1:1)\n")); - JS_FreeCString(ctx, s); - s = JS_ToCString(ctx, ret); - JS_FreeValue(ctx, ret); - assert(s); - assert(!strcmp(s, "TypeError: from cfuncdata")); - JS_FreeCString(ctx, s); - - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -#define MAX_TIME 10 - -static int timeout_interrupt_handler(JSRuntime *rt, void *opaque) -{ - int *time = (int *)opaque; - if (*time <= MAX_TIME) - *time += 1; - return *time > MAX_TIME; -} - -static void sync_call(void) -{ - static const char code[] = -"(function() { \ - try { \ - while (true) {} \ - } catch (e) {} \ -})();"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - int time = 0; - JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time); - JSValue ret = eval(ctx, code); - assert(time > MAX_TIME); - assert(JS_IsException(ret)); - JS_FreeValue(ctx, ret); - assert(JS_HasException(ctx)); - JSValue e = JS_GetException(ctx); - assert(JS_IsUncatchableError(e)); - JS_FreeValue(ctx, e); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void async_call(void) -{ - static const char code[] = -"(async function() { \ - const loop = async () => { \ - await Promise.resolve(); \ - while (true) {} \ - }; \ - await loop().catch(() => {}); \ -})();"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - int time = 0; - JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time); - JSValue ret = eval(ctx, code); - assert(!JS_IsException(ret)); - JS_FreeValue(ctx, ret); - assert(JS_IsJobPending(rt)); - int r = 0; - while (JS_IsJobPending(rt)) { - r = JS_ExecutePendingJob(rt, &ctx); - } - assert(time > MAX_TIME); - assert(r == -1); - assert(JS_HasException(ctx)); - JSValue e = JS_GetException(ctx); - assert(JS_IsUncatchableError(e)); - JS_FreeValue(ctx, e); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static JSValue save_value(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - assert(argc == 1); - JSValue *p = (JSValue *)JS_GetContextOpaque(ctx); - *p = JS_DupValue(ctx, argv[0]); - return JS_UNDEFINED; -} - -static void async_call_stack_overflow(void) -{ - static const char code[] = -"(async function() { \ - const f = () => f(); \ - try { \ - await Promise.resolve(); \ - f(); \ - } catch (e) { \ - save_value(e); \ - } \ -})();"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - JSValue value = JS_UNDEFINED; - JS_SetContextOpaque(ctx, &value); - JSValue global = JS_GetGlobalObject(ctx); - JS_SetPropertyStr(ctx, global, "save_value", JS_NewCFunction(ctx, save_value, "save_value", 1)); - JS_FreeValue(ctx, global); - JSValue ret = eval(ctx, code); - assert(!JS_IsException(ret)); - JS_FreeValue(ctx, ret); - assert(JS_IsJobPending(rt)); - int r = 0; - while (JS_IsJobPending(rt)) { - r = JS_ExecutePendingJob(rt, &ctx); - } - assert(r == 1); - assert(!JS_HasException(ctx)); - assert(JS_IsError(value)); // stack overflow should be caught - JS_FreeValue(ctx, value); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -// https://github.com/quickjs-ng/quickjs/issues/914 -static void raw_context_global_var(void) -{ - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContextRaw(rt); - JS_AddIntrinsicEval(ctx); - { - JSValue ret = eval(ctx, "globalThis"); - assert(JS_IsException(ret)); - JS_FreeValue(ctx, ret); - } - { - JSValue ret = eval(ctx, "var x = 42"); - assert(JS_IsUndefined(ret)); - JS_FreeValue(ctx, ret); - } - { - JSValue ret = eval(ctx, "function f() {}"); - assert(JS_IsUndefined(ret)); - JS_FreeValue(ctx, ret); - } - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void is_array(void) -{ - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - { - JSValue ret = eval(ctx, "[]"); - assert(!JS_IsException(ret)); - assert(JS_IsArray(ret)); - JS_FreeValue(ctx, ret); - } - { - JSValue ret = eval(ctx, "new Proxy([], {})"); - assert(!JS_IsException(ret)); - assert(!JS_IsArray(ret)); - assert(JS_IsProxy(ret)); - JSValue handler = JS_GetProxyHandler(ctx, ret); - JSValue target = JS_GetProxyTarget(ctx, ret); - assert(!JS_IsException(handler)); - assert(!JS_IsException(target)); - assert(!JS_IsProxy(handler)); - assert(!JS_IsProxy(target)); - assert(JS_IsObject(handler)); - assert(JS_IsArray(target)); - JS_FreeValue(ctx, handler); - JS_FreeValue(ctx, target); - JS_FreeValue(ctx, ret); - } - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static int loader_calls; - -static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque) -{ - loader_calls++; - assert(!strcmp(name, "b")); - static const char code[] = "export function f(x){}"; - JSValue ret = JS_Eval(ctx, code, strlen(code), "b", - JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY); - assert(!JS_IsException(ret)); - JSModuleDef *m = JS_VALUE_GET_PTR(ret); - assert(m); - JS_FreeValue(ctx, ret); - return m; -} - -static void module_serde(void) -{ - JSRuntime *rt = JS_NewRuntime(); - //JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE); - JS_SetModuleLoaderFunc(rt, NULL, loader, NULL); - JSContext *ctx = JS_NewContext(rt); - static const char code[] = "import {f} from 'b'; f()"; - assert(loader_calls == 0); - JSValue mod = JS_Eval(ctx, code, strlen(code), "a", - JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY); - assert(loader_calls == 1); - assert(!JS_IsException(mod)); - assert(JS_IsModule(mod)); - size_t len = 0; - uint8_t *buf = JS_WriteObject(ctx, &len, mod, - JS_WRITE_OBJ_BYTECODE|JS_WRITE_OBJ_REFERENCE); - assert(buf); - assert(len > 0); - JS_FreeValue(ctx, mod); - assert(loader_calls == 1); - mod = JS_ReadObject(ctx, buf, len, JS_READ_OBJ_BYTECODE); - js_free(ctx, buf); - assert(loader_calls == 1); // 'b' is returned from cache - assert(!JS_IsException(mod)); - JSValue ret = JS_EvalFunction(ctx, mod); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - JSValue result = JS_PromiseResult(ctx, ret); - assert(!JS_IsException(result)); - assert(JS_IsUndefined(result)); - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, mod); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void two_byte_string(void) -{ - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - { - JSValue v = JS_NewTwoByteString(ctx, NULL, 0); - assert(!JS_IsException(v)); - const char *s = JS_ToCString(ctx, v); - assert(s); - assert(!strcmp(s, "")); - JS_FreeCString(ctx, s); - JS_FreeValue(ctx, v); - } - { - JSValue v = JS_NewTwoByteString(ctx, (uint16_t[]){'o','k'}, 2); - assert(!JS_IsException(v)); - const char *s = JS_ToCString(ctx, v); - assert(s); - assert(!strcmp(s, "ok")); - JS_FreeCString(ctx, s); - JS_FreeValue(ctx, v); - } - { - JSValue v = JS_NewTwoByteString(ctx, (uint16_t[]){0xD800}, 1); - assert(!JS_IsException(v)); - const char *s = JS_ToCString(ctx, v); - assert(s); - // questionable but surrogates don't map to UTF-8 without WTF-8 - assert(!strcmp(s, "\xED\xA0\x80")); - JS_FreeCString(ctx, s); - JS_FreeValue(ctx, v); - } - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void weak_map_gc_check(void) -{ - static const char init_code[] = -"const map = new WeakMap(); \ -function addItem() { \ - const k = { \ - text: 'a', \ - }; \ - map.set(k, {k}); \ -}"; - static const char test_code[] = "addItem()"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - - JSValue ret = eval(ctx, init_code); - assert(!JS_IsException(ret)); - - JSValue ret_test = eval(ctx, test_code); - assert(!JS_IsException(ret_test)); - JS_RunGC(rt); - JSMemoryUsage memory_usage; - JS_ComputeMemoryUsage(rt, &memory_usage); - - for (int i = 0; i < 3; i++) { - JSValue ret_test2 = eval(ctx, test_code); - assert(!JS_IsException(ret_test2)); - JS_RunGC(rt); - JSMemoryUsage memory_usage2; - JS_ComputeMemoryUsage(rt, &memory_usage2); - - assert(memory_usage.memory_used_count == memory_usage2.memory_used_count); - assert(memory_usage.memory_used_size == memory_usage2.memory_used_size); - JS_FreeValue(ctx, ret_test2); - } - - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, ret_test); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -struct { - int hook_type_call_count[4]; -} promise_hook_state; - -static void promise_hook_cb(JSContext *ctx, JSPromiseHookType type, - JSValueConst promise, JSValueConst parent_promise, - void *opaque) -{ - assert(type == JS_PROMISE_HOOK_INIT || - type == JS_PROMISE_HOOK_BEFORE || - type == JS_PROMISE_HOOK_AFTER || - type == JS_PROMISE_HOOK_RESOLVE); - promise_hook_state.hook_type_call_count[type]++; - assert(opaque == (void *)&promise_hook_state); - if (!JS_IsUndefined(parent_promise)) { - JSValue global_object = JS_GetGlobalObject(ctx); - JS_SetPropertyStr(ctx, global_object, "actual", - JS_DupValue(ctx, parent_promise)); - JS_FreeValue(ctx, global_object); - } -} - -static void promise_hook(void) -{ - int *cc = promise_hook_state.hook_type_call_count; - JSContext *unused; - JSRuntime *rt = JS_NewRuntime(); - //JS_SetDumpFlags(rt, JS_DUMP_PROMISE); - JS_SetPromiseHook(rt, promise_hook_cb, &promise_hook_state); - JSContext *ctx = JS_NewContext(rt); - JSValue global_object = JS_GetGlobalObject(ctx); - { - // empty module; creates an outer and inner module promise; - // JS_Eval returns the outer promise - JSValue ret = JS_Eval(ctx, "", 0, "", JS_EVAL_TYPE_MODULE); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); - JS_FreeValue(ctx, ret); - assert(2 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); - assert(!JS_IsJobPending(rt)); - } - memset(&promise_hook_state, 0, sizeof(promise_hook_state)); - { - // module with unresolved promise; the outer and inner module promises - // are resolved but not the user's promise - static const char code[] = "new Promise(() => {})"; - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise - JS_FreeValue(ctx, ret); - assert(3 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); // outer and inner module promise - assert(!JS_IsJobPending(rt)); - } - memset(&promise_hook_state, 0, sizeof(promise_hook_state)); - { - // module with resolved promise - static const char code[] = "new Promise((resolve,reject) => resolve())"; - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise - JS_FreeValue(ctx, ret); - assert(3 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]); - assert(!JS_IsJobPending(rt)); - } - memset(&promise_hook_state, 0, sizeof(promise_hook_state)); - { - // module with rejected promise - static const char code[] = "new Promise((resolve,reject) => reject())"; - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise - JS_FreeValue(ctx, ret); - assert(3 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); - assert(!JS_IsJobPending(rt)); - } - memset(&promise_hook_state, 0, sizeof(promise_hook_state)); - { - // module with promise chain - static const char code[] = - "globalThis.count = 0;" - "globalThis.actual = undefined;" // set by promise_hook_cb - "globalThis.expected = new Promise(resolve => resolve());" - "expected.then(_ => count++)"; - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise - JS_FreeValue(ctx, ret); - assert(4 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]); - JSValue v = JS_GetPropertyStr(ctx, global_object, "count"); - assert(!JS_IsException(v)); - int32_t count; - assert(0 == JS_ToInt32(ctx, &count, v)); - assert(0 == count); - JS_FreeValue(ctx, v); - assert(JS_IsJobPending(rt)); - assert(1 == JS_ExecutePendingJob(rt, &unused)); - assert(!JS_HasException(ctx)); - assert(4 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(4 == cc[JS_PROMISE_HOOK_RESOLVE]); - assert(!JS_IsJobPending(rt)); - v = JS_GetPropertyStr(ctx, global_object, "count"); - assert(!JS_IsException(v)); - assert(0 == JS_ToInt32(ctx, &count, v)); - assert(1 == count); - JS_FreeValue(ctx, v); - JSValue actual = JS_GetPropertyStr(ctx, global_object, "actual"); - JSValue expected = JS_GetPropertyStr(ctx, global_object, "expected"); - assert(!JS_IsException(actual)); - assert(!JS_IsException(expected)); - assert(JS_IsSameValue(ctx, actual, expected)); - JS_FreeValue(ctx, actual); - JS_FreeValue(ctx, expected); - } - memset(&promise_hook_state, 0, sizeof(promise_hook_state)); - { - // module with thenable; fires before and after hooks - static const char code[] = - "new Promise(resolve => resolve({then(resolve){ resolve() }}))"; - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); - assert(!JS_IsException(ret)); - assert(JS_IsPromise(ret)); - assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise - JS_FreeValue(ctx, ret); - assert(3 == cc[JS_PROMISE_HOOK_INIT]); - assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(0 == cc[JS_PROMISE_HOOK_AFTER]); - assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); - assert(JS_IsJobPending(rt)); - assert(1 == JS_ExecutePendingJob(rt, &unused)); - assert(!JS_HasException(ctx)); - assert(3 == cc[JS_PROMISE_HOOK_INIT]); - assert(1 == cc[JS_PROMISE_HOOK_BEFORE]); - assert(1 == cc[JS_PROMISE_HOOK_AFTER]); - assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]); - assert(!JS_IsJobPending(rt)); - } - JS_FreeValue(ctx, global_object); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void dump_memory_usage(void) -{ - JSMemoryUsage stats; - - JSRuntime *rt = NULL; - JSContext *ctx = NULL; - - rt = JS_NewRuntime(); - ctx = JS_NewContext(rt); - - //JS_SetDumpFlags(rt, JS_DUMP_PROMISE); - - static const char code[] = - "globalThis.count = 0;" - "globalThis.actual = undefined;" // set by promise_hook_cb - "globalThis.expected = new Promise(resolve => resolve());" - "expected.then(_ => count++)"; - - JSValue evalVal = JS_Eval(ctx, code, strlen(code), "", 0); - JS_FreeValue(ctx, evalVal); - - FILE *temp = tmpfile(); - assert(temp != NULL); - JS_ComputeMemoryUsage(rt, &stats); - JS_DumpMemoryUsage(temp, &stats, rt); - // JS_DumpMemoryUsage(stdout, &stats, rt); - fclose(temp); - - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void new_errors(void) -{ - typedef struct { - const char name[16]; - JSValue (*func)(JSContext *, const char *, ...); - } Entry; - static const Entry entries[] = { - {"Error", JS_NewPlainError}, - {"InternalError", JS_NewInternalError}, - {"RangeError", JS_NewRangeError}, - {"ReferenceError", JS_NewReferenceError}, - {"SyntaxError", JS_NewSyntaxError}, - {"TypeError", JS_NewTypeError}, - }; - const Entry *e; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - for (e = entries; e < endof(entries); e++) { - JSValue obj = (*e->func)(ctx, "the %s", "needle"); - assert(!JS_IsException(obj)); - assert(JS_IsObject(obj)); - assert(JS_IsError(obj)); - const char *haystack = JS_ToCString(ctx, obj); - char needle[256]; - snprintf(needle, sizeof(needle), "%s: the needle", e->name); - assert(strstr(haystack, needle)); - JS_FreeCString(ctx, haystack); - JSValue stack = JS_GetPropertyStr(ctx, obj, "stack"); - assert(!JS_IsException(stack)); - assert(JS_IsString(stack)); - JS_FreeValue(ctx, stack); - JS_FreeValue(ctx, obj); - } - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static int gop_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop) -{ - const char *name; - int found; - - found = 0; - name = JS_AtomToCString(ctx, prop); - if (!name) - return -1; - if (!strcmp(name, "answer")) { - found = 1; - *desc = (JSPropertyDescriptor){ - .value = JS_NewInt32(ctx, 42), - .flags = JS_PROP_C_W_E | JS_PROP_HAS_VALUE, - }; - } - JS_FreeCString(ctx, name); - return found; -} - -static void global_object_prototype(void) -{ - static const char code[] = "answer"; - JSValue global_object, proto, ret; - JSClassID class_id; - JSRuntime *rt; - JSContext *ctx; - int32_t answer; - int res; - - { - rt = JS_NewRuntime(); - ctx = JS_NewContext(rt); - proto = JS_NewObject(ctx); - assert(JS_IsObject(proto)); - JSCFunctionListEntry prop = JS_PROP_INT32_DEF("answer", 42, JS_PROP_C_W_E); - JS_SetPropertyFunctionList(ctx, proto, &prop, 1); - global_object = JS_GetGlobalObject(ctx); - res = JS_SetPrototype(ctx, global_object, proto); - assert(res == true); - JS_FreeValue(ctx, global_object); - JS_FreeValue(ctx, proto); - ret = eval(ctx, code); - assert(!JS_IsException(ret)); - assert(JS_IsNumber(ret)); - res = JS_ToInt32(ctx, &answer, ret); - assert(res == 0); - assert(answer == 42); - JS_FreeValue(ctx, ret); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - } - { - JSClassExoticMethods exotic = (JSClassExoticMethods){ - .get_own_property = gop_get_own_property, - }; - JSClassDef def = (JSClassDef){ - .class_name = "Global Object", - .exotic = &exotic, - }; - rt = JS_NewRuntime(); - class_id = 0; - JS_NewClassID(rt, &class_id); - res = JS_NewClass(rt, class_id, &def); - assert(res == 0); - ctx = JS_NewContext(rt); - proto = JS_NewObjectClass(ctx, class_id); - assert(JS_IsObject(proto)); - global_object = JS_GetGlobalObject(ctx); - res = JS_SetPrototype(ctx, global_object, proto); - assert(res == true); - JS_FreeValue(ctx, global_object); - JS_FreeValue(ctx, proto); - ret = eval(ctx, code); - assert(!JS_IsException(ret)); - assert(JS_IsNumber(ret)); - res = JS_ToInt32(ctx, &answer, ret); - assert(res == 0); - assert(answer == 42); - JS_FreeValue(ctx, ret); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - } -} - -// https://github.com/quickjs-ng/quickjs/issues/1178 -static void slice_string_tocstring(void) -{ - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - JSValue ret = eval(ctx, "'.'.repeat(16384).slice(1, -1)"); - assert(!JS_IsException(ret)); - assert(JS_IsString(ret)); - const char *str = JS_ToCString(ctx, ret); - assert(strlen(str) == 16382); - JS_FreeCString(ctx, str); - JS_FreeValue(ctx, ret); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -int main(void) -{ - cfunctions(); - sync_call(); - async_call(); - async_call_stack_overflow(); - raw_context_global_var(); - is_array(); - module_serde(); - two_byte_string(); - weak_map_gc_check(); - promise_hook(); - dump_memory_usage(); - new_errors(); - global_object_prototype(); - slice_string_tocstring(); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/builtin-array-fromasync.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/builtin-array-fromasync.h deleted file mode 100755 index 19f1f077..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/builtin-array-fromasync.h +++ /dev/null @@ -1,113 +0,0 @@ -/* File generated automatically by the QuickJS-ng compiler. */ - -#include - -const uint32_t qjsc_builtin_array_fromasync_size = 826; - -const uint8_t qjsc_builtin_array_fromasync[826] = { - 0x15, 0x0d, 0x01, 0x1a, 0x61, 0x73, 0x79, 0x6e, - 0x63, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x01, 0x10, 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, 0x01, 0x04, 0x01, - 0xa4, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x43, 0x02, - 0x01, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x01, - 0x03, 0x05, 0xaa, 0x02, 0x00, 0x01, 0x40, 0xa0, - 0x03, 0x00, 0x01, 0x40, 0xc6, 0x03, 0x00, 0x01, - 0x40, 0xcc, 0x01, 0x00, 0x01, 0x40, 0xc8, 0x03, - 0x00, 0x01, 0x40, 0x0c, 0x60, 0x02, 0x01, 0xf8, - 0x01, 0x03, 0x0e, 0x01, 0x06, 0x05, 0x00, 0x86, - 0x04, 0x11, 0xca, 0x03, 0x00, 0x01, 0x00, 0xcc, - 0x03, 0x00, 0x01, 0x00, 0xce, 0x03, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, - 0x0f, 0x20, 0xcc, 0x03, 0x01, 0x01, 0x20, 0xce, - 0x03, 0x01, 0x02, 0x20, 0xd0, 0x03, 0x02, 0x00, - 0x20, 0xd2, 0x03, 0x02, 0x04, 0x20, 0xd4, 0x03, - 0x02, 0x05, 0x20, 0xd6, 0x03, 0x02, 0x06, 0x20, - 0xd8, 0x03, 0x02, 0x07, 0x20, 0x64, 0x06, 0x08, - 0x20, 0x82, 0x01, 0x07, 0x09, 0x20, 0xda, 0x03, - 0x0a, 0x08, 0x30, 0x82, 0x01, 0x0d, 0x0b, 0x20, - 0xd4, 0x01, 0x0d, 0x0c, 0x20, 0x10, 0x00, 0x01, - 0x00, 0xa0, 0x03, 0x01, 0x03, 0xc6, 0x03, 0x02, - 0x03, 0xc8, 0x03, 0x04, 0x03, 0xaa, 0x02, 0x00, - 0x03, 0xcc, 0x01, 0x03, 0x03, 0x08, 0xc4, 0x0d, - 0x62, 0x02, 0x00, 0x62, 0x01, 0x00, 0x62, 0x00, - 0x00, 0xd3, 0xcb, 0xd4, 0x11, 0xf4, 0xec, 0x08, - 0x0e, 0x39, 0x46, 0x00, 0x00, 0x00, 0xdc, 0xcc, - 0xd5, 0x11, 0xf4, 0xec, 0x08, 0x0e, 0x39, 0x46, - 0x00, 0x00, 0x00, 0xdd, 0xcd, 0x62, 0x07, 0x00, - 0x62, 0x06, 0x00, 0x62, 0x05, 0x00, 0x62, 0x04, - 0x00, 0x62, 0x03, 0x00, 0xd4, 0x39, 0x46, 0x00, - 0x00, 0x00, 0xb0, 0xec, 0x16, 0xd4, 0x98, 0x04, - 0x1b, 0x00, 0x00, 0x00, 0xb0, 0xec, 0x0c, 0xdf, - 0x11, 0x04, 0xee, 0x00, 0x00, 0x00, 0x21, 0x01, - 0x00, 0x30, 0x06, 0xce, 0xb6, 0xc4, 0x04, 0xc3, - 0x0d, 0xf7, 0xc4, 0x05, 0x09, 0xc4, 0x06, 0xd3, - 0xe0, 0x48, 0xc4, 0x07, 0x63, 0x07, 0x00, 0x07, - 0xad, 0xec, 0x0f, 0x0a, 0x11, 0x64, 0x06, 0x00, - 0x0e, 0xd3, 0xe1, 0x48, 0x11, 0x64, 0x07, 0x00, - 0x0e, 0x63, 0x07, 0x00, 0x07, 0xad, 0x6a, 0xa6, - 0x00, 0x00, 0x00, 0x62, 0x08, 0x00, 0x06, 0x11, - 0xf4, 0xed, 0x0c, 0x71, 0x43, 0x32, 0x00, 0x00, - 0x00, 0xc4, 0x08, 0x0e, 0xee, 0x05, 0x0e, 0xd3, - 0xee, 0xf2, 0x63, 0x08, 0x00, 0x8e, 0x11, 0xed, - 0x03, 0x0e, 0xb6, 0x11, 0x64, 0x08, 0x00, 0x0e, - 0x63, 0x05, 0x00, 0xec, 0x0c, 0xc3, 0x0d, 0x11, - 0x63, 0x08, 0x00, 0x21, 0x01, 0x00, 0xee, 0x06, - 0xe2, 0x63, 0x08, 0x00, 0xf1, 0x11, 0x64, 0x03, - 0x00, 0x0e, 0x63, 0x04, 0x00, 0x63, 0x08, 0x00, - 0xa7, 0x6a, 0x2a, 0x01, 0x00, 0x00, 0x62, 0x09, - 0x00, 0xd3, 0x63, 0x04, 0x00, 0x48, 0xc4, 0x09, - 0x63, 0x06, 0x00, 0xec, 0x0a, 0x63, 0x09, 0x00, - 0x8c, 0x11, 0x64, 0x09, 0x00, 0x0e, 0xd4, 0xec, - 0x17, 0xd4, 0x43, 0xef, 0x00, 0x00, 0x00, 0xd5, - 0x63, 0x09, 0x00, 0x63, 0x04, 0x00, 0x24, 0x03, - 0x00, 0x8c, 0x11, 0x64, 0x09, 0x00, 0x0e, 0x5f, - 0x04, 0x00, 0x63, 0x03, 0x00, 0x63, 0x04, 0x00, - 0x92, 0x64, 0x04, 0x00, 0x0b, 0x63, 0x09, 0x00, - 0x4d, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3e, - 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3f, 0x00, 0x00, - 0x00, 0xf3, 0x0e, 0xee, 0x9e, 0x62, 0x0a, 0x00, - 0x63, 0x07, 0x00, 0x43, 0xef, 0x00, 0x00, 0x00, - 0xd3, 0x24, 0x01, 0x00, 0xc4, 0x0a, 0x63, 0x05, - 0x00, 0xec, 0x09, 0xc3, 0x0d, 0x11, 0x21, 0x00, - 0x00, 0xee, 0x03, 0xe2, 0xf0, 0x11, 0x64, 0x03, - 0x00, 0x0e, 0x6d, 0x8c, 0x00, 0x00, 0x00, 0x62, - 0x0c, 0x00, 0x62, 0x0b, 0x00, 0x06, 0x11, 0xf4, - 0xed, 0x13, 0x71, 0x43, 0x41, 0x00, 0x00, 0x00, - 0xc4, 0x0b, 0x43, 0x6a, 0x00, 0x00, 0x00, 0xc4, - 0x0c, 0x0e, 0xee, 0x10, 0x0e, 0x63, 0x0a, 0x00, - 0x43, 0x6b, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, - 0x8c, 0xee, 0xe0, 0x63, 0x0c, 0x00, 0xed, 0x4e, - 0x63, 0x06, 0x00, 0xec, 0x0a, 0x63, 0x0b, 0x00, - 0x8c, 0x11, 0x64, 0x0b, 0x00, 0x0e, 0xd4, 0xec, - 0x17, 0xd4, 0x43, 0xef, 0x00, 0x00, 0x00, 0xd5, - 0x63, 0x0b, 0x00, 0x63, 0x04, 0x00, 0x24, 0x03, - 0x00, 0x8c, 0x11, 0x64, 0x0b, 0x00, 0x0e, 0x5f, - 0x04, 0x00, 0x63, 0x03, 0x00, 0x63, 0x04, 0x00, - 0x92, 0x64, 0x04, 0x00, 0x0b, 0x63, 0x0b, 0x00, - 0x4d, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3e, - 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3f, 0x00, 0x00, - 0x00, 0xf3, 0x0e, 0xee, 0x83, 0x0e, 0x06, 0x6e, - 0x0d, 0x00, 0x00, 0x00, 0x0e, 0xee, 0x1e, 0x6e, - 0x05, 0x00, 0x00, 0x00, 0x30, 0x63, 0x0a, 0x00, - 0x42, 0x06, 0x00, 0x00, 0x00, 0xec, 0x0d, 0x63, - 0x0a, 0x00, 0x43, 0x06, 0x00, 0x00, 0x00, 0x24, - 0x00, 0x00, 0x0e, 0x6f, 0x63, 0x03, 0x00, 0x63, - 0x04, 0x00, 0x44, 0x32, 0x00, 0x00, 0x00, 0x63, - 0x03, 0x00, 0x2f, 0xc1, 0x00, 0x28, 0xc1, 0x00, - 0xcf, 0x28, -}; - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/builtin-array-fromasync.js b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/builtin-array-fromasync.js deleted file mode 100755 index a07e40ef..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/builtin-array-fromasync.js +++ /dev/null @@ -1,36 +0,0 @@ -;(function(Array, TypeError, asyncIterator, defineProperty, iterator) { - "use strict" // result.length=i should throw if .length is not writable - return async function fromAsync(arrayLike, mapFn=undefined, thisArg=undefined) { - if (mapFn !== undefined && typeof mapFn !== "function") throw new TypeError("not a function") - let result, i = 0, isConstructor = typeof this === "function" - let sync = false, method = arrayLike[asyncIterator] - if (method == null) sync = true, method = arrayLike[iterator] - if (method == null) { - let {length} = arrayLike - length = +length || 0 - result = isConstructor ? new this(length) : Array(length) - while (i < length) { - let value = arrayLike[i] - if (sync) value = await value - if (mapFn) value = await mapFn.call(thisArg, value, i) - defineProperty(result, i++, {value, configurable: true, writable: true}) - } - } else { - const iter = method.call(arrayLike) - result = isConstructor ? new this() : Array() - try { - for (;;) { - let {value, done} = await iter.next() - if (done) break - if (sync) value = await value - if (mapFn) value = await mapFn.call(thisArg, value, i) - defineProperty(result, i++, {value, configurable: true, writable: true}) - } - } finally { - if (iter.return) iter.return() - } - } - result.length = i - return result - } -}) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/ctest.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/ctest.c deleted file mode 100755 index eeb03e86..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/ctest.c +++ /dev/null @@ -1,17 +0,0 @@ -// note: file is not actually compiled, only checked for C syntax errors -#include "quickjs.h" - -int main(void) -{ - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - JS_FreeValue(ctx, JS_NAN); - JS_FreeValue(ctx, JS_UNDEFINED); - JS_FreeValue(ctx, JS_NewFloat64(ctx, 42)); - // not a legal way of using JS_MKPTR but this is here - // to have the compiler syntax-check its definition - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_UNINITIALIZED, 0)); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cutils.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cutils.c deleted file mode 100755 index 7d36ddfd..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cutils.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif -#if defined(_WIN32) -#include -#include // _beginthread -#endif -#if defined(__APPLE__) -#include -#endif - -#include "cutils.h" - -#undef NANOSEC -#define NANOSEC ((uint64_t) 1e9) - -#ifdef __GNUC__ -#pragma GCC visibility push(default) -#endif - -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. */ -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; -} - -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; -} - -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); -} - -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; -} - -void dbuf_init(DynBuf *s) -{ - dbuf_init2(s, NULL, NULL); -} - -/* return < 0 if error */ -int dbuf_realloc(DynBuf *s, size_t new_size) -{ - size_t size; - uint8_t *new_buf; - if (new_size > s->allocated_size) { - if (s->error) - return -1; - size = s->allocated_size * 3 / 2; - if (size > new_size) - new_size = size; - new_buf = s->realloc_func(s->opaque, s->buf, new_size); - if (!new_buf) { - s->error = true; - return -1; - } - s->buf = new_buf; - s->allocated_size = new_size; - } - return 0; -} - -int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len) -{ - size_t end; - end = offset + len; - if (dbuf_realloc(s, end)) - return -1; - memcpy(s->buf + offset, data, len); - if (end > s->size) - s->size = end; - return 0; -} - -int dbuf_put(DynBuf *s, const void *data, size_t len) -{ - if (unlikely((s->size + len) > s->allocated_size)) { - if (dbuf_realloc(s, s->size + len)) - return -1; - } - if (len > 0) { - memcpy(s->buf + s->size, data, len); - s->size += len; - } - return 0; -} - -int dbuf_put_self(DynBuf *s, size_t offset, size_t len) -{ - if (unlikely((s->size + len) > s->allocated_size)) { - if (dbuf_realloc(s, s->size + len)) - return -1; - } - memcpy(s->buf + s->size, s->buf + offset, len); - s->size += len; - return 0; -} - -int dbuf_putc(DynBuf *s, uint8_t c) -{ - return dbuf_put(s, &c, 1); -} - -int dbuf_putstr(DynBuf *s, const char *str) -{ - return dbuf_put(s, (const uint8_t *)str, strlen(str)); -} - -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_realloc(s, s->size + 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; -} - -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. - */ -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 - */ -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 - */ -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; -} - -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 - */ -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`. - */ -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. - */ -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`. - */ -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`. - */ -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 */ -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; -} - -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 -uint64_t js__hrtime_ns(void) { - struct timespec t; - - if (clock_gettime(CLOCK_MONOTONIC, &t)) - abort(); - - return t.tv_sec * NANOSEC + t.tv_nsec; -} -#endif - -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) -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__) -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__) -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 -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; -} - -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); -} - -void js_mutex_init(js_mutex_t *mutex) { - InitializeCriticalSection(mutex); -} - -void js_mutex_destroy(js_mutex_t *mutex) { - DeleteCriticalSection(mutex); -} - -void js_mutex_lock(js_mutex_t *mutex) { - EnterCriticalSection(mutex); -} - -void js_mutex_unlock(js_mutex_t *mutex) { - LeaveCriticalSection(mutex); -} - -void js_cond_init(js_cond_t *cond) { - InitializeConditionVariable(cond); -} - -void js_cond_destroy(js_cond_t *cond) { - /* nothing to do */ - (void) cond; -} - -void js_cond_signal(js_cond_t *cond) { - WakeConditionVariable(cond); -} - -void js_cond_broadcast(js_cond_t *cond) { - WakeAllConditionVariable(cond); -} - -void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { - if (!SleepConditionVariableCS(cond, mutex, INFINITE)) - abort(); -} - -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; -} - -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; -} - -int js_thread_join(js_thread_t thrd) -{ - if (WaitForSingleObject(thrd, INFINITE)) - return -1; - CloseHandle(thrd); - return 0; -} - -#else /* !defined(_WIN32) */ - -void js_once(js_once_t *guard, void (*callback)(void)) { - if (pthread_once(guard, callback)) - abort(); -} - -void js_mutex_init(js_mutex_t *mutex) { - if (pthread_mutex_init(mutex, NULL)) - abort(); -} - -void js_mutex_destroy(js_mutex_t *mutex) { - if (pthread_mutex_destroy(mutex)) - abort(); -} - -void js_mutex_lock(js_mutex_t *mutex) { - if (pthread_mutex_lock(mutex)) - abort(); -} - -void js_mutex_unlock(js_mutex_t *mutex) { - if (pthread_mutex_unlock(mutex)) - abort(); -} - -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 -} - -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(); -} - -void js_cond_signal(js_cond_t *cond) { - if (pthread_cond_signal(cond)) - abort(); -} - -void js_cond_broadcast(js_cond_t *cond) { - if (pthread_cond_broadcast(cond)) - abort(); -} - -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 -} - -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; -} - -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; -} - -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 __GNUC__ -#pragma GCC visibility pop -#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cutils.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cutils.h deleted file mode 100755 index f5d2a8bc..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cutils.h +++ /dev/null @@ -1,660 +0,0 @@ -/* - * 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 -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(_MSC_VER) -#include -#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 -#endif -#if !defined(_WIN32) && !defined(EMSCRIPTEN) && !defined(__wasi__) -#include -#include -#endif -#if !defined(_WIN32) -#include -#include -#endif - -#if defined(_MSC_VER) && !defined(__clang__) -# define likely(x) (x) -# define unlikely(x) (x) -# define force_inline __forceinline -# 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 force_inline inline __attribute__((always_inline)) -# define no_inline __attribute__((noinline)) -# define __maybe_unused __attribute__((unused)) -#endif - -#if defined(_MSC_VER) && !defined(__clang__) -#include -#define INF INFINITY -#define NEG_INF -INFINITY -#else -#define INF (1.0/0.0) -#define NEG_INF (-1.0/0.0) -#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 - -void js__pstrcpy(char *buf, int buf_size, const char *str); -char *js__pstrcat(char *buf, int buf_size, const char *s); -int js__strstart(const char *str, const char *val, const char **ptr); -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; - -void dbuf_init(DynBuf *s); -void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); -int dbuf_realloc(DynBuf *s, size_t new_size); -int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len); -int dbuf_put(DynBuf *s, const void *data, size_t len); -int dbuf_put_self(DynBuf *s, size_t offset, size_t len); -int dbuf_putc(DynBuf *s, uint8_t c); -int dbuf_putstr(DynBuf *s, const char *str); -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); -} -int JS_PRINTF_FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, JS_PRINTF_FORMAT const char *fmt, ...); -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 -}; -int utf8_scan(const char *buf, size_t len, size_t *plen); -size_t utf8_encode_len(uint32_t c); -size_t utf8_encode(uint8_t buf[minimum_length(UTF8_CHAR_LEN_MAX)], uint32_t c); -uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp); -uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp); -size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len); -size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len); -size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len); -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; -} - -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; -} - -int64_t js__gettimeofday_us(void); -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 -} - -int js_exepath(char* buffer, size_t* size); - -/* Cross-platform threading APIs. */ - -#if defined(EMSCRIPTEN) || defined(__wasi__) - -#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 - -void js_once(js_once_t *guard, void (*callback)(void)); - -void js_mutex_init(js_mutex_t *mutex); -void js_mutex_destroy(js_mutex_t *mutex); -void js_mutex_lock(js_mutex_t *mutex); -void js_mutex_unlock(js_mutex_t *mutex); - -void js_cond_init(js_cond_t *cond); -void js_cond_destroy(js_cond_t *cond); -void js_cond_signal(js_cond_t *cond); -void js_cond_broadcast(js_cond_t *cond); -void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex); -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) -int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, - int flags); -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 - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* CUTILS_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cxxtest.cc b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cxxtest.cc deleted file mode 100755 index b6151009..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/cxxtest.cc +++ /dev/null @@ -1,2 +0,0 @@ -// note: file is not actually compiled, only checked for C++ syntax errors -#include "ctest.c" diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/dtoa.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/dtoa.c deleted file mode 100755 index a89e824f..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/dtoa.c +++ /dev/null @@ -1,1619 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -// #include -#include -// #include - -#include "cutils.h" -#include "dtoa.h" - -/* - TODO: - - simplify subnormal handling - - reduce max memory usage - - free format: could add shortcut if exact result - - use 64 bit limb_t when possible - - use another algorithm for free format dtoa in base 10 (ryu ?) -*/ - -#define USE_POW5_TABLE -/* use fast path to print small integers in free format */ -#define USE_FAST_INT - -#define LIMB_LOG2_BITS 5 - -#define LIMB_BITS (1 << LIMB_LOG2_BITS) - -typedef int32_t slimb_t; -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; - -#define LIMB_DIGITS 9 - -#define JS_RADIX_MAX 36 - -#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */ -#define MANT_LEN_MAX 18 /* < 36^100 */ - -typedef intptr_t mp_size_t; - -/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */ -typedef struct { - int len; /* >= 1 */ - limb_t tab[]; -} mpb_t; - -static limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) -{ - size_t i; - limb_t k, a; - - k=b; - for(i=0;i> LIMB_BITS; - } - return l; -} - -/* WARNING: d must be >= 2^(LIMB_BITS-1) */ -static inline limb_t udiv1norm_init(limb_t d) -{ - limb_t a0, a1; - a1 = -d - 1; - a0 = -1; - return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; -} - -/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 - / d' with 0 <= a1 < d. */ -static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, - limb_t d, limb_t d_inv) -{ - limb_t n1m, n_adj, q, r, ah; - dlimb_t a; - n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); - n_adj = a0 + (n1m & d); - a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; - q = (a >> LIMB_BITS) + a1; - /* compute a - q * r and update q so that the remainder is between - 0 and d - 1 */ - a = ((dlimb_t)a1 << LIMB_BITS) | a0; - a = a - (dlimb_t)q * d - d; - ah = a >> LIMB_BITS; - q += 1 + ah; - r = (limb_t)a + (ah & d); - *pr = r; - return q; -} - -static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r) -{ - slimb_t i; - dlimb_t a1; - for(i = n - 1; i >= 0; i--) { - a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; - tabr[i] = a1 / b; - r = a1 % b; - } - return r; -} - -/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). - 1 <= shift <= LIMB_BITS - 1 */ -static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t high) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); - l = a; - } - return l & (((limb_t)1 << shift) - 1); -} - -/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low < - 2^shift. */ -static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t low) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = low; - for(i = 0; i < n; i++) { - a = tab[i]; - tab_r[i] = (a << shift) | l; - l = (a >> (LIMB_BITS - shift)); - } - return l; -} - -static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r, limb_t b_inv, int shift) -{ - slimb_t i; - - if (shift != 0) { - r = (r << shift) | mp_shl(tabr, taba, n, shift, 0); - } - for(i = n - 1; i >= 0; i--) { - tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); - } - r >>= shift; - return r; -} - -static __maybe_unused void mpb_dump(const char *str, const mpb_t *a) -{ - int i; - - printf("%s= 0x", str); - for(i = a->len - 1; i >= 0; i--) { - printf("%08x", a->tab[i]); - if (i != 0) - printf("_"); - } - printf("\n"); -} - -static void mpb_renorm(mpb_t *r) -{ - while (r->len > 1 && r->tab[r->len - 1] == 0) - r->len--; -} - -#ifdef USE_POW5_TABLE -static const uint32_t pow5_table[17] = { - 0x00000005, 0x00000019, 0x0000007d, 0x00000271, - 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1, - 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51, - 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1, - 0xa2bc2ec5, -}; - -static const uint8_t pow5h_table[4] = { - 0x00000001, 0x00000007, 0x00000023, 0x000000b1, -}; - -static const uint32_t pow5_inv_table[13] = { - 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1, - 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23, - 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812, - 0xc25c2684, -}; -#endif - -/* return a^b */ -static uint64_t pow_ui(uint32_t a, uint32_t b) -{ - int i, n_bits; - uint64_t r; - if (b == 0) - return 1; - if (b == 1) - return a; -#ifdef USE_POW5_TABLE - if ((a == 5 || a == 10) && b <= 17) { - r = pow5_table[b - 1]; - if (b >= 14) { - r |= (uint64_t)pow5h_table[b - 14] << 32; - } - if (a == 10) - r <<= b; - return r; - } -#endif - r = a; - n_bits = 32 - clz32(b); - for(i = n_bits - 2; i >= 0; i--) { - r *= r; - if ((b >> i) & 1) - r *= a; - } - return r; -} - -static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b) -{ - uint32_t r_inv, r; - int shift; -#ifdef USE_POW5_TABLE - if (a == 5 && b >= 1 && b <= 13) { - r = pow5_table[b - 1]; - shift = clz32(r); - r <<= shift; - r_inv = pow5_inv_table[b - 1]; - } else -#endif - { - r = pow_ui(a, b); - shift = clz32(r); - r <<= shift; - r_inv = udiv1norm_init(r); - } - *pshift = shift; - *pr_inv = r_inv; - return r; -} - -enum { - JS_RNDN, /* round to nearest, ties to even */ - JS_RNDNA, /* round to nearest, ties away from zero */ - JS_RNDZ, -}; - -static int mpb_get_bit(const mpb_t *r, int k) -{ - int l; - - l = (unsigned)k / LIMB_BITS; - k = k & (LIMB_BITS - 1); - if (l >= r->len) - return 0; - else - return (r->tab[l] >> k) & 1; -} - -/* compute round(r / 2^shift). 'shift' can be negative */ -static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode) -{ - int l, i; - - if (shift == 0) - return; - if (shift < 0) { - shift = -shift; - l = (unsigned)shift / LIMB_BITS; - shift = shift & (LIMB_BITS - 1); - if (shift != 0) { - r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0); - r->len++; - mpb_renorm(r); - } - if (l > 0) { - for(i = r->len - 1; i >= 0; i--) - r->tab[i + l] = r->tab[i]; - for(i = 0; i < l; i++) - r->tab[i] = 0; - r->len += l; - } - } else { - limb_t bit1, bit2; - int k, add_one; - - switch(rnd_mode) { - default: - case JS_RNDZ: - add_one = 0; - break; - case JS_RNDN: - case JS_RNDNA: - bit1 = mpb_get_bit(r, shift - 1); - if (bit1) { - if (rnd_mode == JS_RNDNA) { - bit2 = 1; - } else { - /* bit2 = oring of all the bits after bit1 */ - bit2 = 0; - if (shift >= 2) { - k = shift - 1; - l = (unsigned)k / LIMB_BITS; - k = k & (LIMB_BITS - 1); - for(i = 0; i < min_int(l, r->len); i++) - bit2 |= r->tab[i]; - if (l < r->len) - bit2 |= r->tab[l] & (((limb_t)1 << k) - 1); - } - } - if (bit2) { - add_one = 1; - } else { - /* round to even */ - add_one = mpb_get_bit(r, shift); - } - } else { - add_one = 0; - } - break; - } - - l = (unsigned)shift / LIMB_BITS; - shift = shift & (LIMB_BITS - 1); - if (l >= r->len) { - r->len = 1; - r->tab[0] = add_one; - } else { - if (l > 0) { - r->len -= l; - for(i = 0; i < r->len; i++) - r->tab[i] = r->tab[i + l]; - } - if (shift != 0) { - mp_shr(r->tab, r->tab, r->len, shift, 0); - mpb_renorm(r); - } - if (add_one) { - limb_t a; - a = mp_add_ui(r->tab, 1, r->len); - if (a) - r->tab[r->len++] = a; - } - } - } -} - -/* return -1, 0 or 1 */ -static int mpb_cmp(const mpb_t *a, const mpb_t *b) -{ - mp_size_t i; - if (a->len < b->len) - return -1; - else if (a->len > b->len) - return 1; - for(i = a->len - 1; i >= 0; i--) { - if (a->tab[i] != b->tab[i]) { - if (a->tab[i] < b->tab[i]) - return -1; - else - return 1; - } - } - return 0; -} - -static void mpb_set_u64(mpb_t *r, uint64_t m) -{ -#if LIMB_BITS == 64 - r->tab[0] = m; - r->len = 1; -#else - r->tab[0] = m; - r->tab[1] = m >> LIMB_BITS; - if (r->tab[1] == 0) - r->len = 1; - else - r->len = 2; -#endif -} - -static uint64_t mpb_get_u64(mpb_t *r) -{ -#if LIMB_BITS == 64 - return r->tab[0]; -#else - if (r->len == 1) { - return r->tab[0]; - } else { - return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS); - } -#endif -} - -/* floor_log2() = position of the first non zero bit or -1 if zero. */ -static int mpb_floor_log2(mpb_t *a) -{ - limb_t v; - v = a->tab[a->len - 1]; - if (v == 0) - return -1; - else - return a->len * LIMB_BITS - 1 - clz32(v); -} - -#define MUL_LOG2_RADIX_BASE_LOG2 24 - -/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */ -static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = { - 0x000000, 0xa1849d, 0x000000, 0x6e40d2, - 0x6308c9, 0x5b3065, 0x000000, 0x50c24e, - 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54, - 0x433d00, 0x418677, 0x000000, 0x3ea16b, - 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899, - 0x39680b, 0x3897b3, 0x37d5af, 0x372069, - 0x367686, 0x35d6df, 0x354072, 0x34b261, - 0x342bea, 0x33ac62, 0x000000, 0x32bfd9, - 0x3251dd, 0x31e8d6, 0x318465, -}; - -/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */ -static int mul_log2_radix(int a, int radix) -{ - int radix_bits, mult; - - if ((radix & (radix - 1)) == 0) { - /* if the radix is a power of two better to do it exactly */ - radix_bits = 31 - clz32(radix); - if (a < 0) - a -= radix_bits - 1; - return a / radix_bits; - } else { - mult = mul_log2_radix_table[radix - 2]; - return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2; - } -} - -#if 0 -static void build_mul_log2_radix_table(void) -{ - int base, radix, mult, col, base_log2; - - base_log2 = 24; - base = 1 << base_log2; - col = 0; - for(radix = 2; radix <= 36; radix++) { - if ((radix & (radix - 1)) == 0) - mult = 0; - else - mult = lrint((double)base / log2(radix)); - printf("0x%06x, ", mult); - if (++col == 4) { - printf("\n"); - col = 0; - } - } - printf("\n"); -} - -static void mul_log2_radix_test(void) -{ - int radix, i, ref, r; - - for(radix = 2; radix <= 36; radix++) { - for(i = -2048; i <= 2047; i++) { - ref = (int)floor((double)i / log2(radix)); - r = mul_log2_radix(i, radix); - if (ref != r) { - printf("ERROR: radix=%d i=%d r=%d ref=%d\n", - radix, i, r, ref); - exit(1); - } - } - } - if (0) - build_mul_log2_radix_table(); -} -#endif - -static void u32toa_len(char *buf, uint32_t n, size_t len) -{ - int digit, i; - for(i = len - 1; i >= 0; i--) { - digit = n % 10; - n = n / 10; - buf[i] = digit + '0'; - } -} - -/* for power of 2 radixes. len >= 1 */ -static void u64toa_bin_len(char *buf, uint64_t n, unsigned int radix_bits, int len) -{ - int digit, i; - unsigned int mask; - - mask = (1 << radix_bits) - 1; - for(i = len - 1; i >= 0; i--) { - digit = n & mask; - n >>= radix_bits; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } -} - -/* len >= 1. 2 <= radix <= 36 */ -static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) -{ - int digit, i; - - if (radix == 10) { - /* specific case with constant divisor */ -#if LIMB_BITS == 32 - u32toa_len(buf, n, len); -#else - /* XXX: optimize */ - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % 10; - n = (limb_t)n / 10; - buf[i] = digit + '0'; - } -#endif - } else { - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % radix; - n = (limb_t)n / radix; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } - } -} - -size_t u32toa(char *buf, uint32_t n) -{ - char buf1[10], *q; - size_t len; - - q = buf1 + sizeof(buf1); - do { - *--q = n % 10 + '0'; - n /= 10; - } while (n != 0); - len = buf1 + sizeof(buf1) - q; - memcpy(buf, q, len); - return len; -} - -size_t i32toa(char *buf, int32_t n) -{ - if (n >= 0) { - return u32toa(buf, n); - } else { - buf[0] = '-'; - return u32toa(buf + 1, -(uint32_t)n) + 1; - } -} - -#ifdef USE_FAST_INT -size_t u64toa(char *buf, uint64_t n) -{ - if (n < 0x100000000) { - return u32toa(buf, n); - } else { - uint64_t n1; - char *q = buf; - uint32_t n2; - - n1 = n / 1000000000; - n %= 1000000000; - if (n1 >= 0x100000000) { - n2 = n1 / 1000000000; - n1 = n1 % 1000000000; - /* at most two digits */ - if (n2 >= 10) { - *q++ = n2 / 10 + '0'; - n2 %= 10; - } - *q++ = n2 + '0'; - u32toa_len(q, n1, 9); - q += 9; - } else { - q += u32toa(q, n1); - } - u32toa_len(q, n, 9); - q += 9; - return q - buf; - } -} - -size_t i64toa(char *buf, int64_t n) -{ - if (n >= 0) { - return u64toa(buf, n); - } else { - buf[0] = '-'; - return u64toa(buf + 1, -(uint64_t)n) + 1; - } -} - -/* XXX: only tested for 1 <= n < 2^53 */ -size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix) -{ - int radix_bits, l; - if (likely(radix == 10)) - return u64toa(buf, n); - if ((radix & (radix - 1)) == 0) { - radix_bits = 31 - clz32(radix); - if (n == 0) - l = 1; - else - l = (64 - clz64(n) + radix_bits - 1) / radix_bits; - u64toa_bin_len(buf, n, radix_bits, l); - return l; - } else { - char buf1[41], *q; /* maximum length for radix = 3 */ - size_t len; - int digit; - q = buf1 + sizeof(buf1); - do { - digit = n % radix; - n /= radix; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - *--q = digit; - } while (n != 0); - len = buf1 + sizeof(buf1) - q; - memcpy(buf, q, len); - return len; - } -} - -size_t i64toa_radix(char *buf, int64_t n, unsigned int radix) -{ - if (n >= 0) { - return u64toa_radix(buf, n, radix); - } else { - buf[0] = '-'; - return u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1; - } -} -#endif /* USE_FAST_INT */ - -static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { -#if LIMB_BITS == 32 -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, -#else -64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, -#endif -}; - -static const uint32_t 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, -}; - -/* XXX: remove the table ? */ -static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = { - 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, -}; - -/* we limit the maximum number of significant digits for atod to about - 128 bits of precision for non power of two bases. The only - requirement for Javascript is at least 20 digits in base 10. For - power of two bases, we do an exact rounding in all the cases. */ -static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = { - 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24, -}; - -/* if abs(d) >= B^max_exponent, it is an overflow */ -static const int16_t max_exponent[JS_RADIX_MAX - 1] = { - 1024, 647, 512, 442, 397, 365, 342, 324, - 309, 297, 286, 277, 269, 263, 256, 251, - 246, 242, 237, 234, 230, 227, 224, 221, - 218, 216, 214, 211, 209, 207, 205, 203, - 202, 200, 199, -}; - -/* if abs(d) <= B^min_exponent, it is an underflow */ -static const int16_t min_exponent[JS_RADIX_MAX - 1] = { --1075, -679, -538, -463, -416, -383, -359, -340, - -324, -311, -300, -291, -283, -276, -269, -263, - -258, -254, -249, -245, -242, -238, -235, -232, - -229, -227, -224, -222, -220, -217, -215, -214, - -212, -210, -208, -}; - -#if 0 -void build_tables(void) -{ - int r, j, radix, n, col, i; - - /* radix_base_table */ - for(radix = 2; radix <= 36; radix++) { - r = 1; - for(j = 0; j < digits_per_limb_table[radix - 2]; j++) { - r *= radix; - } - printf(" 0x%08x,", r); - if ((radix % 4) == 1) - printf("\n"); - } - printf("\n"); - - /* dtoa_max_digits_table */ - for(radix = 2; radix <= 36; radix++) { - /* Note: over estimated when the radix is a power of two */ - printf(" %d,", 1 + (int)ceil(53.0 / log2(radix))); - } - printf("\n"); - - /* atod_max_digits_table */ - for(radix = 2; radix <= 36; radix++) { - if ((radix & (radix - 1)) == 0) { - /* 64 bits is more than enough */ - n = (int)floor(64.0 / log2(radix)); - } else { - n = (int)floor(128.0 / log2(radix)); - } - printf(" %d,", n); - } - printf("\n"); - - printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n"); - col = 0; - for(radix = 2; radix <= 36; radix++) { - printf("%5d, ", (int)ceil(1024 / log2(radix))); - if (++col == 8) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); - - printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n"); - col = 0; - for(radix = 2; radix <= 36; radix++) { - printf("%5d, ", (int)floor(-1075 / log2(radix))); - if (++col == 8) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); - - printf("static const uint32_t pow5_table[16] = {\n"); - col = 0; - for(i = 2; i <= 17; i++) { - r = 1; - for(j = 0; j < i; j++) { - r *= 5; - } - printf("0x%08x, ", r); - if (++col == 4) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); - - /* high part */ - printf("static const uint8_t pow5h_table[4] = {\n"); - col = 0; - for(i = 14; i <= 17; i++) { - uint64_t r1; - r1 = 1; - for(j = 0; j < i; j++) { - r1 *= 5; - } - printf("0x%08x, ", (uint32_t)(r1 >> 32)); - if (++col == 4) { - col = 0; - printf("\n"); - } - } - printf("\n};\n\n"); -} -#endif - -/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits, - the dot is not displayed. 'a' is modified. */ -static int output_digits(char *buf, - mpb_t *a, int radix, int n_digits1, - int dot_pos) -{ - int n_digits, digits_per_limb, radix_bits, n, len; - - n_digits = n_digits1; - if ((radix & (radix - 1)) == 0) { - /* radix = 2^radix_bits */ - radix_bits = 31 - clz32(radix); - } else { - radix_bits = 0; - } - digits_per_limb = digits_per_limb_table[radix - 2]; - if (radix_bits != 0) { - for(;;) { - n = min_int(n_digits, digits_per_limb); - n_digits -= n; - u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n); - if (n_digits == 0) - break; - mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ); - } - } else { - limb_t r; - while (n_digits != 0) { - n = min_int(n_digits, digits_per_limb); - n_digits -= n; - r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0); - mpb_renorm(a); - limb_to_a(buf + n_digits, r, radix, n); - } - } - - /* add the dot */ - len = n_digits1; - if (dot_pos != n_digits1) { - memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos); - buf[dot_pos] = '.'; - len++; - } - return len; -} - -/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f * - 2^-e_offset. 'f' can be negative. */ -static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, bool is_int, int e) -{ - int e_offset, d, n, n0; - - e_offset = -f * radix_shift; - if (radix1 != 1) { - d = digits_per_limb_table[radix1 - 2]; - if (f >= 0) { - limb_t h, b; - - b = 0; - n0 = 0; - while (f != 0) { - n = min_int(f, d); - if (n != n0) { - b = pow_ui(radix1, n); - n0 = n; - } - h = mp_mul1(a->tab, a->tab, a->len, b, 0); - if (h != 0) { - a->tab[a->len++] = h; - } - f -= n; - } - } else { - int extra_bits, l, shift; - limb_t r, rem, b, b_inv; - - f = -f; - l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */ - e_offset += l * LIMB_BITS; - if (!is_int) { - /* at least 'e' bits are needed in the final result for rounding */ - extra_bits = max_int(e - mpb_floor_log2(a), 0); - } else { - /* at least two extra bits are needed in the final result - for rounding */ - extra_bits = max_int(2 + e - e_offset, 0); - } - e_offset += extra_bits; - mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ); - - b = 0; - b_inv = 0; - shift = 0; - n0 = 0; - rem = 0; - while (f != 0) { - n = min_int(f, d); - if (n != n0) { - b = pow_ui_inv(&b_inv, &shift, radix1, n); - n0 = n; - } - r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift); - rem |= r; - mpb_renorm(a); - f -= n; - } - /* if the remainder is non zero, use it for rounding */ - a->tab[0] |= (rem != 0); - } - } - return e_offset; -} - -/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */ -static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f, - int rnd_mode) -{ - int e_offset; - - mpb_set_u64(tmp1, m); - e_offset = mul_pow(tmp1, radix1, radix_shift, f, true, e); - mpb_shr_round(tmp1, -e + e_offset, rnd_mode); -} - -/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */ -static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode) -{ - int e; - uint64_t m; - - if (a->tab[0] == 0 && a->len == 1) { - /* zero result */ - m = 0; - e = 0; /* don't care */ - } else { - int prec, prec1, e_min; - e = mpb_floor_log2(a) + 1 - e_offset; - prec1 = 53; - e_min = -1021; - if (e < e_min) { - /* subnormal result or zero */ - prec = prec1 - (e_min - e); - } else { - prec = prec1; - } - mpb_shr_round(a, e + e_offset - prec, rnd_mode); - m = mpb_get_u64(a); - m <<= (53 - prec); - /* mantissa overflow due to rounding */ - if (m >= (uint64_t)1 << 53) { - m >>= 1; - e++; - } - } - *pe = e; - return m; -} - -/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52 - <= m < 2^53 or m = 0. - 'a' is modified. */ -static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a, - int radix1, int radix_shift, int f, int rnd_mode) -{ - int e_offset; - - e_offset = mul_pow(a, radix1, radix_shift, f, false, 55); - return round_to_d(pe, a, e_offset, rnd_mode); -} - -#ifdef JS_DTOA_DUMP_STATS -static int out_len_count[17]; - -void js_dtoa_dump_stats(void) -{ - int i, sum; - sum = 0; - for(i = 0; i < 17; i++) - sum += out_len_count[i]; - for(i = 0; i < 17; i++) { - printf("%2d %8d %5.2f%%\n", - i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100); - } -} -#endif - -/* return a maximum bound of the string length. The bound depends on - 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED - is enabled. */ -int js_dtoa_max_len(double d, int radix, int n_digits, int flags) -{ - int fmt = flags & JS_DTOA_FORMAT_MASK; - int n, e; - uint64_t a; - - if (fmt != JS_DTOA_FORMAT_FRAC) { - if (fmt == JS_DTOA_FORMAT_FREE) { - n = dtoa_max_digits_table[radix - 2]; - } else { - n = n_digits; - } - if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) { - /* no exponential */ - a = float64_as_uint64(d); - e = (a >> 52) & 0x7ff; - if (e == 0x7ff) { - /* NaN, Infinity */ - n = 0; - } else { - e -= 1023; - /* XXX: adjust */ - n += 10 + abs(mul_log2_radix(e - 1, radix)); - } - } else { - /* extra: sign, 1 dot and exponent "e-1000" */ - n += 1 + 1 + 6; - } - } else { - a = float64_as_uint64(d); - e = (a >> 52) & 0x7ff; - if (e == 0x7ff) { - /* NaN, Infinity */ - n = 0; - } else { - /* high bound for the integer part */ - e -= 1023; - /* x < 2^(e + 1) */ - if (e < 0) { - n = 1; - } else { - n = 2 + mul_log2_radix(e - 1, radix); - } - /* sign, extra digit, 1 dot */ - n += 1 + 1 + 1 + n_digits; - } - } - return max_int(n, 9); /* also include NaN and [-]Infinity */ -} - -#if defined(__SANITIZE_ADDRESS__) && 0 -static void *dtoa_malloc(uint64_t **pptr, size_t size) -{ - return malloc(size); -} -static void dtoa_free(void *ptr) -{ - free(ptr); -} -#else -static void *dtoa_malloc(uint64_t **pptr, size_t size) -{ - void *ret; - ret = *pptr; - *pptr += (size + 7) / 8; - return ret; -} - -static void dtoa_free(void *ptr) -{ -} -#endif - -/* return the length */ -int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, - JSDTOATempMem *tmp_mem) -{ - uint64_t a, m, *mptr = tmp_mem->mem; - int e, sgn, l, E, P, i, E_max, radix1, radix_shift; - char *q; - mpb_t *tmp1, *mant_max; - int fmt = flags & JS_DTOA_FORMAT_MASK; - - tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); - mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX); - assert((mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0])); - - radix_shift = ctz32(radix); - radix1 = radix >> radix_shift; - a = float64_as_uint64(d); - sgn = a >> 63; - e = (a >> 52) & 0x7ff; - m = a & (((uint64_t)1 << 52) - 1); - q = buf; - if (e == 0x7ff) { - if (m == 0) { - if (sgn) - *q++ = '-'; - memcpy(q, "Infinity", 8); - q += 8; - } else { - memcpy(q, "NaN", 3); - q += 3; - } - goto done; - } else if (e == 0) { - if (m == 0) { - tmp1->len = 1; - tmp1->tab[0] = 0; - E = 1; - if (fmt == JS_DTOA_FORMAT_FREE) - P = 1; - else if (fmt == JS_DTOA_FORMAT_FRAC) - P = n_digits + 1; - else - P = n_digits; - /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */ - if (sgn && (flags & JS_DTOA_MINUS_ZERO)) - *q++ = '-'; - goto output; - } - /* denormal number: convert to a normal number */ - l = clz64(m) - 11; - e -= l - 1; - m <<= l; - } else { - m |= (uint64_t)1 << 52; - } - if (sgn) - *q++ = '-'; - /* remove the bias */ - e -= 1022; - /* d = 2^(e-53)*m */ - // printf("m=0x%016" PRIx64 " e=%d\n", m, e); -#ifdef USE_FAST_INT - if (fmt == JS_DTOA_FORMAT_FREE && - e >= 1 && e <= 53 && - (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 && - (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) { - m >>= 53 - e; - /* 'm' is never zero */ - q += u64toa_radix(q, m, radix); - goto done; - } -#endif - - /* this choice of E implies F=round(x*B^(P-E) is such as: - B^(P-1) <= F < 2.B^P. */ - E = 1 + mul_log2_radix(e - 1, radix); - - if (fmt == JS_DTOA_FORMAT_FREE) { - int P_max, E0, e1, E_found, P_found; - uint64_t m1, mant_found, mant, mant_max1; - /* P_max is guarranteed to work by construction */ - P_max = dtoa_max_digits_table[radix - 2]; - E0 = E; - E_found = 0; - P_found = 0; - mant_found = 0; - /* find the minimum number of digits by successive tries */ - P = P_max; /* P_max is guarateed to work */ - for(;;) { - /* mant_max always fits on 64 bits */ - mant_max1 = pow_ui(radix, P); - /* compute the mantissa in base B */ - E = E0; - for(;;) { - /* XXX: add inexact flag */ - mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN); - mant = mpb_get_u64(tmp1); - if (mant < mant_max1) - break; - E++; /* at most one iteration is possible */ - } - /* remove useless trailing zero digits */ - while ((mant % radix) == 0) { - mant /= radix; - P--; - } - /* garanteed to work for P = P_max */ - if (P_found == 0) - goto prec_found; - /* convert back to base 2 */ - mpb_set_u64(tmp1, mant); - m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN); - // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1); - /* Note: (m, e) is never zero here, so the exponent for m1 - = 0 does not matter */ - if (m1 == m && e1 == e) { - prec_found: - P_found = P; - E_found = E; - mant_found = mant; - if (P == 1) - break; - P--; /* try lower exponent */ - } else { - break; - } - } - P = P_found; - E = E_found; - mpb_set_u64(tmp1, mant_found); -#ifdef JS_DTOA_DUMP_STATS - if (radix == 10) { - out_len_count[P - 1]++; - } -#endif - } else if (fmt == JS_DTOA_FORMAT_FRAC) { - int len; - - assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS); - /* P = max_int(E, 1) + n_digits; */ - /* frac is rounded using RNDNA */ - mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA); - - /* we add one extra digit on the left and remove it if needed - to avoid testing if the result is < radix^P */ - len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits, - max_int(E + 1, 1)); - if (q[0] == '0' && len >= 2 && q[1] != '.') { - len--; - memmove(q, q + 1, len); - } - q += len; - goto done; - } else { - int pow_shift; - assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS); - P = n_digits; - /* mant_max = radix^P */ - mant_max->len = 1; - mant_max->tab[0] = 1; - pow_shift = mul_pow(mant_max, radix1, radix_shift, P, false, 0); - mpb_shr_round(mant_max, pow_shift, JS_RNDZ); - - for(;;) { - /* fixed and frac are rounded using RNDNA */ - mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA); - if (mpb_cmp(tmp1, mant_max) < 0) - break; - E++; /* at most one iteration is possible */ - } - } - output: - if (fmt == JS_DTOA_FORMAT_FIXED) - E_max = n_digits; - else - E_max = dtoa_max_digits_table[radix - 2] + 4; - if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED || - ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) { - q += output_digits(q, tmp1, radix, P, 1); - E--; - if (radix == 10) { - *q++ = 'e'; - } else if (radix1 == 1 && radix_shift <= 4) { - E *= radix_shift; - *q++ = 'p'; - } else { - *q++ = '@'; - } - if (E < 0) { - *q++ = '-'; - E = -E; - } else { - *q++ = '+'; - } - q += u32toa(q, E); - } else if (E <= 0) { - *q++ = '0'; - *q++ = '.'; - for(i = 0; i < -E; i++) - *q++ = '0'; - q += output_digits(q, tmp1, radix, P, P); - } else { - q += output_digits(q, tmp1, radix, P, min_int(P, E)); - for(i = 0; i < E - P; i++) - *q++ = '0'; - } - done: - *q = '\0'; - dtoa_free(mant_max); - dtoa_free(tmp1); - return q - buf; -} - -static inline int 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; -} - -/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */ -static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a) -{ - int i; - if (r->tab[0] == 0 && r->len == 1) { - r->tab[0] = a; - } else { - if (radix_base == 0) { - for(i = r->len; i >= 0; i--) { - r->tab[i + 1] = r->tab[i]; - } - r->tab[0] = a; - } else { - r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len, - radix_base, a); - } - r->len++; - mpb_renorm(r); - } -} - -/* XXX: add fast path for small integers */ -double js_atod(const char *str, const char **pnext, int radix, int flags, - JSATODTempMem *tmp_mem) -{ - uint64_t *mptr = tmp_mem->mem; - const char *p, *p_start; - limb_t cur_limb, radix_base, extra_digits; - int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift; - int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos; - mpb_t *tmp0; - double dval; - bool is_bin_exp, is_zero, expn_overflow; - uint64_t m, a; - - tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); - assert((mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0])); - /* optional separator between digits */ - sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - - p = str; - is_neg = 0; - if (p[0] == '+') { - p++; - p_start = p; - } else if (p[0] == '-') { - is_neg = 1; - p++; - p_start = p; - } else { - p_start = p; - } - - 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 & JS_ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 8; - } else if ((p[1] == 'b' || p[1] == 'B') && - radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 2; - } else if ((p[1] >= '0' && p[1] <= '9') && - radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) { - int i; - 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 (to_digit((uint8_t)*p) >= radix) - goto fail; - no_prefix: ; - } else { - if (!(flags & JS_ATOD_INT_ONLY) && js__strstart(p, "Infinity", &p)) - goto overflow; - } - if (radix == 0) - radix = 10; - - cur_limb = 0; - expn_offset = 0; - digit_count = 0; - limb_digit_count = 0; - max_digits = atod_max_digits_table[radix - 2]; - digits_per_limb = digits_per_limb_table[radix - 2]; - radix_base = radix_base_table[radix - 2]; - radix_shift = ctz32(radix); - radix1 = radix >> radix_shift; - if (radix1 == 1) { - /* radix = 2^radix_bits */ - radix_bits = radix_shift; - } else { - radix_bits = 0; - } - tmp0->len = 1; - tmp0->tab[0] = 0; - extra_digits = 0; - pos = 0; - dot_pos = -1; - /* skip leading zeros */ - for(;;) { - if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && - !(flags & JS_ATOD_INT_ONLY)) { - if (*p == sep) - goto fail; - if (dot_pos >= 0) - break; - dot_pos = pos; - p++; - } - if (*p == sep && p > p_start && p[1] == '0') - p++; - if (*p != '0') - break; - p++; - pos++; - } - - sig_pos = pos; - for(;;) { - limb_t c; - if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && - !(flags & JS_ATOD_INT_ONLY)) { - if (*p == sep) - goto fail; - if (dot_pos >= 0) - break; - dot_pos = pos; - p++; - } - if (*p == sep && p > p_start && to_digit(p[1]) < radix) - p++; - c = to_digit(*p); - if (c >= radix) - break; - p++; - pos++; - if (digit_count < max_digits) { - /* XXX: could be faster when radix_bits != 0 */ - cur_limb = cur_limb * radix + c; - limb_digit_count++; - if (limb_digit_count == digits_per_limb) { - mpb_mul1_base(tmp0, radix_base, cur_limb); - cur_limb = 0; - limb_digit_count = 0; - } - digit_count++; - } else { - extra_digits |= c; - } - } - if (limb_digit_count != 0) { - mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb); - } - if (digit_count == 0) { - is_zero = true; - expn_offset = 0; - } else { - is_zero = false; - if (dot_pos < 0) - dot_pos = pos; - expn_offset = sig_pos + digit_count - dot_pos; - } - - /* Use the extra digits for rounding if the base is a power of - two. Otherwise they are just truncated. */ - if (radix_bits != 0 && extra_digits != 0) { - tmp0->tab[0] |= 1; - } - - /* parse the exponent, if any */ - expn = 0; - expn_overflow = false; - is_bin_exp = false; - if (!(flags & JS_ATOD_INT_ONLY) && - ((radix == 10 && (*p == 'e' || *p == 'E')) || - (radix != 10 && (*p == '@' || - (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) && - p > p_start) { - bool exp_is_neg; - int c; - is_bin_exp = (*p == 'p' || *p == 'P'); - p++; - exp_is_neg = false; - if (*p == '+') { - p++; - } else if (*p == '-') { - exp_is_neg = true; - p++; - } - c = to_digit(*p); - if (c >= 10) - goto fail; /* XXX: could stop before the exponent part */ - expn = c; - p++; - for(;;) { - if (*p == sep && to_digit(p[1]) < 10) - p++; - c = to_digit(*p); - if (c >= 10) - break; - if (!expn_overflow) { - if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) { - expn_overflow = true; - } else { - expn = expn * 10 + c; - } - } - p++; - } - if (exp_is_neg) - expn = -expn; - /* if zero result, the exponent can be arbitrarily large */ - if (!is_zero && expn_overflow) { - if (exp_is_neg) - a = 0; - else - a = (uint64_t)0x7ff << 52; /* infinity */ - goto done; - } - } - - if (p == p_start) - goto fail; - - if (is_zero) { - a = 0; - } else { - int expn1; - if (radix_bits != 0) { - if (!is_bin_exp) - expn *= radix_bits; - expn -= expn_offset * radix_bits; - expn1 = expn + digit_count * radix_bits; - if (expn1 >= 1024 + radix_bits) - goto overflow; - else if (expn1 <= -1075) - goto underflow; - m = round_to_d(&e, tmp0, -expn, JS_RNDN); - } else { - expn -= expn_offset; - expn1 = expn + digit_count; - if (expn1 >= max_exponent[radix - 2] + 1) - goto overflow; - else if (expn1 <= min_exponent[radix - 2]) - goto underflow; - m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN); - } - if (m == 0) { - underflow: - a = 0; - } else if (e > 1024) { - overflow: - /* overflow */ - a = (uint64_t)0x7ff << 52; - } else if (e < -1073) { - /* underflow */ - /* XXX: check rounding */ - a = 0; - } else if (e < -1021) { - /* subnormal */ - a = m >> (-e - 1021); - } else { - a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1)); - } - } - done: - a |= (uint64_t)is_neg << 63; - dval = uint64_as_float64(a); - done1: - if (pnext) - *pnext = p; - dtoa_free(tmp0); - return dval; - fail: - dval = NAN; - goto done1; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/dtoa.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/dtoa.h deleted file mode 100755 index 85be0406..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/dtoa.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/fuzz.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/fuzz.c deleted file mode 100755 index fca8fa41..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/fuzz.c +++ /dev/null @@ -1,22 +0,0 @@ -// clang -g -O1 -fsanitize=fuzzer -o fuzz fuzz.c -#include "quickjs.h" -#include "quickjs.c" -#include "cutils.c" -#include "libregexp.c" -#include "libunicode.c" -#include - -int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) -{ - JSRuntime *rt = JS_NewRuntime(); - if (!rt) - exit(1); - JSContext *ctx = JS_NewContext(rt); - if (!ctx) - exit(1); - JSValue val = JS_ReadObject(ctx, buf, len, /*flags*/0); - JS_FreeValue(ctx, val); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp-opcode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp-opcode.h deleted file mode 100755 index 5c1714ab..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp-opcode.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ - -#ifdef DEF - -DEF(invalid, 1) /* never used */ -DEF(char8, 2) /* 7 bits in fact */ -DEF(char16, 3) -DEF(char32, 5) -DEF(dot, 1) -DEF(any, 1) /* same as dot but match any character including line terminator */ -DEF(line_start, 1) -DEF(line_end, 1) -DEF(goto, 5) -DEF(split_goto_first, 5) -DEF(split_next_first, 5) -DEF(match, 1) -DEF(save_start, 2) /* save start position */ -DEF(save_end, 2) /* save end position, must come after saved_start */ -DEF(save_reset, 3) /* reset save positions */ -DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ -DEF(push_i32, 5) /* push integer on the stack */ -DEF(drop, 1) -DEF(word_boundary, 1) -DEF(not_word_boundary, 1) -DEF(back_reference, 2) -DEF(backward_back_reference, 2) /* must come after back_reference */ -DEF(range, 3) /* variable length */ -DEF(range32, 3) /* variable length */ -DEF(lookahead, 5) -DEF(negative_lookahead, 5) -DEF(push_char_pos, 1) /* push the character position on the stack */ -DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */ -DEF(prev, 1) /* go to the previous char */ -DEF(simple_greedy_quant, 17) - -#endif /* DEF */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp.c deleted file mode 100755 index ac60650c..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp.c +++ /dev/null @@ -1,2676 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "libregexp.h" - -/* - TODO: - - - Add a lock step execution mode (=linear time execution guaranteed) - when the regular expression is "simple" i.e. no backreference nor - complicated lookahead. The opcodes are designed for this execution - model. -*/ - -#if defined(TEST) -#define DUMP_REOP -#endif - -typedef enum { -#define DEF(id, size) REOP_ ## id, -#include "libregexp-opcode.h" -#undef DEF - REOP_COUNT, -} REOPCodeEnum; - -#define CAPTURE_COUNT_MAX 255 -#define STACK_SIZE_MAX 255 -/* must be large enough to have a negligible runtime cost and small - enough to call the interrupt callback often. */ -#define INTERRUPT_COUNTER_INIT 10000 - -/* unicode code points */ -#define CP_LS 0x2028 -#define CP_PS 0x2029 - -#define TMP_BUF_SIZE 128 - -// invariant: is_unicode ^ unicode_sets (or neither, but not both) -typedef struct { - DynBuf byte_code; - const uint8_t *buf_ptr; - const uint8_t *buf_end; - const uint8_t *buf_start; - int re_flags; - bool is_unicode; - bool unicode_sets; - bool ignore_case; - bool dotall; - int capture_count; - int total_capture_count; /* -1 = not computed yet */ - int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ - void *opaque; - DynBuf group_names; - union { - char error_msg[TMP_BUF_SIZE]; - char tmp_buf[TMP_BUF_SIZE]; - } u; -} REParseState; - -typedef struct { -#ifdef DUMP_REOP - const char *name; -#endif - uint8_t size; -} REOpCode; - -static const REOpCode reopcode_info[REOP_COUNT] = { -#ifdef DUMP_REOP -#define DEF(id, size) { #id, size }, -#else -#define DEF(id, size) { size }, -#endif -#include "libregexp-opcode.h" -#undef DEF -}; - -#define RE_HEADER_FLAGS 0 -#define RE_HEADER_CAPTURE_COUNT 2 -#define RE_HEADER_STACK_SIZE 3 -#define RE_HEADER_BYTECODE_LEN 4 - -#define RE_HEADER_LEN 8 - -static inline int lre_is_digit(int c) { - return c >= '0' && c <= '9'; -} - -/* insert 'len' bytes at position 'pos'. Return < 0 if error. */ -static int dbuf_insert(DynBuf *s, int pos, int len) -{ - if (dbuf_realloc(s, s->size + len)) - return -1; - memmove(s->buf + pos + len, s->buf + pos, s->size - pos); - s->size += len; - return 0; -} - -static const uint16_t char_range_d[] = { - 1, - 0x0030, 0x0039 + 1, -}; - -/* code point ranges for Zs,Zl or Zp property */ -static const uint16_t char_range_s[] = { - 10, - 0x0009, 0x000D + 1, - 0x0020, 0x0020 + 1, - 0x00A0, 0x00A0 + 1, - 0x1680, 0x1680 + 1, - 0x2000, 0x200A + 1, - /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ - /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ - 0x2028, 0x2029 + 1, - 0x202F, 0x202F + 1, - 0x205F, 0x205F + 1, - 0x3000, 0x3000 + 1, - /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ - 0xFEFF, 0xFEFF + 1, -}; - -bool lre_is_space(int c) -{ - int i, n, low, high; - n = (countof(char_range_s) - 1) / 2; - for(i = 0; i < n; i++) { - low = char_range_s[2 * i + 1]; - if (c < low) - return false; - high = char_range_s[2 * i + 2]; - if (c < high) - return true; - } - return false; -} - -uint32_t const lre_id_start_table_ascii[4] = { - /* $ A-Z _ a-z */ - 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE -}; - -uint32_t const lre_id_continue_table_ascii[4] = { - /* $ 0-9 A-Z _ a-z */ - 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE -}; - - -static const uint16_t char_range_w[] = { - 4, - 0x0030, 0x0039 + 1, - 0x0041, 0x005A + 1, - 0x005F, 0x005F + 1, - 0x0061, 0x007A + 1, -}; - -#define CLASS_RANGE_BASE 0x40000000 - -typedef enum { - CHAR_RANGE_d, - CHAR_RANGE_D, - CHAR_RANGE_s, - CHAR_RANGE_S, - CHAR_RANGE_w, - CHAR_RANGE_W, -} CharRangeEnum; - -static const uint16_t *char_range_table[] = { - char_range_d, - char_range_s, - char_range_w, -}; - -static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) -{ - bool invert; - const uint16_t *c_pt; - int len, i; - - invert = c & 1; - c_pt = char_range_table[c >> 1]; - len = *c_pt++; - cr_init(cr, s->opaque, lre_realloc); - for(i = 0; i < len * 2; i++) { - if (cr_add_point(cr, c_pt[i])) - goto fail; - } - if (invert) { - if (cr_invert(cr)) - goto fail; - } - return 0; - fail: - cr_free(cr); - return -1; -} - -#ifdef DUMP_REOP -static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, - int buf_len) -{ - int pos, len, opcode, bc_len, re_flags, i; - uint32_t val; - - assert(buf_len >= RE_HEADER_LEN); - - re_flags = lre_get_flags(buf); - bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); - assert(bc_len + RE_HEADER_LEN <= buf_len); - printf("flags: 0x%x capture_count=%d stack_size=%d\n", - re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); - if (re_flags & LRE_FLAG_NAMED_GROUPS) { - const char *p; - p = (char *)buf + RE_HEADER_LEN + bc_len; - printf("named groups: "); - for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) { - if (i != 1) - printf(","); - printf("<%s>", p); - p += strlen(p) + 1; - } - printf("\n"); - assert(p == (char *)(buf + buf_len)); - } - printf("bytecode_len=%d\n", bc_len); - - buf += RE_HEADER_LEN; - pos = 0; - while (pos < bc_len) { - printf("%5u: ", pos); - opcode = buf[pos]; - len = reopcode_info[opcode].size; - if (opcode >= REOP_COUNT) { - printf(" invalid opcode=0x%02x\n", opcode); - break; - } - if ((pos + len) > bc_len) { - printf(" buffer overflow (opcode=0x%02x)\n", opcode); - break; - } - printf("%s", reopcode_info[opcode].name); - switch(opcode) { - case REOP_char8: - val = get_u8(buf + pos + 1); - goto printchar; - case REOP_char16: - val = get_u16(buf + pos + 1); - goto printchar; - case REOP_char32: - val = get_u32(buf + pos + 1); - printchar: - if (val >= ' ' && val <= 126) - printf(" '%c'", val); - else - printf(" 0x%08x", val); - break; - case REOP_goto: - case REOP_split_goto_first: - case REOP_split_next_first: - case REOP_loop: - case REOP_lookahead: - case REOP_negative_lookahead: - val = get_u32(buf + pos + 1); - val += (pos + 5); - printf(" %u", val); - break; - case REOP_simple_greedy_quant: - printf(" %u %u %u %u", - get_u32(buf + pos + 1) + (pos + 17), - get_u32(buf + pos + 1 + 4), - get_u32(buf + pos + 1 + 8), - get_u32(buf + pos + 1 + 12)); - break; - case REOP_save_start: - case REOP_save_end: - case REOP_back_reference: - case REOP_backward_back_reference: - printf(" %u", buf[pos + 1]); - break; - case REOP_save_reset: - printf(" %u %u", buf[pos + 1], buf[pos + 2]); - break; - case REOP_push_i32: - val = get_u32(buf + pos + 1); - printf(" %d", val); - break; - case REOP_range: - { - int n, i; - n = get_u16(buf + pos + 1); - len += n * 4; - for(i = 0; i < n * 2; i++) { - val = get_u16(buf + pos + 3 + i * 2); - printf(" 0x%04x", val); - } - } - break; - case REOP_range32: - { - int n, i; - n = get_u16(buf + pos + 1); - len += n * 8; - for(i = 0; i < n * 2; i++) { - val = get_u32(buf + pos + 3 + i * 4); - printf(" 0x%08x", val); - } - } - break; - default: - break; - } - printf("\n"); - pos += len; - } -} -#endif - -static void re_emit_op(REParseState *s, int op) -{ - dbuf_putc(&s->byte_code, op); -} - -/* return the offset of the u32 value */ -static int re_emit_op_u32(REParseState *s, int op, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val); - return pos; -} - -static int re_emit_goto(REParseState *s, int op, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val - (pos + 4)); - return pos; -} - -static void re_emit_op_u8(REParseState *s, int op, uint32_t val) -{ - dbuf_putc(&s->byte_code, op); - dbuf_putc(&s->byte_code, val); -} - -static void re_emit_op_u16(REParseState *s, int op, uint32_t val) -{ - dbuf_putc(&s->byte_code, op); - dbuf_put_u16(&s->byte_code, val); -} - -static int JS_PRINTF_FORMAT_ATTR(2, 3) re_parse_error(REParseState *s, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap); - va_end(ap); - return -1; -} - -static int re_parse_out_of_memory(REParseState *s) -{ - return re_parse_error(s, "out of memory"); -} - -static int lre_check_size(REParseState *s) -{ - if (s->byte_code.size < 64*1024*1024) - return 0; - return re_parse_out_of_memory(s); -} - -/* If allow_overflow is false, return -1 in case of - overflow. Otherwise return INT32_MAX. */ -static int parse_digits(const uint8_t **pp, bool allow_overflow) -{ - const uint8_t *p; - uint64_t v; - int c; - - p = *pp; - v = 0; - for(;;) { - c = *p; - if (c < '0' || c > '9') - break; - v = v * 10 + c - '0'; - if (v >= INT32_MAX) { - if (allow_overflow) - v = INT32_MAX; - else - return -1; - } - p++; - } - *pp = p; - return v; -} - -static int re_parse_expect(REParseState *s, const uint8_t **pp, int c) -{ - const uint8_t *p; - p = *pp; - if (*p != c) - return re_parse_error(s, "expecting '%c'", c); - p++; - *pp = p; - return 0; -} - -/* Parse an escape sequence, *pp points after the '\': - allow_utf16 value: - 0 : no UTF-16 escapes allowed - 1 : UTF-16 escapes allowed - 2 : UTF-16 escapes allowed and escapes of surrogate pairs are - converted to a unicode character (unicode regexp case). - - Return the unicode char and update *pp if recognized, - return -1 if malformed escape, - return -2 otherwise. */ -int lre_parse_escape(const uint8_t **pp, int allow_utf16) -{ - const uint8_t *p; - uint32_t c; - - p = *pp; - c = *p++; - switch(c) { - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case 'x': - case 'u': - { - int h, n, i; - uint32_t c1; - - if (*p == '{' && allow_utf16) { - p++; - c = 0; - for(;;) { - h = from_hex(*p++); - if (h < 0) - return -1; - c = (c << 4) | h; - if (c > 0x10FFFF) - return -1; - if (*p == '}') - break; - } - p++; - } else { - if (c == 'x') { - n = 2; - } else { - n = 4; - } - - c = 0; - for(i = 0; i < n; i++) { - h = from_hex(*p++); - if (h < 0) { - return -1; - } - c = (c << 4) | h; - } - if (is_hi_surrogate(c) && - allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { - /* convert an escaped surrogate pair into a - unicode char */ - c1 = 0; - for(i = 0; i < 4; i++) { - h = from_hex(p[2 + i]); - if (h < 0) - break; - c1 = (c1 << 4) | h; - } - if (i == 4 && is_lo_surrogate(c1)) { - p += 6; - c = from_surrogate(c, c1); - } - } - } - } - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - c -= '0'; - if (allow_utf16 == 2) { - /* only accept \0 not followed by digit */ - if (c != 0 || lre_is_digit(*p)) - return -1; - } else { - /* parse a legacy octal sequence */ - uint32_t v; - v = *p - '0'; - if (v > 7) - break; - c = (c << 3) | v; - p++; - if (c >= 32) - break; - v = *p - '0'; - if (v > 7) - break; - c = (c << 3) | v; - p++; - } - break; - default: - return -2; - } - *pp = p; - return c; -} - -/* XXX: we use the same chars for name and value */ -static bool is_unicode_char(int c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c == '_')); -} - -static int parse_unicode_property(REParseState *s, CharRange *cr, - const uint8_t **pp, bool is_inv) -{ - const uint8_t *p; - char name[64], value[64]; - char *q; - bool script_ext; - int ret; - - p = *pp; - if (*p != '{') - return re_parse_error(s, "expecting '{' after \\p"); - p++; - q = name; - while (is_unicode_char(*p)) { - if ((q - name) >= sizeof(name) - 1) - goto unknown_property_name; - *q++ = *p++; - } - *q = '\0'; - q = value; - if (*p == '=') { - p++; - while (is_unicode_char(*p)) { - if ((q - value) >= sizeof(value) - 1) - return re_parse_error(s, "unknown unicode property value"); - *q++ = *p++; - } - } - *q = '\0'; - if (*p != '}') - return re_parse_error(s, "expecting '}'"); - p++; - // printf("name=%s value=%s\n", name, value); - - if (!strcmp(name, "Script") || !strcmp(name, "sc")) { - script_ext = false; - goto do_script; - } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { - script_ext = true; - do_script: - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_script(cr, value, script_ext); - if (ret) { - cr_free(cr); - if (ret == -2) - return re_parse_error(s, "unknown unicode script"); - else - goto out_of_memory; - } - } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_general_category(cr, value); - if (ret) { - cr_free(cr); - if (ret == -2) - return re_parse_error(s, "unknown unicode general category"); - else - goto out_of_memory; - } - } else if (value[0] == '\0') { - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_general_category(cr, name); - if (ret == -1) { - cr_free(cr); - goto out_of_memory; - } - if (ret < 0) { - ret = unicode_prop(cr, name); - if (ret) { - cr_free(cr); - if (ret == -2) - goto unknown_property_name; - else - goto out_of_memory; - } - } - } else { - unknown_property_name: - return re_parse_error(s, "unknown unicode property name"); - } - - if (is_inv) { - if (cr_invert(cr)) { - cr_free(cr); - return -1; - } - } - *pp = p; - return 0; - out_of_memory: - return re_parse_out_of_memory(s); -} - -/* return -1 if error otherwise the character or a class range - (CLASS_RANGE_BASE). In case of class range, 'cr' is - initialized. Otherwise, it is ignored. */ -static int get_class_atom(REParseState *s, CharRange *cr, - const uint8_t **pp, bool inclass) -{ - const uint8_t *p, *p_next; - uint32_t c; - int ret; - - p = *pp; - - c = *p; - switch(c) { - case '\\': - p++; - if (p >= s->buf_end) - goto unexpected_end; - c = *p++; - switch(c) { - case 'd': - c = CHAR_RANGE_d; - goto class_range; - case 'D': - c = CHAR_RANGE_D; - goto class_range; - case 's': - c = CHAR_RANGE_s; - goto class_range; - case 'S': - c = CHAR_RANGE_S; - goto class_range; - case 'w': - c = CHAR_RANGE_w; - goto class_range; - case 'W': - c = CHAR_RANGE_W; - class_range: - if (cr_init_char_range(s, cr, c)) - return -1; - c = CLASS_RANGE_BASE; - break; - case 'c': - c = *p; - if ((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (((c >= '0' && c <= '9') || c == '_') && - inclass && !s->is_unicode)) { /* Annex B.1.4 */ - c &= 0x1f; - p++; - } else if (s->is_unicode) { - goto invalid_escape; - } else { - /* otherwise return '\' and 'c' */ - p--; - c = '\\'; - } - break; - case 'p': - case 'P': - if (s->is_unicode) { - if (parse_unicode_property(s, cr, &p, (c == 'P'))) - return -1; - c = CLASS_RANGE_BASE; - break; - } - /* fall thru */ - default: - p--; - ret = lre_parse_escape(&p, s->is_unicode * 2); - if (ret >= 0) { - c = ret; - } else { - if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) { - /* always valid to escape these characters */ - goto normal_char; - } else if (s->is_unicode) { - // special case: allowed inside [] but not outside - if (ret == -2 && *p == '-' && inclass) - goto normal_char; - invalid_escape: - return re_parse_error(s, "invalid escape sequence in regular expression"); - } else { - /* just ignore the '\' */ - goto normal_char; - } - } - break; - } - break; - case '\0': - if (p >= s->buf_end) { - unexpected_end: - return re_parse_error(s, "unexpected end"); - } - /* fall thru */ - default: - normal_char: - p++; - if (c >= 0x80) { - c = utf8_decode(p - 1, &p_next); - if (p_next == p) - return re_parse_error(s, "invalid UTF-8 sequence"); - p = p_next; - if (c > 0xFFFF && !s->is_unicode) { - // TODO(chqrlie): should handle non BMP-1 code points in - // the calling function and no require the source string - // to be CESU-8 encoded if not s->is_unicode - return re_parse_error(s, "malformed unicode char"); - } - } - break; - } - *pp = p; - return c; -} - -static int re_emit_range(REParseState *s, const CharRange *cr) -{ - int len, i; - uint32_t high; - - len = (unsigned)cr->len / 2; - if (len >= 65535) - return re_parse_error(s, "too many ranges"); - if (len == 0) { - /* not sure it can really happen. Emit a match that is always - false */ - re_emit_op_u32(s, REOP_char32, -1); - } else { - high = cr->points[cr->len - 1]; - if (high == UINT32_MAX) - high = cr->points[cr->len - 2]; - if (high <= 0xffff) { - /* can use 16 bit ranges with the conversion that 0xffff = - infinity */ - re_emit_op_u16(s, REOP_range, len); - for(i = 0; i < cr->len; i += 2) { - dbuf_put_u16(&s->byte_code, cr->points[i]); - high = cr->points[i + 1] - 1; - if (high == UINT32_MAX - 1) - high = 0xffff; - dbuf_put_u16(&s->byte_code, high); - } - } else { - re_emit_op_u16(s, REOP_range32, len); - for(i = 0; i < cr->len; i += 2) { - dbuf_put_u32(&s->byte_code, cr->points[i]); - dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); - } - } - } - return 0; -} - -// s->unicode turns patterns like []] into syntax errors -// s->unicode_sets turns more patterns into errors, like [a-] or [[] -static int re_parse_char_class(REParseState *s, const uint8_t **pp) -{ - const uint8_t *p; - uint32_t c1, c2; - CharRange cr_s, *cr = &cr_s; - CharRange cr1_s, *cr1 = &cr1_s; - bool invert; - - cr_init(cr, s->opaque, lre_realloc); - p = *pp; - p++; /* skip '[' */ - - if (s->unicode_sets) { - static const char verboten[] = - "()[{}/-|" "\0" - "&&!!##$$%%**++,,..::;;<<==>>??@@``~~" "\0" - "^^^_^^"; - const char *s = verboten; - int n = 1; - do { - if (!memcmp(s, p, n)) - if (p[n] == ']') - goto invalid_class_range; - s += n; - if (!*s) { - s++; - n++; - } - } while (n < 4); - } - - invert = false; - if (*p == '^') { - p++; - invert = true; - } - - for(;;) { - if (*p == ']') - break; - c1 = get_class_atom(s, cr1, &p, true); - if ((int)c1 < 0) - goto fail; - if (*p == '-' && p[1] == ']' && s->unicode_sets) { - if (c1 >= CLASS_RANGE_BASE) - cr_free(cr1); - goto invalid_class_range; - } - if (*p == '-' && p[1] != ']') { - const uint8_t *p0 = p + 1; - if (c1 >= CLASS_RANGE_BASE) { - if (s->is_unicode) { - cr_free(cr1); - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - c2 = get_class_atom(s, cr1, &p0, true); - if ((int)c2 < 0) - goto fail; - if (c2 >= CLASS_RANGE_BASE) { - cr_free(cr1); - if (s->is_unicode) { - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - p = p0; - if (c2 < c1) { - invalid_class_range: - re_parse_error(s, "invalid class range"); - goto fail; - } - if (cr_union_interval(cr, c1, c2)) - goto memory_error; - } else { - class_atom: - if (c1 >= CLASS_RANGE_BASE) { - int ret; - ret = cr_union1(cr, cr1->points, cr1->len); - cr_free(cr1); - if (ret) - goto memory_error; - } else { - if (cr_union_interval(cr, c1, c1)) - goto memory_error; - } - } - } - if (s->ignore_case) { - if (cr_regexp_canonicalize(cr, s->is_unicode)) - goto memory_error; - } - if (invert) { - if (cr_invert(cr)) - goto memory_error; - } - if (re_emit_range(s, cr)) - goto fail; - cr_free(cr); - p++; /* skip ']' */ - *pp = p; - return 0; - memory_error: - re_parse_out_of_memory(s); - fail: - cr_free(cr); - return -1; -} - -/* Return: - - true if the opcodes may not advance the char pointer - - false if the opcodes always advance the char pointer -*/ -static bool re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len) -{ - int pos, opcode, len; - uint32_t val; - bool ret; - - ret = true; - pos = 0; - - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - switch(opcode) { - case REOP_range: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - goto simple_char; - case REOP_range32: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - goto simple_char; - case REOP_char32: - case REOP_char16: - case REOP_char8: - case REOP_dot: - case REOP_any: - simple_char: - ret = false; - break; - case REOP_line_start: - case REOP_line_end: - case REOP_push_i32: - case REOP_push_char_pos: - case REOP_drop: - case REOP_word_boundary: - case REOP_not_word_boundary: - case REOP_prev: - /* no effect */ - break; - case REOP_save_start: - case REOP_save_end: - case REOP_save_reset: - case REOP_back_reference: - case REOP_backward_back_reference: - break; - default: - /* safe behvior: we cannot predict the outcome */ - return true; - } - pos += len; - } - return ret; -} - -/* return -1 if a simple quantifier cannot be used. Otherwise return - the number of characters in the atom. */ -static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) -{ - int pos, opcode, len, count; - uint32_t val; - - count = 0; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - switch(opcode) { - case REOP_range: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - goto simple_char; - case REOP_range32: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - goto simple_char; - case REOP_char32: - case REOP_char16: - case REOP_char8: - case REOP_dot: - case REOP_any: - simple_char: - count++; - break; - case REOP_line_start: - case REOP_line_end: - case REOP_word_boundary: - case REOP_not_word_boundary: - break; - default: - return -1; - } - pos += len; - } - return count; -} - -/* '*pp' is the first char after '<' */ -static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) -{ - const uint8_t *p, *p_next; - uint32_t c, d; - char *q; - - p = *pp; - q = buf; - for(;;) { - c = *p++; - if (c == '\\') { - if (*p != 'u') - return -1; - c = lre_parse_escape(&p, 2); // accept surrogate pairs - if ((int)c < 0) - return -1; - } else if (c == '>') { - break; - } else if (c >= 0x80) { - c = utf8_decode(p - 1, &p_next); - if (p_next == p) - return -1; - p = p_next; - if (is_hi_surrogate(c)) { - d = utf8_decode(p, &p_next); - if (is_lo_surrogate(d)) { - c = from_surrogate(c, d); - p = p_next; - } - } - } - if (q == buf) { - if (!lre_js_is_ident_first(c)) - return -1; - } else { - if (!lre_js_is_ident_next(c)) - return -1; - } - if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size) - return -1; - if (c < 0x80) { - *q++ = c; - } else { - q += utf8_encode((uint8_t*)q, c); - } - } - if (q == buf) - return -1; - *q = '\0'; - *pp = p; - return 0; -} - -/* if capture_name = NULL: return the number of captures + 1. - Otherwise, return the capture index corresponding to capture_name - or -1 if none */ -static int re_parse_captures(REParseState *s, int *phas_named_captures, - const char *capture_name) -{ - const uint8_t *p; - int capture_index; - char name[TMP_BUF_SIZE]; - - capture_index = 1; - *phas_named_captures = 0; - for (p = s->buf_start; p < s->buf_end; p++) { - switch (*p) { - case '(': - if (p[1] == '?') { - if (p[2] == '<' && p[3] != '=' && p[3] != '!') { - *phas_named_captures = 1; - /* potential named capture */ - if (capture_name) { - p += 3; - if (re_parse_group_name(name, sizeof(name), &p) == 0) { - if (!strcmp(name, capture_name)) - return capture_index; - } - } - capture_index++; - if (capture_index >= CAPTURE_COUNT_MAX) - goto done; - } - } else { - capture_index++; - if (capture_index >= CAPTURE_COUNT_MAX) - goto done; - } - break; - case '\\': - p++; - break; - case '[': - for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) { - if (*p == '\\') - p++; - } - break; - } - } - done: - if (capture_name) - return -1; - else - return capture_index; -} - -static int re_count_captures(REParseState *s) -{ - if (s->total_capture_count < 0) { - s->total_capture_count = re_parse_captures(s, &s->has_named_captures, - NULL); - } - return s->total_capture_count; -} - -static bool re_has_named_captures(REParseState *s) -{ - if (s->has_named_captures < 0) - re_count_captures(s); - return s->has_named_captures; -} - -static int find_group_name(REParseState *s, const char *name) -{ - const char *p, *buf_end; - size_t len, name_len; - int capture_index; - - p = (char *)s->group_names.buf; - if (!p) return -1; - buf_end = (char *)s->group_names.buf + s->group_names.size; - name_len = strlen(name); - capture_index = 1; - while (p < buf_end) { - len = strlen(p); - if (len == name_len && memcmp(name, p, name_len) == 0) - return capture_index; - p += len + 1; - capture_index++; - } - return -1; -} - -static int re_parse_disjunction(REParseState *s, bool is_backward_dir); - -static int re_parse_term(REParseState *s, bool is_backward_dir) -{ - const uint8_t *p; - int c, last_atom_start, quant_min, quant_max, last_capture_count; - bool greedy, add_zero_advance_check, is_neg, is_backward_lookahead; - CharRange cr_s, *cr = &cr_s; - - if (lre_check_size(s)) - return -1; - last_atom_start = -1; - last_capture_count = 0; - p = s->buf_ptr; - c = *p; - switch(c) { - case '^': - p++; - re_emit_op(s, REOP_line_start); - break; - case '$': - p++; - re_emit_op(s, REOP_line_end); - break; - case '.': - p++; - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - re_emit_op(s, s->dotall ? REOP_any : REOP_dot); - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - case '{': - if (s->is_unicode) { - return re_parse_error(s, "syntax error"); - } else if (!lre_is_digit(p[1])) { - /* Annex B: we accept '{' not followed by digits as a - normal atom */ - goto parse_class_atom; - } else { - const uint8_t *p1 = p + 1; - /* Annex B: error if it is like a repetition count */ - parse_digits(&p1, true); - if (*p1 == ',') { - p1++; - if (lre_is_digit(*p1)) { - parse_digits(&p1, true); - } - } - if (*p1 != '}') { - goto parse_class_atom; - } - } - /* fall thru */ - case '*': - case '+': - case '?': - return re_parse_error(s, "nothing to repeat"); - case '(': - if (p[1] == '?') { - if (p[2] == ':') { - p += 3; - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - } else if ((p[2] == '=' || p[2] == '!')) { - is_neg = (p[2] == '!'); - is_backward_lookahead = false; - p += 3; - goto lookahead; - } else if (p[2] == '<' && - (p[3] == '=' || p[3] == '!')) { - int pos; - is_neg = (p[3] == '!'); - is_backward_lookahead = true; - p += 4; - /* lookahead */ - lookahead: - /* Annex B allows lookahead to be used as an atom for - the quantifiers */ - if (!s->is_unicode && !is_backward_lookahead) { - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - } - pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_lookahead)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - re_emit_op(s, REOP_match); - /* jump after the 'match' after the lookahead is successful */ - if (dbuf_error(&s->byte_code)) - return -1; - put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); - } else if (p[2] == '<') { - p += 3; - if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p)) { - return re_parse_error(s, "invalid group name"); - } - if (find_group_name(s, s->u.tmp_buf) > 0) { - return re_parse_error(s, "duplicate group name"); - } - /* group name with a trailing zero */ - dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf, - strlen(s->u.tmp_buf) + 1); - s->has_named_captures = 1; - goto parse_capture; - } else { - return re_parse_error(s, "invalid group"); - } - } else { - int capture_index; - p++; - /* capture without group name */ - dbuf_putc(&s->group_names, 0); - parse_capture: - if (s->capture_count >= CAPTURE_COUNT_MAX) - return re_parse_error(s, "too many captures"); - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - capture_index = s->capture_count++; - re_emit_op_u8(s, REOP_save_start + is_backward_dir, - capture_index); - - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - - re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, - capture_index); - - if (re_parse_expect(s, &p, ')')) - return -1; - } - break; - case '\\': - switch(p[1]) { - case 'b': - case 'B': - re_emit_op(s, REOP_word_boundary + (p[1] != 'b')); - p += 2; - break; - case 'k': - { - const uint8_t *p1; - int dummy_res; - - p1 = p; - if (p1[2] != '<') { - /* annex B: we tolerate invalid group names in non - unicode mode if there is no named capture - definition */ - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "expecting group name"); - else - goto parse_class_atom; - } - p1 += 3; - if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p1)) { - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "invalid group name"); - else - goto parse_class_atom; - } - c = find_group_name(s, s->u.tmp_buf); - if (c < 0) { - /* no capture name parsed before, try to look - after (inefficient, but hopefully not common */ - c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); - if (c < 0) { - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "group name not defined"); - else - goto parse_class_atom; - } - } - p = p1; - } - goto emit_back_reference; - case '0': - p += 2; - c = 0; - if (s->is_unicode) { - if (lre_is_digit(*p)) { - return re_parse_error(s, "invalid decimal escape in regular expression"); - } - } else { - /* Annex B.1.4: accept legacy octal */ - if (*p >= '0' && *p <= '7') { - c = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - } - } - } - goto normal_char; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': - case '9': - { - const uint8_t *q = ++p; - - c = parse_digits(&p, false); - if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { - if (!s->is_unicode) { - /* Annex B.1.4: accept legacy octal */ - p = q; - if (*p <= '7') { - c = 0; - if (*p <= '3') - c = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - } - } - } else { - c = *p++; - } - goto normal_char; - } - return re_parse_error(s, "back reference out of range in regular expression"); - } - emit_back_reference: - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c); - } - break; - default: - goto parse_class_atom; - } - break; - case '[': - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - if (re_parse_char_class(s, &p)) - return -1; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - case ']': - case '}': - if (s->is_unicode) - return re_parse_error(s, "syntax error"); - goto parse_class_atom; - default: - parse_class_atom: - c = get_class_atom(s, cr, &p, false); - if ((int)c < 0) - return -1; - normal_char: - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - if (c >= CLASS_RANGE_BASE) { - int ret; - /* Note: canonicalization is not needed */ - ret = re_emit_range(s, cr); - cr_free(cr); - if (ret) - return -1; - } else { - if (s->ignore_case) - c = lre_canonicalize(c, s->is_unicode); - if (c <= 0x7f) - re_emit_op_u8(s, REOP_char8, c); - else if (c <= 0xffff) - re_emit_op_u16(s, REOP_char16, c); - else - re_emit_op_u32(s, REOP_char32, c); - } - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - } - - /* quantifier */ - if (last_atom_start >= 0) { - c = *p; - switch(c) { - case '*': - p++; - quant_min = 0; - quant_max = INT32_MAX; - goto quantifier; - case '+': - p++; - quant_min = 1; - quant_max = INT32_MAX; - goto quantifier; - case '?': - p++; - quant_min = 0; - quant_max = 1; - goto quantifier; - case '{': - { - const uint8_t *p1 = p; - /* As an extension (see ES6 annex B), we accept '{' not - followed by digits as a normal atom */ - if (!lre_is_digit(p[1])) { - if (s->is_unicode) - goto invalid_quant_count; - break; - } - p++; - quant_min = parse_digits(&p, true); - quant_max = quant_min; - if (*p == ',') { - p++; - if (lre_is_digit(*p)) { - quant_max = parse_digits(&p, true); - if (quant_max < quant_min) { - invalid_quant_count: - return re_parse_error(s, "invalid repetition count"); - } - } else { - quant_max = INT32_MAX; /* infinity */ - } - } - if (*p != '}' && !s->is_unicode) { - /* Annex B: normal atom if invalid '{' syntax */ - p = p1; - break; - } - if (re_parse_expect(s, &p, '}')) - return -1; - } - quantifier: - greedy = true; - if (*p == '?') { - p++; - greedy = false; - } - if (last_atom_start < 0) { - return re_parse_error(s, "nothing to repeat"); - } - if (greedy) { - int len, pos; - - if (quant_max > 0) { - /* specific optimization for simple quantifiers */ - if (dbuf_error(&s->byte_code)) - goto out_of_memory; - len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start, - s->byte_code.size - last_atom_start); - if (len > 0) { - re_emit_op(s, REOP_match); - - if (dbuf_insert(&s->byte_code, last_atom_start, 17)) - goto out_of_memory; - pos = last_atom_start; - s->byte_code.buf[pos++] = REOP_simple_greedy_quant; - put_u32(&s->byte_code.buf[pos], - s->byte_code.size - last_atom_start - 17); - pos += 4; - put_u32(&s->byte_code.buf[pos], quant_min); - pos += 4; - put_u32(&s->byte_code.buf[pos], quant_max); - pos += 4; - put_u32(&s->byte_code.buf[pos], len); - pos += 4; - goto done; - } - } - - if (dbuf_error(&s->byte_code)) - goto out_of_memory; - } - /* the spec tells that if there is no advance when - running the atom after the first quant_min times, - then there is no match. We remove this test when we - are sure the atom always advances the position. */ - add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, - s->byte_code.size - last_atom_start); - - { - int len, pos; - len = s->byte_code.size - last_atom_start; - if (quant_min == 0) { - /* need to reset the capture in case the atom is - not executed */ - if (last_capture_count != s->capture_count) { - if (dbuf_insert(&s->byte_code, last_atom_start, 3)) - goto out_of_memory; - s->byte_code.buf[last_atom_start++] = REOP_save_reset; - s->byte_code.buf[last_atom_start++] = last_capture_count; - s->byte_code.buf[last_atom_start++] = s->capture_count - 1; - } - if (quant_max == 0) { - s->byte_code.size = last_atom_start; - } else if (quant_max == 1 || quant_max == INT32_MAX) { - bool has_goto = (quant_max == INT32_MAX); - if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check)) - goto out_of_memory; - s->byte_code.buf[last_atom_start] = REOP_split_goto_first + - greedy; - put_u32(s->byte_code.buf + last_atom_start + 1, - len + 5 * has_goto + add_zero_advance_check * 2); - if (add_zero_advance_check) { - s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; - re_emit_op(s, REOP_check_advance); - } - if (has_goto) - re_emit_goto(s, REOP_goto, last_atom_start); - } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 10 + add_zero_advance_check)) - goto out_of_memory; - pos = last_atom_start; - s->byte_code.buf[pos++] = REOP_push_i32; - put_u32(s->byte_code.buf + pos, quant_max); - pos += 4; - s->byte_code.buf[pos++] = REOP_split_goto_first + greedy; - put_u32(s->byte_code.buf + pos, len + 5 + add_zero_advance_check * 2); - pos += 4; - if (add_zero_advance_check) { - s->byte_code.buf[pos++] = REOP_push_char_pos; - re_emit_op(s, REOP_check_advance); - } - re_emit_goto(s, REOP_loop, last_atom_start + 5); - re_emit_op(s, REOP_drop); - } - } else if (quant_min == 1 && quant_max == INT32_MAX && - !add_zero_advance_check) { - re_emit_goto(s, REOP_split_next_first - greedy, - last_atom_start); - } else { - if (quant_min == 1) { - /* nothing to add */ - } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 5)) - goto out_of_memory; - s->byte_code.buf[last_atom_start] = REOP_push_i32; - put_u32(s->byte_code.buf + last_atom_start + 1, - quant_min); - last_atom_start += 5; - re_emit_goto(s, REOP_loop, last_atom_start); - re_emit_op(s, REOP_drop); - } - if (quant_max == INT32_MAX) { - pos = s->byte_code.size; - re_emit_op_u32(s, REOP_split_goto_first + greedy, - len + 5 + add_zero_advance_check * 2); - if (add_zero_advance_check) - re_emit_op(s, REOP_push_char_pos); - /* copy the atom */ - dbuf_put_self(&s->byte_code, last_atom_start, len); - if (add_zero_advance_check) - re_emit_op(s, REOP_check_advance); - re_emit_goto(s, REOP_goto, pos); - } else if (quant_max > quant_min) { - re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min); - pos = s->byte_code.size; - re_emit_op_u32(s, REOP_split_goto_first + greedy, - len + 5 + add_zero_advance_check * 2); - if (add_zero_advance_check) - re_emit_op(s, REOP_push_char_pos); - /* copy the atom */ - dbuf_put_self(&s->byte_code, last_atom_start, len); - if (add_zero_advance_check) - re_emit_op(s, REOP_check_advance); - re_emit_goto(s, REOP_loop, pos); - re_emit_op(s, REOP_drop); - } - } - last_atom_start = -1; - } - break; - default: - break; - } - } - done: - s->buf_ptr = p; - return 0; - out_of_memory: - return re_parse_out_of_memory(s); -} - -static int re_parse_alternative(REParseState *s, bool is_backward_dir) -{ - const uint8_t *p; - int ret; - size_t start, term_start, end, term_size; - - if (lre_check_size(s)) - return -1; - start = s->byte_code.size; - for(;;) { - p = s->buf_ptr; - if (p >= s->buf_end) - break; - if (*p == '|' || *p == ')') - break; - term_start = s->byte_code.size; - ret = re_parse_term(s, is_backward_dir); - if (ret) - return ret; - if (is_backward_dir) { - /* reverse the order of the terms (XXX: inefficient, but - speed is not really critical here) */ - end = s->byte_code.size; - term_size = end - term_start; - if (dbuf_realloc(&s->byte_code, end + term_size)) - return -1; - memmove(s->byte_code.buf + start + term_size, - s->byte_code.buf + start, - end - start); - memcpy(s->byte_code.buf + start, s->byte_code.buf + end, - term_size); - } - } - return 0; -} - -static int re_parse_disjunction(REParseState *s, bool is_backward_dir) -{ - int start, len, pos; - - if (lre_check_stack_overflow(s->opaque, 0)) - return re_parse_error(s, "stack overflow"); - if (lre_check_size(s)) - return -1; - start = s->byte_code.size; - if (re_parse_alternative(s, is_backward_dir)) - return -1; - while (*s->buf_ptr == '|') { - s->buf_ptr++; - - len = s->byte_code.size - start; - - /* insert a split before the first alternative */ - if (dbuf_insert(&s->byte_code, start, 5)) { - return re_parse_out_of_memory(s); - } - s->byte_code.buf[start] = REOP_split_next_first; - put_u32(s->byte_code.buf + start + 1, len + 5); - - pos = re_emit_op_u32(s, REOP_goto, 0); - - if (re_parse_alternative(s, is_backward_dir)) - return -1; - - /* patch the goto */ - len = s->byte_code.size - (pos + 4); - put_u32(s->byte_code.buf + pos, len); - } - return 0; -} - -/* the control flow is recursive so the analysis can be linear */ -static int lre_compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) -{ - int stack_size, stack_size_max, pos, opcode, len; - uint32_t val; - - stack_size = 0; - stack_size_max = 0; - bc_buf += RE_HEADER_LEN; - bc_buf_len -= RE_HEADER_LEN; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - assert(opcode < REOP_COUNT); - assert((pos + len) <= bc_buf_len); - switch(opcode) { - case REOP_push_i32: - case REOP_push_char_pos: - stack_size++; - if (stack_size > stack_size_max) { - if (stack_size > STACK_SIZE_MAX) - return -1; - stack_size_max = stack_size; - } - break; - case REOP_drop: - case REOP_check_advance: - assert(stack_size > 0); - stack_size--; - break; - case REOP_range: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - break; - case REOP_range32: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - break; - } - pos += len; - } - return stack_size_max; -} - -/* 'buf' must be a zero terminated UTF-8 string of length buf_len. - Return NULL if error and allocate an error message in *perror_msg, - otherwise the compiled bytecode and its length in plen. -*/ -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) -{ - REParseState s_s, *s = &s_s; - int stack_size; - bool is_sticky; - - memset(s, 0, sizeof(*s)); - s->opaque = opaque; - s->buf_ptr = (const uint8_t *)buf; - s->buf_end = s->buf_ptr + buf_len; - s->buf_start = s->buf_ptr; - s->re_flags = re_flags; - s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0); - is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); - s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); - s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); - s->unicode_sets = ((re_flags & LRE_FLAG_UNICODE_SETS) != 0); - s->capture_count = 1; - s->total_capture_count = -1; - s->has_named_captures = -1; - - dbuf_init2(&s->byte_code, opaque, lre_realloc); - dbuf_init2(&s->group_names, opaque, lre_realloc); - - dbuf_put_u16(&s->byte_code, re_flags); /* first element is the flags */ - dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ - dbuf_putc(&s->byte_code, 0); /* stack size */ - dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ - - if (!is_sticky) { - /* iterate thru all positions (about the same as .*?( ... ) ) - . We do it without an explicit loop so that lock step - thread execution will be possible in an optimized - implementation */ - re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); - re_emit_op(s, REOP_any); - re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); - } - re_emit_op_u8(s, REOP_save_start, 0); - - if (re_parse_disjunction(s, false)) { - error: - dbuf_free(&s->byte_code); - dbuf_free(&s->group_names); - js__pstrcpy(error_msg, error_msg_size, s->u.error_msg); - *plen = 0; - return NULL; - } - - re_emit_op_u8(s, REOP_save_end, 0); - - re_emit_op(s, REOP_match); - - if (*s->buf_ptr != '\0') { - re_parse_error(s, "extraneous characters at the end"); - goto error; - } - - if (dbuf_error(&s->byte_code)) { - re_parse_out_of_memory(s); - goto error; - } - - stack_size = lre_compute_stack_size(s->byte_code.buf, s->byte_code.size); - if (stack_size < 0) { - re_parse_error(s, "too many imbricated quantifiers"); - goto error; - } - - s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; - s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; - put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, - s->byte_code.size - RE_HEADER_LEN); - - /* add the named groups if needed */ - if (s->group_names.size > (s->capture_count - 1)) { - dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size); - put_u16(s->byte_code.buf + RE_HEADER_FLAGS, - LRE_FLAG_NAMED_GROUPS | lre_get_flags(s->byte_code.buf)); - } - dbuf_free(&s->group_names); - -#ifdef DUMP_REOP - lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); -#endif - - error_msg[0] = '\0'; - *plen = s->byte_code.size; - return s->byte_code.buf; -} - -static bool is_line_terminator(uint32_t c) -{ - return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); -} - -static bool is_word_char(uint32_t c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c == '_')); -} - -#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = *cptr++; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr; \ - const uint16_t *_end = (const uint16_t *)cbuf_end; \ - c = *_p++; \ - if (is_hi_surrogate(c)) \ - if (cbuf_type == 2) \ - if (_p < _end) \ - if (is_lo_surrogate(*_p)) \ - c = from_surrogate(c, *_p++); \ - cptr = (const void *)_p; \ - } \ - } while (0) - -#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[0]; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr; \ - const uint16_t *_end = (const uint16_t *)cbuf_end; \ - c = *_p++; \ - if (is_hi_surrogate(c)) \ - if (cbuf_type == 2) \ - if (_p < _end) \ - if (is_lo_surrogate(*_p)) \ - c = from_surrogate(c, *_p); \ - } \ - } while (0) - -#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[-1]; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr - 1; \ - const uint16_t *_start = (const uint16_t *)cbuf_start; \ - c = *_p; \ - if (is_lo_surrogate(c)) \ - if (cbuf_type == 2) \ - if (_p > _start) \ - if (is_hi_surrogate(_p[-1])) \ - c = from_surrogate(*--_p, c); \ - } \ - } while (0) - -#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - c = cptr[0]; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr - 1; \ - const uint16_t *_start = (const uint16_t *)cbuf_start; \ - c = *_p; \ - if (is_lo_surrogate(c)) \ - if (cbuf_type == 2) \ - if (_p > _start) \ - if (is_hi_surrogate(_p[-1])) \ - c = from_surrogate(*--_p, c); \ - cptr = (const void *)_p; \ - } \ - } while (0) - -#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - } else { \ - const uint16_t *_p = (const uint16_t *)cptr - 1; \ - const uint16_t *_start = (const uint16_t *)cbuf_start; \ - if (is_lo_surrogate(*_p)) \ - if (cbuf_type == 2) \ - if (_p > _start) \ - if (is_hi_surrogate(_p[-1])) \ - _p--; \ - cptr = (const void *)_p; \ - } \ - } while (0) - -typedef uintptr_t StackInt; - -typedef enum { - RE_EXEC_STATE_SPLIT, - RE_EXEC_STATE_LOOKAHEAD, - RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, - RE_EXEC_STATE_GREEDY_QUANT, -} REExecStateEnum; - -typedef struct REExecState { - REExecStateEnum type : 8; - uint8_t stack_len; - size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */ - const uint8_t *cptr; - const uint8_t *pc; - void *buf[]; -} REExecState; - -typedef struct { - const uint8_t *cbuf; - const uint8_t *cbuf_end; - /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ - int cbuf_type; - int capture_count; - int stack_size_max; - bool multi_line; - bool ignore_case; - bool is_unicode; - int interrupt_counter; - void *opaque; /* used for stack overflow check */ - - size_t state_size; - uint8_t *state_stack; - size_t state_stack_size; - size_t state_stack_len; -} REExecContext; - -static int push_state(REExecContext *s, - uint8_t **capture, - StackInt *stack, size_t stack_len, - const uint8_t *pc, const uint8_t *cptr, - REExecStateEnum type, size_t count) -{ - REExecState *rs; - uint8_t *new_stack; - size_t new_size, i, n; - StackInt *stack_buf; - - if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) { - /* reallocate the stack */ - new_size = s->state_stack_size * 3 / 2; - if (new_size < 8) - new_size = 8; - new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size); - if (!new_stack) - return -1; - s->state_stack_size = new_size; - s->state_stack = new_stack; - } - rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size); - s->state_stack_len++; - rs->type = type; - rs->count = count; - rs->stack_len = stack_len; - rs->cptr = cptr; - rs->pc = pc; - n = 2 * s->capture_count; - for(i = 0; i < n; i++) - rs->buf[i] = capture[i]; - stack_buf = (StackInt *)(rs->buf + n); - for(i = 0; i < stack_len; i++) - stack_buf[i] = stack[i]; - return 0; -} - -static int lre_poll_timeout(REExecContext *s) -{ - if (unlikely(--s->interrupt_counter <= 0)) { - s->interrupt_counter = INTERRUPT_COUNTER_INIT; - if (lre_check_timeout(s->opaque)) - return LRE_RET_TIMEOUT; - } - return 0; -} - -/* return 1 if match, 0 if not match or < 0 if error. */ -static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, - StackInt *stack, int stack_len, - const uint8_t *pc, const uint8_t *cptr, - bool no_recurse) -{ - int opcode, ret; - int cbuf_type; - uint32_t val, c; - const uint8_t *cbuf_end; - - cbuf_type = s->cbuf_type; - cbuf_end = s->cbuf_end; - - for(;;) { - // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN))); - opcode = *pc++; - switch(opcode) { - case REOP_match: - { - REExecState *rs; - if (no_recurse) - return (intptr_t)cptr; - ret = 1; - goto recurse; - no_match: - if (no_recurse) - return 0; - ret = 0; - recurse: - for(;;) { - if (lre_poll_timeout(s)) - return LRE_RET_TIMEOUT; - if (s->state_stack_len == 0) - return ret; - rs = (REExecState *)(s->state_stack + - (s->state_stack_len - 1) * s->state_size); - if (rs->type == RE_EXEC_STATE_SPLIT) { - if (!ret) { - pop_state: - memcpy(capture, rs->buf, - sizeof(capture[0]) * 2 * s->capture_count); - pop_state1: - pc = rs->pc; - cptr = rs->cptr; - stack_len = rs->stack_len; - memcpy(stack, rs->buf + 2 * s->capture_count, - stack_len * sizeof(stack[0])); - s->state_stack_len--; - break; - } - } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) { - if (!ret) { - uint32_t char_count, i; - memcpy(capture, rs->buf, - sizeof(capture[0]) * 2 * s->capture_count); - stack_len = rs->stack_len; - memcpy(stack, rs->buf + 2 * s->capture_count, - stack_len * sizeof(stack[0])); - pc = rs->pc; - cptr = rs->cptr; - /* go backward */ - char_count = get_u32(pc + 12); - for(i = 0; i < char_count; i++) { - PREV_CHAR(cptr, s->cbuf, cbuf_type); - } - pc = (pc + 16) + (int)get_u32(pc); - rs->cptr = cptr; - rs->count--; - if (rs->count == 0) { - s->state_stack_len--; - } - break; - } - } else { - ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) || - (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret)); - if (ret) { - /* keep the capture in case of positive lookahead */ - if (rs->type == RE_EXEC_STATE_LOOKAHEAD) - goto pop_state1; - else - goto pop_state; - } - } - s->state_stack_len--; - } - } - break; - case REOP_char32: - val = get_u32(pc); - pc += 4; - goto test_char; - case REOP_char16: - val = get_u16(pc); - pc += 2; - goto test_char; - case REOP_char8: - val = get_u8(pc); - pc += 1; - test_char: - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_unicode); - } - if (val != c) - goto no_match; - break; - case REOP_split_goto_first: - case REOP_split_next_first: - { - const uint8_t *pc1; - - val = get_u32(pc); - pc += 4; - if (opcode == REOP_split_next_first) { - pc1 = pc + (int)val; - } else { - pc1 = pc; - pc = pc + (int)val; - } - ret = push_state(s, capture, stack, stack_len, - pc1, cptr, RE_EXEC_STATE_SPLIT, 0); - if (ret < 0) - return LRE_RET_MEMORY_ERROR; - break; - } - case REOP_lookahead: - case REOP_negative_lookahead: - val = get_u32(pc); - pc += 4; - ret = push_state(s, capture, stack, stack_len, - pc + (int)val, cptr, - RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, - 0); - if (ret < 0) - return LRE_RET_MEMORY_ERROR; - break; - - case REOP_goto: - val = get_u32(pc); - pc += 4 + (int)val; - if (lre_poll_timeout(s)) - return LRE_RET_TIMEOUT; - break; - case REOP_line_start: - if (cptr == s->cbuf) - break; - if (!s->multi_line) - goto no_match; - PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); - if (!is_line_terminator(c)) - goto no_match; - break; - case REOP_line_end: - if (cptr == cbuf_end) - break; - if (!s->multi_line) - goto no_match; - PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); - if (!is_line_terminator(c)) - goto no_match; - break; - case REOP_dot: - if (cptr == cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (is_line_terminator(c)) - goto no_match; - break; - case REOP_any: - if (cptr == cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - break; - case REOP_save_start: - case REOP_save_end: - val = *pc++; - assert(val < s->capture_count); - capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr; - break; - case REOP_save_reset: - { - uint32_t val2; - val = pc[0]; - val2 = pc[1]; - pc += 2; - assert(val2 < s->capture_count); - while (val <= val2) { - capture[2 * val] = NULL; - capture[2 * val + 1] = NULL; - val++; - } - } - break; - case REOP_push_i32: - val = get_u32(pc); - pc += 4; - stack[stack_len++] = val; - break; - case REOP_drop: - stack_len--; - break; - case REOP_loop: - val = get_u32(pc); - pc += 4; - if (--stack[stack_len - 1] != 0) { - pc += (int)val; - if (lre_poll_timeout(s)) - return LRE_RET_TIMEOUT; - } - break; - case REOP_push_char_pos: - stack[stack_len++] = (uintptr_t)cptr; - break; - case REOP_check_advance: - if (stack[--stack_len] == (uintptr_t)cptr) - goto no_match; - break; - case REOP_word_boundary: - case REOP_not_word_boundary: - { - bool v1, v2; - /* char before */ - if (cptr == s->cbuf) { - v1 = false; - } else { - PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); - v1 = is_word_char(c); - } - /* current char */ - if (cptr >= cbuf_end) { - v2 = false; - } else { - PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); - v2 = is_word_char(c); - } - if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) - goto no_match; - } - break; - case REOP_back_reference: - case REOP_backward_back_reference: - { - const uint8_t *cptr1, *cptr1_end, *cptr1_start; - uint32_t c1, c2; - - val = *pc++; - if (val >= s->capture_count) - goto no_match; - cptr1_start = capture[2 * val]; - cptr1_end = capture[2 * val + 1]; - if (!cptr1_start || !cptr1_end) - break; - if (opcode == REOP_back_reference) { - cptr1 = cptr1_start; - while (cptr1 < cptr1_end) { - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); - GET_CHAR(c2, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_unicode); - c2 = lre_canonicalize(c2, s->is_unicode); - } - if (c1 != c2) - goto no_match; - } - } else { - cptr1 = cptr1_end; - while (cptr1 > cptr1_start) { - if (cptr == s->cbuf) - goto no_match; - GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); - GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); - if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_unicode); - c2 = lre_canonicalize(c2, s->is_unicode); - } - if (c1 != c2) - goto no_match; - } - } - } - break; - case REOP_range: - { - int n; - uint32_t low, high, idx_min, idx_max, idx; - - n = get_u16(pc); /* n must be >= 1 */ - pc += 2; - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_unicode); - } - idx_min = 0; - low = get_u16(pc + 0 * 4); - if (c < low) - goto no_match; - idx_max = n - 1; - high = get_u16(pc + idx_max * 4 + 2); - /* 0xffff in for last value means +infinity */ - if (unlikely(c >= 0xffff) && high == 0xffff) - goto range_match; - if (c > high) - goto no_match; - while (idx_min <= idx_max) { - idx = (idx_min + idx_max) / 2; - low = get_u16(pc + idx * 4); - high = get_u16(pc + idx * 4 + 2); - if (c < low) - idx_max = idx - 1; - else if (c > high) - idx_min = idx + 1; - else - goto range_match; - } - goto no_match; - range_match: - pc += 4 * n; - } - break; - case REOP_range32: - { - int n; - uint32_t low, high, idx_min, idx_max, idx; - - n = get_u16(pc); /* n must be >= 1 */ - pc += 2; - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_unicode); - } - idx_min = 0; - low = get_u32(pc + 0 * 8); - if (c < low) - goto no_match; - idx_max = n - 1; - high = get_u32(pc + idx_max * 8 + 4); - if (c > high) - goto no_match; - while (idx_min <= idx_max) { - idx = (idx_min + idx_max) / 2; - low = get_u32(pc + idx * 8); - high = get_u32(pc + idx * 8 + 4); - if (c < low) - idx_max = idx - 1; - else if (c > high) - idx_min = idx + 1; - else - goto range32_match; - } - goto no_match; - range32_match: - pc += 8 * n; - } - break; - case REOP_prev: - /* go to the previous char */ - if (cptr == s->cbuf) - goto no_match; - PREV_CHAR(cptr, s->cbuf, cbuf_type); - break; - case REOP_simple_greedy_quant: - { - uint32_t next_pos, quant_min, quant_max; - size_t q; - intptr_t res; - const uint8_t *pc1; - - next_pos = get_u32(pc); - quant_min = get_u32(pc + 4); - quant_max = get_u32(pc + 8); - pc += 16; - pc1 = pc; - pc += (int)next_pos; - - q = 0; - for(;;) { - if (lre_poll_timeout(s)) - return LRE_RET_TIMEOUT; - res = lre_exec_backtrack(s, capture, stack, stack_len, - pc1, cptr, true); - if (res == LRE_RET_MEMORY_ERROR || - res == LRE_RET_TIMEOUT) - return res; - if (!res) - break; - cptr = (uint8_t *)res; - q++; - if (q >= quant_max && quant_max != INT32_MAX) - break; - } - if (q < quant_min) - goto no_match; - if (q > quant_min) { - /* will examine all matches down to quant_min */ - ret = push_state(s, capture, stack, stack_len, - pc1 - 16, cptr, - RE_EXEC_STATE_GREEDY_QUANT, - q - quant_min); - if (ret < 0) - return LRE_RET_MEMORY_ERROR; - } - } - break; - default: - abort(); - } - } -} - -/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the - starting position of the match and must be such as 0 <= cindex <= - clen. */ -int lre_exec(uint8_t **capture, - const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, - int cbuf_type, void *opaque) -{ - REExecContext s_s, *s = &s_s; - int re_flags, i, alloca_size, ret; - StackInt *stack_buf; - - re_flags = lre_get_flags(bc_buf); - s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; - s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; - s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 0; - s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; - s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; - s->cbuf = cbuf; - s->cbuf_end = cbuf + (clen << cbuf_type); - s->cbuf_type = cbuf_type; - if (s->cbuf_type == 1 && s->is_unicode) - s->cbuf_type = 2; - s->interrupt_counter = INTERRUPT_COUNTER_INIT; - s->opaque = opaque; - - s->state_size = sizeof(REExecState) + - s->capture_count * sizeof(capture[0]) * 2 + - s->stack_size_max * sizeof(stack_buf[0]); - s->state_stack = NULL; - s->state_stack_len = 0; - s->state_stack_size = 0; - - for(i = 0; i < s->capture_count * 2; i++) - capture[i] = NULL; - alloca_size = s->stack_size_max * sizeof(stack_buf[0]); - stack_buf = alloca(alloca_size); - ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN, - cbuf + (cindex << cbuf_type), false); - lre_realloc(s->opaque, s->state_stack, 0); - return ret; -} - -int lre_get_capture_count(const uint8_t *bc_buf) -{ - return bc_buf[RE_HEADER_CAPTURE_COUNT]; -} - -int lre_get_flags(const uint8_t *bc_buf) -{ - return get_u16(bc_buf + RE_HEADER_FLAGS); -} - -/* Return NULL if no group names. Otherwise, return a pointer to - 'capture_count - 1' zero terminated UTF-8 strings. */ -const char *lre_get_groupnames(const uint8_t *bc_buf) -{ - uint32_t re_bytecode_len; - if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) - return NULL; - re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN); - return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); -} - -void lre_byte_swap(uint8_t *buf, size_t len, bool is_byte_swapped) -{ - uint8_t *p, *pe; - uint32_t n, r, nw; - - p = buf; - if (len < RE_HEADER_LEN) - abort(); - - // format is: - //
- // - // - // - // etc. - inplace_bswap16(&p[RE_HEADER_FLAGS]); - - n = get_u32(&p[RE_HEADER_BYTECODE_LEN]); - inplace_bswap32(&p[RE_HEADER_BYTECODE_LEN]); - if (is_byte_swapped) - n = bswap32(n); - if (n > len - RE_HEADER_LEN) - abort(); - - p = &buf[RE_HEADER_LEN]; - pe = &p[n]; - - while (p < pe) { - n = reopcode_info[*p].size; - switch (n) { - case 1: - case 2: - break; - case 3: - switch (*p) { - case REOP_save_reset: // has two 8 bit arguments - break; - case REOP_range32: // variable length - nw = get_u16(&p[1]); // number of pairs of uint32_t - if (is_byte_swapped) - n = bswap16(n); - for (r = 3 + 8 * nw; n < r; n += 4) - inplace_bswap32(&p[n]); - goto doswap16; - case REOP_range: // variable length - nw = get_u16(&p[1]); // number of pairs of uint16_t - if (is_byte_swapped) - n = bswap16(n); - for (r = 3 + 4 * nw; n < r; n += 2) - inplace_bswap16(&p[n]); - goto doswap16; - default: - doswap16: - inplace_bswap16(&p[1]); - break; - } - break; - case 5: - inplace_bswap32(&p[1]); - break; - case 17: - assert(*p == REOP_simple_greedy_quant); - inplace_bswap32(&p[1]); - inplace_bswap32(&p[5]); - inplace_bswap32(&p[9]); - inplace_bswap32(&p[13]); - break; - default: - abort(); - } - p = &p[n]; - } -} - -#ifdef TEST - -bool lre_check_stack_overflow(void *opaque, size_t alloca_size) -{ - return false; -} - -void *lre_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -int main(int argc, char **argv) -{ - int len, flags, ret, i; - uint8_t *bc; - char error_msg[64]; - uint8_t *capture[CAPTURE_COUNT_MAX * 2]; - const char *input; - int input_len, capture_count; - - if (argc < 4) { - printf("usage: %s regexp flags input\n", argv[0]); - exit(1); - } - flags = atoi(argv[2]); - bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], - strlen(argv[1]), flags, NULL); - if (!bc) { - fprintf(stderr, "error: %s\n", error_msg); - exit(1); - } - - input = argv[3]; - input_len = strlen(input); - - ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); - printf("ret=%d\n", ret); - if (ret == 1) { - capture_count = lre_get_capture_count(bc); - for(i = 0; i < 2 * capture_count; i++) { - uint8_t *ptr; - ptr = capture[i]; - printf("%d: ", i); - if (!ptr) - printf(""); - else - printf("%u", (int)(ptr - (uint8_t *)input)); - printf("\n"); - } - } - return 0; -} -#endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp.h deleted file mode 100755 index 898e9a7a..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libregexp.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 - -#include "libunicode.h" - -#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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode-table.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode-table.h deleted file mode 100755 index b48a3a74..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode-table.h +++ /dev/null @@ -1,4658 +0,0 @@ -/* 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, 0x53e60240, - 0x53e80240, 0x53eb0640, 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, - 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, 0x52, 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, 0x55, 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, - 0x54, 0x00, 0x3e, 0x69, 0xbb, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x25, 0x00, 0x48, 0xaa, 0x8a, 0x8b, - 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, - 0x6f, 0xb2, 0x63, 0x62, 0x65, 0x64, 0x67, 0x66, - 0x6c, 0x6d, 0x6e, 0x6f, 0x68, 0x69, 0x6a, 0x6b, - 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[193] = { - 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, 0x00, 0x39, 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, 0xa2, 0x00, 0x80, 0x9b, - 0x12, 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, 0xbb, 0x07, 0x01, 0xdb, 0xd6, 0x01, 0x8a, - 0xf1, 0x01, -}; - -static const uint8_t unicode_prop_Case_Ignorable_table[764] = { - 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, - 0x9e, 0xb0, 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, 0xe6, 0x12, 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, - 0xce, 0x83, 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, - 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, - 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, - 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 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, 0x60, 0x40, 0x0a, - 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, 0x42, 0xdf, 0x86, 0xec, 0x87, - 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, 0x80, 0x9d, - 0xdf, 0xff, 0x40, 0xef, -}; - -static const uint8_t unicode_prop_Case_Ignorable_index[72] = { - 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, - 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, - 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, - 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, - 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, - 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x02, 0x10, 0x01, - 0x42, 0x12, 0x41, 0xc4, 0x14, 0x21, 0xe1, 0x19, - 0x81, 0x48, 0x1d, 0x01, 0x44, 0x6b, 0x01, 0x83, - 0xd1, 0x21, 0x3e, 0xe1, 0x01, 0xf0, 0x01, 0x0e, -}; - -static const uint8_t unicode_prop_ID_Start_table[1133] = { - 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, 0x05, 0x90, 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, 0x11, 0x01, 0x81, 0x9d, 0x03, - 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e, - 0x08, 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, 0xc2, 0x09, 0x00, 0x07, - 0x94, 0x8f, 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, 0xc5, - 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, - 0x82, 0xba, 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, 0x41, 0x46, 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, 0xff, - 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, - 0x57, 0xf7, 0x87, 0x44, 0xd5, 0xa8, 0x89, 0x60, - 0x22, 0xe6, 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, 0x41, 0xee, 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, 0x50, 0x39, 0x85, 0x40, - 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, - 0x42, 0x6d, 0x49, 0xa1, 0x42, 0x1d, 0x45, 0xe1, - 0x53, 0x4a, 0x84, 0x50, 0x5f, -}; - -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, 0xdd, 0xa7, 0x00, 0x4c, 0xaa, 0x20, - 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, - 0x21, 0x96, 0x05, 0x01, 0x9f, 0x08, 0x01, 0x49, - 0x0c, 0x21, 0x76, 0x10, 0x21, 0xa9, 0x12, 0x01, - 0xb0, 0x14, 0x01, 0x42, 0x19, 0x41, 0x90, 0x1c, - 0x01, 0xf1, 0x2f, 0x21, 0x90, 0x6b, 0x21, 0x33, - 0xb1, 0x21, 0x06, 0xd5, 0x01, 0xc3, 0xd7, 0x01, - 0xff, 0xe7, 0x21, 0x63, 0xee, 0x01, 0x5e, 0xee, - 0x42, 0xb0, 0x23, 0x03, -}; - -static const uint8_t unicode_prop_ID_Continue1_table[695] = { - 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, 0x8f, 0xb0, 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, 0xce, 0x83, - 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, - 0x41, 0x55, 0x89, 0xb4, 0x38, 0x87, 0x8f, 0x89, - 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, - 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, - 0x41, 0x48, 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, 0x42, 0xd4, 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, 0x05, 0x1b, 0x00, - 0x0e, 0x20, 0x00, 0xa0, 0xa6, 0x20, 0xe6, 0xa9, - 0x20, 0x10, 0xfe, 0x00, 0x40, 0x0a, 0x01, 0xc3, - 0x10, 0x01, 0x4e, 0x13, 0x01, 0x41, 0x16, 0x01, - 0x0b, 0x1a, 0x01, 0xaa, 0x1d, 0x01, 0x7a, 0x6d, - 0x21, 0x45, 0xd2, 0x21, 0xaf, 0xe2, 0x01, 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[916] = { - 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, 0xc3, 0xb0, 0x34, 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, 0x1f, 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, 0xb2, 0xaf, 0x06, 0xdc, 0xb0, - 0x3c, 0xc5, 0x00, 0x07, -}; - -static const uint8_t unicode_cc_index[87] = { - 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, 0xd3, - 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, - 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, - 0x00, 0x39, 0x0a, 0x01, 0x4c, 0x0f, 0x01, 0x35, - 0x11, 0x21, 0x66, 0x13, 0x01, 0x40, 0x16, 0x01, - 0x47, 0x1a, 0x01, 0xf0, 0x6a, 0x21, 0x8a, 0xd1, - 0x01, 0xec, 0xe4, 0x21, 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, 0x29fc8195, 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, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e, - 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e, - 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5, - 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e, - 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3, - 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada, - 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52, - 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24, - 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c4d, 0x1c52, 0x1c88, 0x1cce, - 0x1cdc, 0x1ce1, 0x1cea, 0x1cf3, 0x1d01, 0x1d06, 0x1d0b, 0x1d1d, - 0x1d2f, 0x1d38, 0x1d3d, 0x1d61, 0x1d6f, 0x1d71, 0x1d73, 0x1d93, - 0x1dae, 0x1db0, 0x1db2, 0x1db4, 0x1db6, 0x1db8, 0x1dba, 0x1dbc, - 0x1ddc, 0x1dde, 0x1de0, 0x1de2, 0x1de4, 0x1deb, 0x1ded, 0x1def, - 0x1df1, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, - 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, - 0x1e20, 0x03f4, 0x1e22, 0x2207, 0x1e24, 0x2202, 0x1e26, 0x1e2e, - 0x03f4, 0x1e30, 0x2207, 0x1e32, 0x2202, 0x1e34, 0x1e3c, 0x03f4, - 0x1e3e, 0x2207, 0x1e40, 0x2202, 0x1e42, 0x1e4a, 0x03f4, 0x1e4c, - 0x2207, 0x1e4e, 0x2202, 0x1e50, 0x1e58, 0x03f4, 0x1e5a, 0x2207, - 0x1e5c, 0x2202, 0x1e5e, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, - 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e80, 0x1ea3, 0x1ea7, 0x1ead, - 0x1eca, 0x062d, 0x1ed2, 0x1ede, 0x062c, 0x1eee, 0x1f5e, 0x1f6a, - 0x1f7d, 0x1f8f, 0x1fa2, 0x1fa4, 0x1fa8, 0x1fae, 0x1fb4, 0x1fb6, - 0x1fba, 0x1fbc, 0x1fc4, 0x1fc7, 0x1fc9, 0x1fcf, 0x1fd1, 0x30b5, - 0x1fd7, 0x202f, 0x2045, 0x2049, 0x204b, 0x2050, 0x209d, 0x20ae, - 0x21af, 0x21bf, 0x21c5, 0x22bf, 0x23dd, -}; - -static const uint8_t unicode_decomp_data[9451] = { - 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, 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[4070] = { - 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, 0x05, - 0xe2, 0x13, 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, 0xa5, 0x00, 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, 0x20, 0x05, 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, 0xa0, - 0x25, 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, 0x08, 0xe0, 0x29, 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, 0x19, - 0xe0, 0x07, 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, 0x18, 0x00, 0xef, - 0x61, 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, 0x3f, 0x20, 0x3f, 0x00, 0x02, 0x00, 0x02, - 0xdf, 0xe0, 0x0d, 0x44, 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, 0xe0, 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, - 0xef, 0x08, 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, - 0x0f, 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, 0xe0, 0x38, - 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, 0xe0, - 0x2f, 0x66, 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, 0x80, 0x2e, 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, 0xe0, - 0x80, 0xae, 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, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, - 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, - 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, - 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, - 0x4e, 0xe0, 0x21, 0xe5, 0x02, 0xe0, 0xa2, 0x5f, - 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, 0xa0, 0xef, 0x81, 0x2c, - 0xe0, 0x44, 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, 0x81, - 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, 0x50, - 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, 0xef, - 0x6f, 0x60, 0xef, 0x57, 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, 0x36, 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, - 0x06, 0x20, 0xef, 0x05, 0x40, 0xef, 0x02, 0x80, - 0xef, 0x30, 0xc0, 0xef, 0x07, 0x20, 0xef, 0x03, - 0xa0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, 0x00, - 0xef, 0x54, 0xe9, 0x02, 0xe0, 0x83, 0x7e, 0xe5, - 0xc0, 0x66, 0x58, 0xe0, 0x18, 0xe5, 0x8f, 0xb2, - 0xa0, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, - 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x07, 0xe5, - 0x81, 0xe6, 0xe0, 0x89, 0x1a, 0xe5, 0x81, 0x96, - 0xe0, 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, - 0x8f, 0xd8, 0xe0, 0xca, 0x9b, 0xc9, 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_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_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_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_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_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" - "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" - "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" - "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" - "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" - "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[2803] = { - 0xc0, 0x19, 0x99, 0x4a, 0x85, 0x19, 0x99, 0x4a, - 0xae, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x80, 0x4a, - 0x84, 0x19, 0x96, 0x4a, 0x80, 0x19, 0x9e, 0x4a, - 0x80, 0x19, 0xe1, 0x60, 0x4a, 0xa6, 0x19, 0x84, - 0x4a, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, - 0x0f, 0x3a, 0x83, 0x2d, 0x80, 0x19, 0x82, 0x2d, - 0x01, 0x83, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x03, - 0x80, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x80, 0x19, - 0x82, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x93, 0x2d, - 0x00, 0xbe, 0x2d, 0x8d, 0x1a, 0x8f, 0x2d, 0xe0, - 0x24, 0x1d, 0x81, 0x3a, 0xe0, 0x48, 0x1d, 0x00, - 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, - 0x00, 0xb6, 0x37, 0x07, 0x9a, 0x37, 0x03, 0x85, - 0x37, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, - 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04, - 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04, - 0x8a, 0x3a, 0x99, 0x04, 0x80, 0x3a, 0xe0, 0x0b, - 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x90, 0x00, - 0xbb, 0x90, 0x01, 0x82, 0x90, 0xaf, 0x04, 0xb1, - 0x9a, 0x0d, 0xba, 0x69, 0x01, 0x82, 0x69, 0xad, - 0x83, 0x01, 0x8e, 0x83, 0x00, 0x9b, 0x55, 0x01, - 0x80, 0x55, 0x00, 0x8a, 0x90, 0x04, 0x9e, 0x04, - 0x00, 0x81, 0x04, 0x04, 0xca, 0x04, 0x80, 0x19, - 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x3a, 0x8e, 0x20, - 0x81, 0x19, 0x99, 0x20, 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, 0x30, - 0x00, 0x85, 0x30, 0x03, 0x81, 0x30, 0x01, 0x95, - 0x30, 0x00, 0x86, 0x30, 0x00, 0x81, 0x30, 0x00, - 0x81, 0x30, 0x00, 0x81, 0x30, 0x01, 0x80, 0x30, - 0x00, 0x84, 0x30, 0x03, 0x81, 0x30, 0x01, 0x82, - 0x30, 0x02, 0x80, 0x30, 0x06, 0x83, 0x30, 0x00, - 0x80, 0x30, 0x06, 0x90, 0x30, 0x09, 0x82, 0x2e, - 0x00, 0x88, 0x2e, 0x00, 0x82, 0x2e, 0x00, 0x95, - 0x2e, 0x00, 0x86, 0x2e, 0x00, 0x81, 0x2e, 0x00, - 0x84, 0x2e, 0x01, 0x89, 0x2e, 0x00, 0x82, 0x2e, - 0x00, 0x82, 0x2e, 0x01, 0x80, 0x2e, 0x0e, 0x83, - 0x2e, 0x01, 0x8b, 0x2e, 0x06, 0x86, 0x2e, 0x00, - 0x82, 0x78, 0x00, 0x87, 0x78, 0x01, 0x81, 0x78, - 0x01, 0x95, 0x78, 0x00, 0x86, 0x78, 0x00, 0x81, - 0x78, 0x00, 0x84, 0x78, 0x01, 0x88, 0x78, 0x01, - 0x81, 0x78, 0x01, 0x82, 0x78, 0x06, 0x82, 0x78, - 0x03, 0x81, 0x78, 0x00, 0x84, 0x78, 0x01, 0x91, - 0x78, 0x09, 0x81, 0x97, 0x00, 0x85, 0x97, 0x02, - 0x82, 0x97, 0x00, 0x83, 0x97, 0x02, 0x81, 0x97, - 0x00, 0x80, 0x97, 0x00, 0x81, 0x97, 0x02, 0x81, - 0x97, 0x02, 0x82, 0x97, 0x02, 0x8b, 0x97, 0x03, - 0x84, 0x97, 0x02, 0x82, 0x97, 0x00, 0x83, 0x97, - 0x01, 0x80, 0x97, 0x05, 0x80, 0x97, 0x0d, 0x94, - 0x97, 0x04, 0x8c, 0x99, 0x00, 0x82, 0x99, 0x00, - 0x96, 0x99, 0x00, 0x8f, 0x99, 0x01, 0x88, 0x99, - 0x00, 0x82, 0x99, 0x00, 0x83, 0x99, 0x06, 0x81, - 0x99, 0x00, 0x82, 0x99, 0x01, 0x80, 0x99, 0x01, - 0x83, 0x99, 0x01, 0x89, 0x99, 0x06, 0x88, 0x99, - 0x8c, 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x96, 0x3f, - 0x00, 0x89, 0x3f, 0x00, 0x84, 0x3f, 0x01, 0x88, - 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x83, 0x3f, 0x06, - 0x81, 0x3f, 0x05, 0x81, 0x3f, 0x00, 0x83, 0x3f, - 0x01, 0x89, 0x3f, 0x00, 0x82, 0x3f, 0x0b, 0x8c, - 0x54, 0x00, 0x82, 0x54, 0x00, 0xb2, 0x54, 0x00, - 0x82, 0x54, 0x00, 0x85, 0x54, 0x03, 0x8f, 0x54, - 0x01, 0x99, 0x54, 0x00, 0x82, 0x89, 0x00, 0x91, - 0x89, 0x02, 0x97, 0x89, 0x00, 0x88, 0x89, 0x00, - 0x80, 0x89, 0x01, 0x86, 0x89, 0x02, 0x80, 0x89, - 0x03, 0x85, 0x89, 0x00, 0x80, 0x89, 0x00, 0x87, - 0x89, 0x05, 0x89, 0x89, 0x01, 0x82, 0x89, 0x0b, - 0xb9, 0x9b, 0x03, 0x80, 0x19, 0x9b, 0x9b, 0x24, - 0x81, 0x49, 0x00, 0x80, 0x49, 0x00, 0x84, 0x49, - 0x00, 0x97, 0x49, 0x00, 0x80, 0x49, 0x00, 0x96, - 0x49, 0x01, 0x84, 0x49, 0x00, 0x80, 0x49, 0x00, - 0x86, 0x49, 0x00, 0x89, 0x49, 0x01, 0x83, 0x49, - 0x1f, 0xc7, 0x9c, 0x00, 0xa3, 0x9c, 0x03, 0xa6, - 0x9c, 0x00, 0xa3, 0x9c, 0x00, 0x8e, 0x9c, 0x00, - 0x86, 0x9c, 0x83, 0x19, 0x81, 0x9c, 0x24, 0xe0, - 0x3f, 0x63, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, - 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83, - 0x28, 0xe0, 0x9f, 0x33, 0xc8, 0x27, 0x00, 0x83, - 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, - 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, - 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, - 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, - 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27, - 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99, - 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, - 0xe2, 0x1f, 0x12, 0x9c, 0x6c, 0x02, 0xca, 0x82, - 0x82, 0x19, 0x8a, 0x82, 0x06, 0x95, 0x91, 0x08, - 0x80, 0x91, 0x94, 0x35, 0x81, 0x19, 0x08, 0x93, - 0x11, 0x0b, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00, - 0x81, 0x92, 0x0b, 0xdd, 0x44, 0x01, 0x89, 0x44, - 0x05, 0x89, 0x44, 0x05, 0x81, 0x60, 0x81, 0x19, - 0x80, 0x60, 0x80, 0x19, 0x93, 0x60, 0x05, 0xd8, - 0x60, 0x06, 0xaa, 0x60, 0x04, 0xc5, 0x12, 0x09, - 0x9e, 0x4c, 0x00, 0x8b, 0x4c, 0x03, 0x8b, 0x4c, - 0x03, 0x80, 0x4c, 0x02, 0x8b, 0x4c, 0x9d, 0x93, - 0x01, 0x84, 0x93, 0x0a, 0xab, 0x67, 0x03, 0x99, - 0x67, 0x05, 0x8a, 0x67, 0x02, 0x81, 0x67, 0x9f, - 0x44, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x94, - 0x00, 0x9c, 0x94, 0x01, 0x8a, 0x94, 0x05, 0x89, - 0x94, 0x05, 0x8d, 0x94, 0x01, 0x9e, 0x3a, 0x30, - 0xcc, 0x07, 0x00, 0xb1, 0x07, 0xbf, 0x8d, 0xb3, - 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x4b, 0x02, 0x8e, - 0x4b, 0x02, 0x82, 0x4b, 0xaf, 0x6d, 0x8a, 0x1d, - 0x04, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87, 0x8d, - 0x07, 0x82, 0x3a, 0x80, 0x19, 0x8c, 0x3a, 0x80, - 0x19, 0x86, 0x3a, 0x83, 0x19, 0x80, 0x3a, 0x85, - 0x19, 0x80, 0x3a, 0x82, 0x19, 0x81, 0x3a, 0x80, - 0x19, 0x04, 0xa5, 0x4a, 0x84, 0x2d, 0x80, 0x1d, - 0xb0, 0x4a, 0x84, 0x2d, 0x83, 0x4a, 0x84, 0x2d, - 0x8c, 0x4a, 0x80, 0x1d, 0xc5, 0x4a, 0x80, 0x2d, - 0xbf, 0x3a, 0xe0, 0x9f, 0x4a, 0x95, 0x2d, 0x01, - 0x85, 0x2d, 0x01, 0xa5, 0x2d, 0x01, 0x85, 0x2d, - 0x01, 0x87, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x80, - 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x9e, 0x2d, 0x01, - 0xb4, 0x2d, 0x00, 0x8e, 0x2d, 0x00, 0x8d, 0x2d, - 0x01, 0x85, 0x2d, 0x00, 0x92, 0x2d, 0x01, 0x82, - 0x2d, 0x00, 0x88, 0x2d, 0x00, 0x8b, 0x19, 0x81, - 0x3a, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, 0x4a, - 0x01, 0x8a, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x00, - 0x8c, 0x4a, 0x02, 0xa0, 0x19, 0x0e, 0xa0, 0x3a, - 0x0e, 0xa5, 0x19, 0x80, 0x2d, 0x82, 0x19, 0x81, - 0x4a, 0x85, 0x19, 0x80, 0x4a, 0x9a, 0x19, 0x80, - 0x4a, 0x90, 0x19, 0xa8, 0x4a, 0x82, 0x19, 0x03, - 0xe2, 0x39, 0x19, 0x15, 0x8a, 0x19, 0x14, 0xe3, - 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x19, - 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, 0xdf, - 0x29, 0x9f, 0x4a, 0xe0, 0x13, 0x1a, 0x04, 0x86, - 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, 0x80, - 0x28, 0x01, 0xb7, 0x9d, 0x06, 0x81, 0x9d, 0x0d, - 0x80, 0x9d, 0x96, 0x27, 0x08, 0x86, 0x27, 0x00, - 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, - 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, - 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, 0xdd, - 0x19, 0x21, 0x99, 0x32, 0x00, 0xd8, 0x32, 0x0b, - 0xe0, 0x75, 0x32, 0x19, 0x94, 0x19, 0x80, 0x32, - 0x80, 0x19, 0x80, 0x32, 0x98, 0x19, 0x88, 0x32, - 0x83, 0x3a, 0x81, 0x33, 0x87, 0x19, 0x83, 0x32, - 0x83, 0x19, 0x00, 0xd5, 0x38, 0x01, 0x81, 0x3a, - 0x81, 0x19, 0x82, 0x38, 0x80, 0x19, 0xd9, 0x40, - 0x81, 0x19, 0x82, 0x40, 0x04, 0xaa, 0x0d, 0x00, - 0xdd, 0x33, 0x00, 0x8f, 0x19, 0x9f, 0x0d, 0xa5, - 0x19, 0x08, 0x80, 0x19, 0x8f, 0x40, 0x9e, 0x33, - 0x00, 0xbf, 0x19, 0x9e, 0x33, 0xd0, 0x19, 0xae, - 0x40, 0x80, 0x19, 0xd7, 0x40, 0xe0, 0x47, 0x19, - 0xf0, 0x09, 0x5f, 0x32, 0xbf, 0x19, 0xf0, 0x41, - 0x9f, 0x32, 0xe4, 0x2c, 0xa9, 0x02, 0xb6, 0xa9, - 0x08, 0xaf, 0x4f, 0xe0, 0xcb, 0xa4, 0x13, 0xdf, - 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, 0xe0, 0x05, - 0x4a, 0x82, 0x19, 0xc2, 0x4a, 0x01, 0x81, 0x4a, - 0x00, 0x80, 0x4a, 0x00, 0x87, 0x4a, 0x14, 0x8d, - 0x4a, 0xac, 0x8f, 0x02, 0x89, 0x19, 0x05, 0xb7, - 0x7e, 0x07, 0xc5, 0x84, 0x07, 0x8b, 0x84, 0x05, - 0x9f, 0x20, 0xad, 0x42, 0x80, 0x19, 0x80, 0x42, - 0xa3, 0x81, 0x0a, 0x80, 0x81, 0x9c, 0x33, 0x02, - 0xcd, 0x3d, 0x00, 0x80, 0x19, 0x89, 0x3d, 0x03, - 0x81, 0x3d, 0x9e, 0x63, 0x00, 0xb6, 0x16, 0x08, - 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, 0x16, - 0x9f, 0x63, 0xc2, 0x95, 0x17, 0x84, 0x95, 0x96, - 0x5a, 0x09, 0x85, 0x27, 0x01, 0x85, 0x27, 0x01, - 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, 0x86, 0x27, - 0x00, 0xaa, 0x4a, 0x80, 0x19, 0x88, 0x4a, 0x80, - 0x2d, 0x83, 0x4a, 0x81, 0x19, 0x03, 0xcf, 0x17, - 0xad, 0x5a, 0x01, 0x89, 0x5a, 0x05, 0xf0, 0x1b, - 0x43, 0x33, 0x0b, 0x96, 0x33, 0x03, 0xb0, 0x33, - 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x32, 0x01, 0xe0, - 0x09, 0x32, 0x25, 0x86, 0x4a, 0x0b, 0x84, 0x05, - 0x04, 0x99, 0x37, 0x00, 0x84, 0x37, 0x00, 0x80, - 0x37, 0x00, 0x81, 0x37, 0x00, 0x81, 0x37, 0x00, - 0x89, 0x37, 0xe0, 0x12, 0x04, 0x0f, 0xe1, 0x0a, - 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, 0xb5, 0x04, - 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, 0x8f, 0x3a, - 0x89, 0x19, 0x05, 0x8d, 0x3a, 0x81, 0x1d, 0xa2, - 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, 0x19, 0x03, - 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, - 0x19, 0x00, 0x9f, 0x19, 0x99, 0x4a, 0x85, 0x19, - 0x99, 0x4a, 0x8a, 0x19, 0x89, 0x40, 0x80, 0x19, - 0xac, 0x40, 0x81, 0x19, 0x9e, 0x33, 0x02, 0x85, - 0x33, 0x01, 0x85, 0x33, 0x01, 0x85, 0x33, 0x01, - 0x82, 0x33, 0x02, 0x86, 0x19, 0x00, 0x86, 0x19, - 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4e, 0x00, 0x99, - 0x4e, 0x00, 0x92, 0x4e, 0x00, 0x81, 0x4e, 0x00, - 0x8e, 0x4e, 0x01, 0x8d, 0x4e, 0x21, 0xe0, 0x1a, - 0x4e, 0x04, 0x82, 0x19, 0x03, 0xac, 0x19, 0x02, - 0x88, 0x19, 0xce, 0x2d, 0x00, 0x8c, 0x19, 0x02, - 0x80, 0x2d, 0x2e, 0xac, 0x19, 0x80, 0x3a, 0x60, - 0x21, 0x9c, 0x50, 0x02, 0xb0, 0x13, 0x0e, 0x80, - 0x3a, 0x9a, 0x19, 0x03, 0xa3, 0x70, 0x08, 0x82, - 0x70, 0x9a, 0x2a, 0x04, 0xaa, 0x72, 0x04, 0x9d, - 0xa3, 0x00, 0x80, 0xa3, 0xa3, 0x73, 0x03, 0x8d, - 0x73, 0x29, 0xcf, 0x1f, 0xaf, 0x86, 0x9d, 0x7a, - 0x01, 0x89, 0x7a, 0x05, 0xa3, 0x79, 0x03, 0xa3, - 0x79, 0x03, 0xa7, 0x25, 0x07, 0xb3, 0x14, 0x0a, - 0x80, 0x14, 0x8a, 0xa5, 0x00, 0x8e, 0xa5, 0x00, - 0x86, 0xa5, 0x00, 0x81, 0xa5, 0x00, 0x8a, 0xa5, - 0x00, 0x8e, 0xa5, 0x00, 0x86, 0xa5, 0x00, 0x81, - 0xa5, 0x02, 0xb3, 0xa0, 0x0b, 0xe0, 0xd6, 0x4d, - 0x08, 0x95, 0x4d, 0x09, 0x87, 0x4d, 0x17, 0x85, - 0x4a, 0x00, 0xa9, 0x4a, 0x00, 0x88, 0x4a, 0x44, - 0x85, 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, - 0x00, 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, - 0x1c, 0x95, 0x39, 0x00, 0x88, 0x39, 0x9f, 0x7c, - 0x9e, 0x64, 0x07, 0x88, 0x64, 0x2f, 0x92, 0x36, - 0x00, 0x81, 0x36, 0x04, 0x84, 0x36, 0x9b, 0x7f, - 0x02, 0x80, 0x7f, 0x99, 0x51, 0x04, 0x80, 0x51, - 0x3f, 0x9f, 0x5d, 0x97, 0x5c, 0x03, 0x93, 0x5c, - 0x01, 0xad, 0x5c, 0x83, 0x43, 0x00, 0x81, 0x43, - 0x04, 0x87, 0x43, 0x00, 0x82, 0x43, 0x00, 0x9c, - 0x43, 0x01, 0x82, 0x43, 0x03, 0x89, 0x43, 0x06, - 0x88, 0x43, 0x06, 0x9f, 0x75, 0x9f, 0x71, 0x1f, - 0xa6, 0x56, 0x03, 0x8b, 0x56, 0x08, 0xb5, 0x06, - 0x02, 0x86, 0x06, 0x95, 0x3c, 0x01, 0x87, 0x3c, - 0x92, 0x3b, 0x04, 0x87, 0x3b, 0x91, 0x80, 0x06, - 0x83, 0x80, 0x0b, 0x86, 0x80, 0x4f, 0xc8, 0x76, - 0x36, 0xb2, 0x6f, 0x0c, 0xb2, 0x6f, 0x06, 0x85, - 0x6f, 0xa7, 0x34, 0x07, 0x89, 0x34, 0x05, 0xa5, - 0x2b, 0x02, 0x9c, 0x2b, 0x07, 0x81, 0x2b, 0x60, - 0x6f, 0x9e, 0x04, 0x00, 0xa9, 0xa8, 0x00, 0x82, - 0xa8, 0x01, 0x81, 0xa8, 0x0f, 0x82, 0x04, 0x36, - 0x83, 0x04, 0xa7, 0x74, 0x07, 0xa9, 0x8a, 0x15, - 0x99, 0x77, 0x25, 0x9b, 0x18, 0x13, 0x96, 0x26, - 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08, 0x80, - 0x0e, 0xc2, 0x3e, 0x09, 0x80, 0x3e, 0x01, 0x98, - 0x8b, 0x06, 0x89, 0x8b, 0x05, 0xb4, 0x15, 0x00, - 0x91, 0x15, 0x07, 0xa6, 0x53, 0x08, 0xdf, 0x85, - 0x00, 0x93, 0x89, 0x0a, 0x91, 0x45, 0x00, 0xae, - 0x45, 0x3d, 0x86, 0x62, 0x00, 0x80, 0x62, 0x00, - 0x83, 0x62, 0x00, 0x8e, 0x62, 0x00, 0x8a, 0x62, - 0x05, 0xba, 0x47, 0x04, 0x89, 0x47, 0x05, 0x83, - 0x2c, 0x00, 0x87, 0x2c, 0x01, 0x81, 0x2c, 0x01, - 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, - 0x00, 0x84, 0x2c, 0x00, 0x80, 0x3a, 0x88, 0x2c, - 0x01, 0x81, 0x2c, 0x01, 0x82, 0x2c, 0x01, 0x80, - 0x2c, 0x05, 0x80, 0x2c, 0x04, 0x86, 0x2c, 0x01, - 0x86, 0x2c, 0x02, 0x84, 0x2c, 0x0a, 0x89, 0xa2, - 0x00, 0x80, 0xa2, 0x01, 0x80, 0xa2, 0x00, 0xa5, - 0xa2, 0x00, 0x89, 0xa2, 0x00, 0x80, 0xa2, 0x01, - 0x80, 0xa2, 0x00, 0x83, 0xa2, 0x00, 0x89, 0xa2, - 0x00, 0x81, 0xa2, 0x07, 0x81, 0xa2, 0x1c, 0xdb, - 0x68, 0x00, 0x84, 0x68, 0x1d, 0xc7, 0x9e, 0x07, - 0x89, 0x9e, 0x60, 0x45, 0xb5, 0x87, 0x01, 0xa5, - 0x87, 0x21, 0xc4, 0x5f, 0x0a, 0x89, 0x5f, 0x05, - 0x8c, 0x60, 0x12, 0xb9, 0x96, 0x05, 0x89, 0x96, - 0x05, 0x93, 0x63, 0x1b, 0x9a, 0x02, 0x01, 0x8e, - 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, - 0x60, 0x03, 0xd2, 0xa7, 0x0b, 0x80, 0xa7, 0x86, - 0x21, 0x01, 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, - 0x81, 0x21, 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, - 0x01, 0x8b, 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, - 0x66, 0x01, 0xad, 0x66, 0x01, 0x8a, 0x66, 0x1a, - 0xc7, 0xaa, 0x07, 0xd2, 0x8c, 0x0c, 0x8f, 0x12, - 0xb8, 0x7d, 0x06, 0x89, 0x20, 0x60, 0x55, 0xa1, - 0x8e, 0x0d, 0x89, 0x8e, 0x05, 0x88, 0x0c, 0x00, - 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, - 0x02, 0x9f, 0x57, 0x01, 0x95, 0x57, 0x00, 0x8d, - 0x57, 0x48, 0x86, 0x58, 0x00, 0x81, 0x58, 0x00, - 0xab, 0x58, 0x02, 0x80, 0x58, 0x00, 0x81, 0x58, - 0x00, 0x88, 0x58, 0x07, 0x89, 0x58, 0x05, 0x85, - 0x2f, 0x00, 0x81, 0x2f, 0x00, 0xa4, 0x2f, 0x00, - 0x81, 0x2f, 0x00, 0x85, 0x2f, 0x06, 0x89, 0x2f, - 0x60, 0xd5, 0x98, 0x52, 0x06, 0x90, 0x41, 0x00, - 0xa8, 0x41, 0x02, 0x9c, 0x41, 0x54, 0x80, 0x4f, - 0x0e, 0xb1, 0x97, 0x0c, 0x80, 0x97, 0xe3, 0x39, - 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, 0x00, 0x84, - 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, 0xeb, 0xe0, - 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, 0x09, 0xef, - 0x3a, 0x24, 0x04, 0xe1, 0xe6, 0x03, 0x70, 0x0a, - 0x58, 0xb9, 0x31, 0x66, 0x65, 0xe1, 0xd8, 0x08, - 0x06, 0x9e, 0x61, 0x00, 0x89, 0x61, 0x03, 0x81, - 0x61, 0xce, 0x9f, 0x00, 0x89, 0x9f, 0x05, 0x9d, - 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x7b, 0x09, - 0x89, 0x7b, 0x00, 0x86, 0x7b, 0x00, 0x94, 0x7b, - 0x04, 0x92, 0x7b, 0x61, 0x4f, 0xb9, 0x48, 0x60, - 0x65, 0xda, 0x59, 0x60, 0x04, 0xca, 0x5e, 0x03, - 0xb8, 0x5e, 0x06, 0x90, 0x5e, 0x3f, 0x80, 0x98, - 0x80, 0x6a, 0x81, 0x32, 0x80, 0x46, 0x0a, 0x81, - 0x32, 0x0d, 0xf0, 0x07, 0x97, 0x98, 0x07, 0xe2, - 0x9f, 0x98, 0xe1, 0x75, 0x46, 0x28, 0x80, 0x46, - 0x88, 0x98, 0x70, 0x12, 0x86, 0x83, 0x40, 0x00, - 0x86, 0x40, 0x00, 0x81, 0x40, 0x00, 0x80, 0x40, - 0xe0, 0xbe, 0x38, 0x82, 0x40, 0x0e, 0x80, 0x38, - 0x1c, 0x82, 0x38, 0x01, 0x80, 0x40, 0x0d, 0x83, - 0x40, 0x07, 0xe1, 0x2b, 0x6a, 0x68, 0xa3, 0xe0, - 0x0a, 0x23, 0x04, 0x8c, 0x23, 0x02, 0x88, 0x23, - 0x06, 0x89, 0x23, 0x01, 0x83, 0x23, 0x83, 0x19, - 0x6e, 0xfb, 0xe0, 0x99, 0x19, 0x05, 0xe1, 0x53, - 0x19, 0x4b, 0xad, 0x3a, 0x01, 0x96, 0x3a, 0x08, - 0xe0, 0x13, 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, - 0xa6, 0x19, 0x01, 0xbd, 0x19, 0x82, 0x3a, 0x90, - 0x19, 0x87, 0x3a, 0x81, 0x19, 0x86, 0x3a, 0x9d, - 0x19, 0x83, 0x3a, 0xbc, 0x19, 0x14, 0xc5, 0x2d, - 0x60, 0x19, 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, - 0xd6, 0x19, 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, - 0x19, 0x00, 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, - 0x80, 0x19, 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, - 0x00, 0x8b, 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, - 0x19, 0x00, 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, - 0x87, 0x19, 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, - 0x00, 0x83, 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, - 0x19, 0x02, 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, - 0x01, 0xe0, 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, - 0x2b, 0x88, 0x0e, 0x84, 0x88, 0x00, 0x8e, 0x88, - 0x63, 0xef, 0x9e, 0x4a, 0x05, 0x85, 0x4a, 0x60, - 0x74, 0x86, 0x29, 0x00, 0x90, 0x29, 0x01, 0x86, - 0x29, 0x00, 0x81, 0x29, 0x00, 0x84, 0x29, 0x04, - 0xbd, 0x1d, 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, - 0x6b, 0x02, 0x8d, 0x6b, 0x01, 0x89, 0x6b, 0x03, - 0x81, 0x6b, 0x60, 0xdf, 0x9e, 0xa1, 0x10, 0xb9, - 0xa6, 0x04, 0x80, 0xa6, 0x61, 0x6f, 0xa9, 0x65, - 0x60, 0x75, 0xaa, 0x6e, 0x03, 0x80, 0x6e, 0x61, - 0x7f, 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, - 0x27, 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x5b, - 0x01, 0x8f, 0x5b, 0x28, 0xcb, 0x01, 0x03, 0x89, - 0x01, 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, - 0x4b, 0xbc, 0x19, 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, 0x19, 0x03, 0xe0, - 0x03, 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, - 0x00, 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, - 0x4d, 0x19, 0x37, 0x99, 0x19, 0x80, 0x38, 0x81, - 0x19, 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, - 0x81, 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, - 0x77, 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, - 0x02, 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, - 0x8b, 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, - 0x03, 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, - 0x19, 0x07, 0x9d, 0x19, 0x01, 0x8b, 0x19, 0x03, - 0x81, 0x19, 0x3d, 0xe0, 0xf3, 0x19, 0x0b, 0x8d, - 0x19, 0x01, 0x8c, 0x19, 0x02, 0x89, 0x19, 0x04, - 0xb7, 0x19, 0x06, 0x8e, 0x19, 0x01, 0x8a, 0x19, - 0x05, 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, - 0xe0, 0x05, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, - 0x32, 0x1f, 0xef, 0xd9, 0x32, 0x05, 0xe0, 0x7d, - 0x32, 0x01, 0xf0, 0x06, 0x21, 0x32, 0x0d, 0xf0, - 0x0c, 0xd0, 0x32, 0x0e, 0xe2, 0x0d, 0x32, 0x69, - 0x41, 0xe1, 0xbd, 0x32, 0x65, 0x81, 0xf0, 0x02, - 0xea, 0x32, 0x04, 0xef, 0xff, 0x32, 0x7a, 0xcb, - 0xf0, 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, - 0xe0, 0x8f, 0x3a, -}; - -static const uint8_t unicode_script_ext_table[1253] = { - 0x80, 0x36, 0x00, 0x00, 0x10, 0x06, 0x13, 0x1a, - 0x23, 0x25, 0x28, 0x29, 0x2f, 0x2a, 0x2d, 0x32, - 0x4a, 0x51, 0x53, 0x72, 0x86, 0x81, 0x83, 0x00, - 0x00, 0x07, 0x0b, 0x1d, 0x20, 0x4a, 0x4f, 0x9b, - 0xa1, 0x09, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x00, - 0x00, 0x02, 0x02, 0x0d, 0x4a, 0x00, 0x00, 0x00, - 0x02, 0x4a, 0x4f, 0x08, 0x00, 0x00, 0x02, 0x4a, - 0x9b, 0x00, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x25, - 0x00, 0x00, 0x08, 0x17, 0x1a, 0x1d, 0x2d, 0x4a, - 0x72, 0x8e, 0x93, 0x00, 0x08, 0x17, 0x1d, 0x2d, - 0x4a, 0x79, 0x8e, 0x93, 0xa0, 0x00, 0x04, 0x17, - 0x1d, 0x4a, 0x9d, 0x00, 0x05, 0x29, 0x4a, 0x8e, - 0x90, 0x9b, 0x00, 0x0b, 0x14, 0x17, 0x1a, 0x1d, - 0x2a, 0x2d, 0x4a, 0x79, 0x90, 0x9d, 0xa0, 0x00, - 0x06, 0x1a, 0x25, 0x29, 0x2a, 0x40, 0x4a, 0x00, - 0x04, 0x1d, 0x2d, 0x4a, 0x72, 0x00, 0x09, 0x1a, - 0x23, 0x37, 0x4a, 0x72, 0x90, 0x93, 0x9d, 0xa0, - 0x00, 0x0a, 0x05, 0x1d, 0x23, 0x2a, 0x2d, 0x37, - 0x4a, 0x72, 0x90, 0x93, 0x00, 0x02, 0x4a, 0x9d, - 0x00, 0x03, 0x23, 0x4a, 0x90, 0x00, 0x04, 0x17, - 0x1d, 0x4a, 0x79, 0x00, 0x03, 0x17, 0x4a, 0x93, - 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x02, 0x27, 0x4a, - 0x00, 0x00, 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x03, - 0x1d, 0x4a, 0xa0, 0x00, 0x00, 0x00, 0x04, 0x2d, - 0x4a, 0x72, 0xa0, 0x0b, 0x00, 0x00, 0x02, 0x4a, - 0x90, 0x01, 0x00, 0x00, 0x05, 0x17, 0x23, 0x40, - 0x4a, 0x90, 0x00, 0x04, 0x17, 0x23, 0x4a, 0x90, - 0x00, 0x02, 0x4a, 0x90, 0x06, 0x00, 0x00, 0x03, - 0x4a, 0x8e, 0x90, 0x00, 0x02, 0x4a, 0x90, 0x00, - 0x00, 0x00, 0x03, 0x17, 0x4a, 0x90, 0x00, 0x06, - 0x14, 0x17, 0x2a, 0x4a, 0x8e, 0x9b, 0x0f, 0x00, - 0x00, 0x01, 0x2d, 0x01, 0x00, 0x00, 0x01, 0x2d, - 0x11, 0x00, 0x00, 0x02, 0x4a, 0x79, 0x04, 0x00, - 0x00, 0x03, 0x14, 0x4a, 0xa0, 0x03, 0x00, 0x0c, - 0x01, 0x4a, 0x03, 0x00, 0x01, 0x02, 0x1a, 0x2d, - 0x80, 0x8c, 0x00, 0x00, 0x02, 0x1d, 0x72, 0x00, - 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x4a, 0x00, - 0x02, 0x1d, 0x29, 0x80, 0x80, 0x00, 0x00, 0x03, - 0x05, 0x28, 0x29, 0x80, 0x01, 0x00, 0x00, 0x07, - 0x04, 0x2b, 0x69, 0x34, 0x90, 0x9a, 0xa8, 0x0d, - 0x00, 0x00, 0x07, 0x04, 0x2b, 0x69, 0x34, 0x90, - 0x9a, 0xa8, 0x00, 0x03, 0x04, 0x90, 0x9a, 0x01, - 0x00, 0x00, 0x08, 0x01, 0x04, 0x2b, 0x69, 0x34, - 0x90, 0x9a, 0xa8, 0x1f, 0x00, 0x00, 0x09, 0x01, - 0x04, 0x55, 0x56, 0x77, 0x80, 0x34, 0x8a, 0x90, - 0x09, 0x00, 0x0a, 0x02, 0x04, 0x90, 0x09, 0x00, - 0x09, 0x03, 0x04, 0x9a, 0xa8, 0x05, 0x00, 0x00, - 0x02, 0x04, 0x90, 0x62, 0x00, 0x00, 0x02, 0x04, - 0x34, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x20, - 0x2c, 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x85, - 0x97, 0x99, 0x9e, 0x00, 0x0c, 0x0b, 0x20, 0x2c, - 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x97, 0x99, - 0x9e, 0x10, 0x00, 0x00, 0x15, 0x0b, 0x20, 0x22, - 0x2f, 0x58, 0x2c, 0x2e, 0x30, 0x3f, 0x53, 0x54, - 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, 0x97, - 0x99, 0x9e, 0x00, 0x17, 0x0b, 0x20, 0x22, 0x2f, - 0x58, 0x2c, 0x2e, 0x31, 0x30, 0x3f, 0x4c, 0x53, - 0x54, 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, - 0x97, 0x99, 0x9e, 0x09, 0x04, 0x20, 0x22, 0x3e, - 0x53, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x8f, - 0x75, 0x00, 0x09, 0x02, 0x30, 0x62, 0x75, 0x00, - 0x09, 0x02, 0x2e, 0x45, 0x80, 0x75, 0x00, 0x0d, - 0x02, 0x2c, 0x97, 0x80, 0x71, 0x00, 0x09, 0x03, - 0x3f, 0x66, 0xa2, 0x82, 0xcf, 0x00, 0x09, 0x03, - 0x15, 0x63, 0x93, 0x80, 0x30, 0x00, 0x00, 0x03, - 0x28, 0x29, 0x4a, 0x85, 0x6e, 0x00, 0x02, 0x01, - 0x82, 0x46, 0x00, 0x01, 0x04, 0x11, 0x35, 0x92, - 0x91, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x60, 0x7e, - 0x00, 0x00, 0x00, 0x02, 0x60, 0x7e, 0x84, 0x49, - 0x00, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, 0x00, - 0x01, 0x20, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, - 0x00, 0x03, 0x20, 0x2c, 0x3f, 0x00, 0x01, 0x20, - 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, - 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, - 0x00, 0x06, 0x20, 0x3f, 0x54, 0x78, 0x97, 0x99, - 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, 0x85, 0x01, - 0x01, 0x20, 0x00, 0x02, 0x20, 0x85, 0x00, 0x02, - 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, 0x02, 0x20, - 0x66, 0x00, 0x02, 0x0b, 0x20, 0x01, 0x01, 0x20, - 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, 0x20, 0x00, - 0x0b, 0x0b, 0x20, 0x2c, 0x3f, 0x54, 0x66, 0x78, - 0x89, 0x99, 0x9e, 0xa2, 0x00, 0x02, 0x20, 0x2c, - 0x00, 0x04, 0x20, 0x2c, 0x3f, 0xa2, 0x01, 0x02, - 0x0b, 0x20, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, - 0x2c, 0x00, 0x01, 0x66, 0x80, 0x44, 0x00, 0x01, - 0x01, 0x2d, 0x35, 0x00, 0x00, 0x03, 0x1d, 0x4a, - 0x90, 0x00, 0x00, 0x00, 0x01, 0x90, 0x81, 0xb3, - 0x00, 0x00, 0x03, 0x4a, 0x60, 0x7e, 0x1e, 0x00, - 0x00, 0x02, 0x01, 0x04, 0x09, 0x00, 0x00, 0x06, - 0x13, 0x28, 0x29, 0x6f, 0x50, 0x76, 0x01, 0x00, - 0x00, 0x04, 0x13, 0x2d, 0x6f, 0x5d, 0x80, 0x11, - 0x00, 0x00, 0x03, 0x20, 0x2c, 0x4a, 0x8c, 0xa5, - 0x00, 0x00, 0x02, 0x1a, 0x4a, 0x17, 0x00, 0x00, - 0x02, 0x06, 0x76, 0x00, 0x07, 0x06, 0x13, 0x28, - 0x6f, 0x3e, 0x51, 0x83, 0x09, 0x00, 0x00, 0x01, - 0x23, 0x03, 0x00, 0x00, 0x03, 0x01, 0x04, 0x6f, - 0x00, 0x00, 0x00, 0x02, 0x1d, 0x29, 0x81, 0x2b, - 0x00, 0x0f, 0x02, 0x32, 0x98, 0x00, 0x00, 0x00, - 0x07, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, 0xa9, - 0x00, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, - 0x7e, 0xa9, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, - 0x40, 0x01, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, - 0x01, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, - 0x9c, 0xa9, 0x01, 0x09, 0x0d, 0x33, 0x32, 0x38, - 0x40, 0x4f, 0x60, 0x9c, 0xa9, 0x05, 0x06, 0x0d, - 0x33, 0x32, 0x38, 0x40, 0xa9, 0x00, 0x00, 0x00, - 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x07, 0x06, - 0x0d, 0x33, 0x32, 0x38, 0x40, 0xa9, 0x03, 0x05, - 0x0d, 0x33, 0x32, 0x38, 0x40, 0x09, 0x00, 0x03, - 0x02, 0x0d, 0x32, 0x01, 0x00, 0x00, 0x05, 0x0d, - 0x33, 0x32, 0x38, 0x40, 0x04, 0x02, 0x38, 0x40, - 0x00, 0x00, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, - 0x40, 0x03, 0x00, 0x01, 0x03, 0x32, 0x38, 0x40, - 0x01, 0x01, 0x32, 0x58, 0x00, 0x03, 0x02, 0x38, - 0x40, 0x02, 0x00, 0x00, 0x02, 0x38, 0x40, 0x59, - 0x00, 0x00, 0x06, 0x0d, 0x33, 0x32, 0x38, 0x40, - 0xa9, 0x00, 0x02, 0x38, 0x40, 0x80, 0x12, 0x00, - 0x0f, 0x01, 0x32, 0x1f, 0x00, 0x25, 0x01, 0x32, - 0x08, 0x00, 0x00, 0x02, 0x32, 0x98, 0x2f, 0x00, - 0x27, 0x01, 0x32, 0x37, 0x00, 0x30, 0x01, 0x32, - 0x0e, 0x00, 0x0b, 0x01, 0x32, 0x32, 0x00, 0x00, - 0x01, 0x32, 0x57, 0x00, 0x18, 0x01, 0x32, 0x09, - 0x00, 0x04, 0x01, 0x32, 0x5f, 0x00, 0x1e, 0x01, - 0x32, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, - 0x29, 0x80, 0x0f, 0x00, 0x07, 0x02, 0x32, 0x4a, - 0x80, 0xa7, 0x00, 0x02, 0x10, 0x20, 0x22, 0x2e, - 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x54, 0x5f, 0x66, - 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x02, 0x0f, 0x20, - 0x22, 0x2e, 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x5f, - 0x66, 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x01, 0x0b, - 0x20, 0x22, 0x2e, 0x30, 0x45, 0x3e, 0x53, 0x5f, - 0x47, 0x96, 0x9e, 0x00, 0x0c, 0x20, 0x22, 0x2e, - 0x30, 0x45, 0x3e, 0x53, 0x5f, 0x85, 0x47, 0x96, - 0x9e, 0x00, 0x0b, 0x20, 0x22, 0x2e, 0x30, 0x45, - 0x3e, 0x53, 0x5f, 0x47, 0x96, 0x9e, 0x80, 0x36, - 0x00, 0x00, 0x03, 0x0b, 0x20, 0xa2, 0x00, 0x00, - 0x00, 0x02, 0x20, 0x97, 0x39, 0x00, 0x00, 0x03, - 0x42, 0x4a, 0x63, 0x80, 0x1f, 0x00, 0x00, 0x02, - 0x10, 0x3d, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, - 0x04, 0x69, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, - 0x9a, 0x09, 0x00, 0x00, 0x02, 0x04, 0x9a, 0x46, - 0x00, 0x01, 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, - 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x33, 0x32, - 0x38, 0x40, 0xa9, 0x09, 0x00, 0x00, 0x02, 0x38, - 0x40, 0x2c, 0x00, 0x01, 0x02, 0x38, 0x40, 0x80, - 0xdf, 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4e, 0x00, - 0x02, 0x1c, 0x4e, 0x03, 0x00, 0x2c, 0x03, 0x1c, - 0x4d, 0x4e, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4e, - 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, - 0x75, 0x00, 0x00, 0x02, 0x56, 0x77, 0x87, 0x8d, - 0x00, 0x00, 0x02, 0x2c, 0x97, 0x00, 0x00, 0x00, - 0x02, 0x2c, 0x97, 0x36, 0x00, 0x01, 0x02, 0x2c, - 0x97, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2c, 0x97, - 0x00, 0x00, 0x00, 0x02, 0x2c, 0x97, 0xc0, 0x5c, - 0x4b, 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, - 0x11, 0x01, 0x32, 0x9e, 0x5d, 0x00, 0x01, 0x01, - 0x32, 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[443] = { - 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, 0xce, - 0x80, 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, 0x41, 0x96, 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, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, 0x85, - 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Other_Lowercase_table[69] = { - 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, - 0x40, 0x80, 0x12, 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[450] = { - 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, 0x40, 0x80, - 0x12, 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[438] = { - 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, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, - 0x40, 0x85, 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, 0xe7, 0x80, 0x8e, 0x80, 0xa5, - 0x88, 0xb5, 0x81, 0xb9, 0x80, 0x8a, 0x81, 0xc1, - 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, - 0xb1, 0xbe, 0xd8, 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, 0xec, 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, 0x8e, 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, 0x41, 0xa8, - 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[111] = { - 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, 0x60, 0x50, 0xa8, - 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, - 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[72] = { - 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, 0x9a, 0x57, 0xf7, 0x87, 0x44, - 0xd5, 0xa8, 0x89, 0x60, 0x24, 0x66, 0x41, 0x8b, - 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50, - 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, - 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x49, 0xa1, 0x42, - 0x1d, 0x45, 0xe1, 0x53, 0x4a, 0x84, 0x50, 0x5f, -}; - -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[48] = { - 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, 0x50, 0x39, 0x85, 0x40, 0xdd, - 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, 0x42, - 0x6d, 0x51, 0xa1, 0x53, 0x4a, 0x84, 0x50, 0x5f, -}; - -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[238] = { - 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, 0x0a, 0x83, - 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, - 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, - 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, 0xb7, - 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, -}; - -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[144] = { - 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, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, - 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, - 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, - 0x84, 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, -}; - -static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { - 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, - 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, - 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, - 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, - 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, - 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, - 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, - 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, - 0x8a, 0x84, 0xac, 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, - 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, - 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, - 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, - 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, - 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, - 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, - 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, - 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), -}; - diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode.c deleted file mode 100755 index a621c523..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "libunicode.h" -#include "libunicode-table.h" - -// note: stored as 4 bit tag, not much room left -enum { - RUN_TYPE_U, - RUN_TYPE_L, - RUN_TYPE_UF, - RUN_TYPE_LF, - RUN_TYPE_UL, - RUN_TYPE_LSU, - RUN_TYPE_U2L_399_EXT2, - RUN_TYPE_UF_D20, - RUN_TYPE_UF_D1_EXT, - RUN_TYPE_U_EXT, - RUN_TYPE_LF_EXT, - RUN_TYPE_UF_EXT2, - RUN_TYPE_LF_EXT2, - RUN_TYPE_UF_EXT3, -}; - -static int lre_case_conv1(uint32_t c, int conv_type) -{ - uint32_t res[LRE_CC_RES_LEN_MAX]; - lre_case_conv(res, c, conv_type); - return res[0]; -} - -/* case conversion using the table entry 'idx' with value 'v' */ -static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v) -{ - uint32_t code, data, type, a, is_lower; - is_lower = (conv_type != 0); - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - data = ((v & 0xf) << 8) | case_conv_table2[idx]; - code = v >> (32 - 17); - switch(type) { - case RUN_TYPE_U: - case RUN_TYPE_L: - case RUN_TYPE_UF: - case RUN_TYPE_LF: - if (conv_type == (type & 1) || - (type >= RUN_TYPE_UF && conv_type == 2)) { - c = c - code + (case_conv_table1[data] >> (32 - 17)); - } - break; - case RUN_TYPE_UL: - a = c - code; - if ((a & 1) != (1 - is_lower)) - break; - c = (a ^ 1) + code; - break; - case RUN_TYPE_LSU: - a = c - code; - if (a == 1) { - c += 2 * is_lower - 1; - } else if (a == (1 - is_lower) * 2) { - c += (2 * is_lower - 1) * 2; - } - break; - case RUN_TYPE_U2L_399_EXT2: - if (!is_lower) { - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = 0x399; - return 2; - } else { - c = c - code + case_conv_ext[data & 0x3f]; - } - break; - case RUN_TYPE_UF_D20: - if (conv_type == 1) - break; - c = data + (conv_type == 2) * 0x20; - break; - case RUN_TYPE_UF_D1_EXT: - if (conv_type == 1) - break; - c = case_conv_ext[data] + (conv_type == 2); - break; - case RUN_TYPE_U_EXT: - case RUN_TYPE_LF_EXT: - if (is_lower != (type - RUN_TYPE_U_EXT)) - break; - c = case_conv_ext[data]; - break; - case RUN_TYPE_LF_EXT2: - if (!is_lower) - break; - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = case_conv_ext[data & 0x3f]; - return 2; - case RUN_TYPE_UF_EXT2: - if (conv_type == 1) - break; - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = case_conv_ext[data & 0x3f]; - if (conv_type == 2) { - /* convert to lower */ - res[0] = lre_case_conv1(res[0], 1); - res[1] = lre_case_conv1(res[1], 1); - } - return 2; - default: - case RUN_TYPE_UF_EXT3: - if (conv_type == 1) - break; - res[0] = case_conv_ext[data >> 8]; - res[1] = case_conv_ext[(data >> 4) & 0xf]; - res[2] = case_conv_ext[data & 0xf]; - if (conv_type == 2) { - /* convert to lower */ - res[0] = lre_case_conv1(res[0], 1); - res[1] = lre_case_conv1(res[1], 1); - res[2] = lre_case_conv1(res[2], 1); - } - return 3; - } - res[0] = c; - return 1; -} - -/* conv_type: - 0 = to upper - 1 = to lower - 2 = case folding (= to lower with modifications) -*/ -int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) -{ - if (c < 128) { - if (conv_type) { - if (c >= 'A' && c <= 'Z') { - c = c - 'A' + 'a'; - } - } else { - if (c >= 'a' && c <= 'z') { - c = c - 'a' + 'A'; - } - } - } else { - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return lre_case_conv_entry(res, c, conv_type, idx, v); - } - } - } - res[0] = c; - return 1; -} - -static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, bool is_unicode) -{ - uint32_t res[LRE_CC_RES_LEN_MAX]; - int len; - - if (is_unicode) { - len = lre_case_conv_entry(res, c, 2, idx, v); - if (len == 1) { - c = res[0]; - } else { - /* handle the few specific multi-character cases (see - unicode_gen.c:dump_case_folding_special_cases()) */ - if (c == 0xfb06) { - c = 0xfb05; - } else if (c == 0x01fd3) { - c = 0x390; - } else if (c == 0x01fe3) { - c = 0x3b0; - } - } - } else { - if (likely(c < 128)) { - if (c >= 'a' && c <= 'z') - c = c - 'a' + 'A'; - } else { - /* legacy regexp: to upper case if single char >= 128 */ - len = lre_case_conv_entry(res, c, false, idx, v); - if (len == 1 && res[0] >= 128) - c = res[0]; - } - } - return c; -} - -/* JS regexp specific rules for case folding */ -int lre_canonicalize(uint32_t c, bool is_unicode) -{ - if (c < 128) { - /* fast case */ - if (is_unicode) { - if (c >= 'A' && c <= 'Z') { - c = c - 'A' + 'a'; - } - } else { - if (c >= 'a' && c <= 'z') { - c = c - 'a' + 'A'; - } - } - } else { - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return lre_case_folding_entry(c, idx, v, is_unicode); - } - } - } - return c; -} - -static uint32_t get_le24(const uint8_t *ptr) -{ - return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); -} - -#define UNICODE_INDEX_BLOCK_LEN 32 - -/* return -1 if not in table, otherwise the offset in the block */ -static int get_index_pos(uint32_t *pcode, uint32_t c, - const uint8_t *index_table, int index_table_len) -{ - uint32_t code, v; - int idx_min, idx_max, idx; - - idx_min = 0; - v = get_le24(index_table); - code = v & ((1 << 21) - 1); - if (c < code) { - *pcode = 0; - return 0; - } - idx_max = index_table_len - 1; - code = get_le24(index_table + idx_max * 3); - if (c >= code) - return -1; - /* invariant: tab[idx_min] <= c < tab2[idx_max] */ - while ((idx_max - idx_min) > 1) { - idx = (idx_max + idx_min) / 2; - v = get_le24(index_table + idx * 3); - code = v & ((1 << 21) - 1); - if (c < code) { - idx_max = idx; - } else { - idx_min = idx; - } - } - v = get_le24(index_table + idx_min * 3); - *pcode = v & ((1 << 21) - 1); - return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); -} - -static bool lre_is_in_table(uint32_t c, const uint8_t *table, - const uint8_t *index_table, int index_table_len) -{ - uint32_t code, b, bit; - int pos; - const uint8_t *p; - - pos = get_index_pos(&code, c, index_table, index_table_len); - if (pos < 0) - return false; /* outside the table */ - p = table + pos; - bit = 0; - for(;;) { - b = *p++; - if (b < 64) { - code += (b >> 3) + 1; - if (c < code) - return bit; - bit ^= 1; - code += (b & 7) + 1; - } else if (b >= 0x80) { - code += b - 0x80 + 1; - } else if (b < 0x60) { - code += (((b - 0x40) << 8) | p[0]) + 1; - p++; - } else { - code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; - p += 2; - } - if (c < code) - return bit; - bit ^= 1; - } -} - -bool lre_is_cased(uint32_t c) -{ - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return true; - } - } - return lre_is_in_table(c, unicode_prop_Cased1_table, - unicode_prop_Cased1_index, - sizeof(unicode_prop_Cased1_index) / 3); -} - -bool lre_is_case_ignorable(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, - unicode_prop_Case_Ignorable_index, - sizeof(unicode_prop_Case_Ignorable_index) / 3); -} - -/* character range */ - -static __maybe_unused void cr_dump(CharRange *cr) -{ - int i; - for(i = 0; i < cr->len; i++) - printf("%d: 0x%04x\n", i, cr->points[i]); -} - -static void *cr_default_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) -{ - cr->len = cr->size = 0; - cr->points = NULL; - cr->mem_opaque = mem_opaque; - cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; -} - -void cr_free(CharRange *cr) -{ - cr->realloc_func(cr->mem_opaque, cr->points, 0); -} - -int cr_realloc(CharRange *cr, int size) -{ - int new_size; - uint32_t *new_buf; - - if (size > cr->size) { - new_size = max_int(size, cr->size * 3 / 2); - new_buf = cr->realloc_func(cr->mem_opaque, cr->points, - new_size * sizeof(cr->points[0])); - if (!new_buf) - return -1; - cr->points = new_buf; - cr->size = new_size; - } - return 0; -} - -int cr_copy(CharRange *cr, const CharRange *cr1) -{ - if (cr_realloc(cr, cr1->len)) - return -1; - memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); - cr->len = cr1->len; - return 0; -} - -/* merge consecutive intervals and remove empty intervals */ -static void cr_compress(CharRange *cr) -{ - int i, j, k, len; - uint32_t *pt; - - pt = cr->points; - len = cr->len; - i = 0; - j = 0; - k = 0; - while ((i + 1) < len) { - if (pt[i] == pt[i + 1]) { - /* empty interval */ - i += 2; - } else { - j = i; - while ((j + 3) < len && pt[j + 1] == pt[j + 2]) - j += 2; - /* just copy */ - pt[k] = pt[i]; - pt[k + 1] = pt[j + 1]; - k += 2; - i = j + 2; - } - } - cr->len = k; -} - -/* union or intersection */ -int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, - const uint32_t *b_pt, int b_len, int op) -{ - int a_idx, b_idx, is_in; - uint32_t v; - - a_idx = 0; - b_idx = 0; - for(;;) { - /* get one more point from a or b in increasing order */ - if (a_idx < a_len && b_idx < b_len) { - if (a_pt[a_idx] < b_pt[b_idx]) { - goto a_add; - } else if (a_pt[a_idx] == b_pt[b_idx]) { - v = a_pt[a_idx]; - a_idx++; - b_idx++; - } else { - goto b_add; - } - } else if (a_idx < a_len) { - a_add: - v = a_pt[a_idx++]; - } else if (b_idx < b_len) { - b_add: - v = b_pt[b_idx++]; - } else { - break; - } - /* add the point if the in/out status changes */ - switch(op) { - case CR_OP_UNION: - is_in = (a_idx & 1) | (b_idx & 1); - break; - case CR_OP_INTER: - is_in = (a_idx & 1) & (b_idx & 1); - break; - case CR_OP_XOR: - is_in = (a_idx & 1) ^ (b_idx & 1); - break; - default: - abort(); - } - if (is_in != (cr->len & 1)) { - if (cr_add_point(cr, v)) - return -1; - } - } - cr_compress(cr); - return 0; -} - -int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) -{ - CharRange a = *cr; - int ret; - cr->len = 0; - cr->size = 0; - cr->points = NULL; - ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); - cr_free(&a); - return ret; -} - -int cr_invert(CharRange *cr) -{ - int len; - len = cr->len; - if (cr_realloc(cr, len + 2)) - return -1; - memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); - cr->points[0] = 0; - cr->points[len + 1] = UINT32_MAX; - cr->len = len + 2; - cr_compress(cr); - return 0; -} - -bool lre_is_id_start(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_ID_Start_table, - unicode_prop_ID_Start_index, - sizeof(unicode_prop_ID_Start_index) / 3); -} - -bool lre_is_id_continue(uint32_t c) -{ - return lre_is_id_start(c) || - lre_is_in_table(c, unicode_prop_ID_Continue1_table, - unicode_prop_ID_Continue1_index, - sizeof(unicode_prop_ID_Continue1_index) / 3); -} - -bool lre_is_white_space(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_White_Space_table, - unicode_prop_White_Space_index, - sizeof(unicode_prop_White_Space_index) / 3); -} - -#define UNICODE_DECOMP_LEN_MAX 18 - -typedef enum { - DECOMP_TYPE_C1, /* 16 bit char */ - DECOMP_TYPE_L1, /* 16 bit char table */ - DECOMP_TYPE_L2, - DECOMP_TYPE_L3, - DECOMP_TYPE_L4, - DECOMP_TYPE_L5, /* XXX: not used */ - DECOMP_TYPE_L6, /* XXX: could remove */ - DECOMP_TYPE_L7, /* XXX: could remove */ - DECOMP_TYPE_LL1, /* 18 bit char table */ - DECOMP_TYPE_LL2, - DECOMP_TYPE_S1, /* 8 bit char table */ - DECOMP_TYPE_S2, - DECOMP_TYPE_S3, - DECOMP_TYPE_S4, - DECOMP_TYPE_S5, - DECOMP_TYPE_I1, /* increment 16 bit char value */ - DECOMP_TYPE_I2_0, - DECOMP_TYPE_I2_1, - DECOMP_TYPE_I3_1, - DECOMP_TYPE_I3_2, - DECOMP_TYPE_I4_1, - DECOMP_TYPE_I4_2, - DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ - DECOMP_TYPE_B2, - DECOMP_TYPE_B3, - DECOMP_TYPE_B4, - DECOMP_TYPE_B5, - DECOMP_TYPE_B6, - DECOMP_TYPE_B7, - DECOMP_TYPE_B8, - DECOMP_TYPE_B18, - DECOMP_TYPE_LS2, - DECOMP_TYPE_PAT3, - DECOMP_TYPE_S2_UL, - DECOMP_TYPE_LS2_UL, -} DecompTypeEnum; - -static uint32_t unicode_get_short_code(uint32_t c) -{ - static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; - - if (c < 0x80) - return c; - else if (c < 0x80 + 0x50) - return c - 0x80 + 0x300; - else - return unicode_short_table[c - 0x80 - 0x50]; -} - -static uint32_t unicode_get_lower_simple(uint32_t c) -{ - if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) - c += 0x20; - else - c++; - return c; -} - -static uint16_t unicode_get16(const uint8_t *p) -{ - return p[0] | (p[1] << 8); -} - -static int unicode_decomp_entry(uint32_t *res, uint32_t c, - int idx, uint32_t code, uint32_t len, - uint32_t type) -{ - uint32_t c1; - int l, i, p; - const uint8_t *d; - - if (type == DECOMP_TYPE_C1) { - res[0] = unicode_decomp_table2[idx]; - return 1; - } else { - d = unicode_decomp_data + unicode_decomp_table2[idx]; - switch(type) { - case DECOMP_TYPE_L1: - case DECOMP_TYPE_L2: - case DECOMP_TYPE_L3: - case DECOMP_TYPE_L4: - case DECOMP_TYPE_L5: - case DECOMP_TYPE_L6: - case DECOMP_TYPE_L7: - l = type - DECOMP_TYPE_L1 + 1; - d += (c - code) * l * 2; - for(i = 0; i < l; i++) { - if ((res[i] = unicode_get16(d + 2 * i)) == 0) - return 0; - } - return l; - case DECOMP_TYPE_LL1: - case DECOMP_TYPE_LL2: - { - uint32_t k, p; - l = type - DECOMP_TYPE_LL1 + 1; - k = (c - code) * l; - p = len * l * 2; - for(i = 0; i < l; i++) { - c1 = unicode_get16(d + 2 * k) | - (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); - if (!c1) - return 0; - res[i] = c1; - k++; - } - } - return l; - case DECOMP_TYPE_S1: - case DECOMP_TYPE_S2: - case DECOMP_TYPE_S3: - case DECOMP_TYPE_S4: - case DECOMP_TYPE_S5: - l = type - DECOMP_TYPE_S1 + 1; - d += (c - code) * l; - for(i = 0; i < l; i++) { - if ((res[i] = unicode_get_short_code(d[i])) == 0) - return 0; - } - return l; - case DECOMP_TYPE_I1: - l = 1; - p = 0; - goto decomp_type_i; - case DECOMP_TYPE_I2_0: - case DECOMP_TYPE_I2_1: - case DECOMP_TYPE_I3_1: - case DECOMP_TYPE_I3_2: - case DECOMP_TYPE_I4_1: - case DECOMP_TYPE_I4_2: - l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); - p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); - decomp_type_i: - for(i = 0; i < l; i++) { - c1 = unicode_get16(d + 2 * i); - if (i == p) - c1 += c - code; - res[i] = c1; - } - return l; - case DECOMP_TYPE_B18: - l = 18; - goto decomp_type_b; - case DECOMP_TYPE_B1: - case DECOMP_TYPE_B2: - case DECOMP_TYPE_B3: - case DECOMP_TYPE_B4: - case DECOMP_TYPE_B5: - case DECOMP_TYPE_B6: - case DECOMP_TYPE_B7: - case DECOMP_TYPE_B8: - l = type - DECOMP_TYPE_B1 + 1; - decomp_type_b: - { - uint32_t c_min; - c_min = unicode_get16(d); - d += 2 + (c - code) * l; - for(i = 0; i < l; i++) { - c1 = d[i]; - if (c1 == 0xff) - c1 = 0x20; - else - c1 += c_min; - res[i] = c1; - } - } - return l; - case DECOMP_TYPE_LS2: - d += (c - code) * 3; - if (!(res[0] = unicode_get16(d))) - return 0; - res[1] = unicode_get_short_code(d[2]); - return 2; - case DECOMP_TYPE_PAT3: - res[0] = unicode_get16(d); - res[2] = unicode_get16(d + 2); - d += 4 + (c - code) * 2; - res[1] = unicode_get16(d); - return 3; - case DECOMP_TYPE_S2_UL: - case DECOMP_TYPE_LS2_UL: - c1 = c - code; - if (type == DECOMP_TYPE_S2_UL) { - d += c1 & ~1; - c = unicode_get_short_code(*d); - d++; - } else { - d += (c1 >> 1) * 3; - c = unicode_get16(d); - d += 2; - } - if (c1 & 1) - c = unicode_get_lower_simple(c); - res[0] = c; - res[1] = unicode_get_short_code(*d); - return 2; - } - } - return 0; -} - - -/* return the length of the decomposition (length <= - UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ -static int unicode_decomp_char(uint32_t *res, uint32_t c, bool is_compat1) -{ - uint32_t v, type, is_compat, code, len; - int idx_min, idx_max, idx; - - idx_min = 0; - idx_max = countof(unicode_decomp_table1) - 1; - while (idx_min <= idx_max) { - idx = (idx_max + idx_min) / 2; - v = unicode_decomp_table1[idx]; - code = v >> (32 - 18); - len = (v >> (32 - 18 - 7)) & 0x7f; - // printf("idx=%d code=%05x len=%d\n", idx, code, len); - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - is_compat = v & 1; - if (is_compat1 < is_compat) - break; - type = (v >> (32 - 18 - 7 - 6)) & 0x3f; - return unicode_decomp_entry(res, c, idx, code, len, type); - } - } - return 0; -} - -/* return 0 if no pair found */ -static int unicode_compose_pair(uint32_t c0, uint32_t c1) -{ - uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; - int idx_min, idx_max, idx, d; - uint32_t pair[2]; - - idx_min = 0; - idx_max = countof(unicode_comp_table) - 1; - while (idx_min <= idx_max) { - idx = (idx_max + idx_min) / 2; - idx1 = unicode_comp_table[idx]; - - /* idx1 represent an entry of the decomposition table */ - d_idx = idx1 >> 6; - d_offset = idx1 & 0x3f; - v = unicode_decomp_table1[d_idx]; - code = v >> (32 - 18); - len = (v >> (32 - 18 - 7)) & 0x7f; - type = (v >> (32 - 18 - 7 - 6)) & 0x3f; - ch = code + d_offset; - unicode_decomp_entry(pair, ch, d_idx, code, len, type); - d = c0 - pair[0]; - if (d == 0) - d = c1 - pair[1]; - if (d < 0) { - idx_max = idx - 1; - } else if (d > 0) { - idx_min = idx + 1; - } else { - return ch; - } - } - return 0; -} - -/* return the combining class of character c (between 0 and 255) */ -static int unicode_get_cc(uint32_t c) -{ - uint32_t code, n, type, cc, c1, b; - int pos; - const uint8_t *p; - - pos = get_index_pos(&code, c, - unicode_cc_index, sizeof(unicode_cc_index) / 3); - if (pos < 0) - return 0; - p = unicode_cc_table + pos; - for(;;) { - b = *p++; - type = b >> 6; - n = b & 0x3f; - if (n < 48) { - } else if (n < 56) { - n = (n - 48) << 8; - n |= *p++; - n += 48; - } else { - n = (n - 56) << 8; - n |= *p++ << 8; - n |= *p++; - n += 48 + (1 << 11); - } - if (type <= 1) - p++; - c1 = code + n + 1; - if (c < c1) { - switch(type) { - case 0: - cc = p[-1]; - break; - case 1: - cc = p[-1] + c - code; - break; - case 2: - cc = 0; - break; - default: - case 3: - cc = 230; - break; - } - return cc; - } - code = c1; - } -} - -static void sort_cc(int *buf, int len) -{ - int i, j, k, cc, cc1, start, ch1; - - for(i = 0; i < len; i++) { - cc = unicode_get_cc(buf[i]); - if (cc != 0) { - start = i; - j = i + 1; - while (j < len) { - ch1 = buf[j]; - cc1 = unicode_get_cc(ch1); - if (cc1 == 0) - break; - k = j - 1; - while (k >= start) { - if (unicode_get_cc(buf[k]) <= cc1) - break; - buf[k + 1] = buf[k]; - k--; - } - buf[k + 1] = ch1; - j++; - } - i = j; - } - } -} - -static void to_nfd_rec(DynBuf *dbuf, - const int *src, int src_len, int is_compat) -{ - uint32_t c, v; - int i, l; - uint32_t res[UNICODE_DECOMP_LEN_MAX]; - - for(i = 0; i < src_len; i++) { - c = src[i]; - if (c >= 0xac00 && c < 0xd7a4) { - /* Hangul decomposition */ - c -= 0xac00; - dbuf_put_u32(dbuf, 0x1100 + c / 588); - dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); - v = c % 28; - if (v != 0) - dbuf_put_u32(dbuf, 0x11a7 + v); - } else { - l = unicode_decomp_char(res, c, is_compat); - if (l) { - to_nfd_rec(dbuf, (int *)res, l, is_compat); - } else { - dbuf_put_u32(dbuf, c); - } - } - } -} - -/* return 0 if not found */ -static int compose_pair(uint32_t c0, uint32_t c1) -{ - /* Hangul composition */ - if (c0 >= 0x1100 && c0 < 0x1100 + 19 && - c1 >= 0x1161 && c1 < 0x1161 + 21) { - return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; - } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && - (c0 - 0xac00) % 28 == 0 && - c1 >= 0x11a7 && c1 < 0x11a7 + 28) { - return c0 + c1 - 0x11a7; - } else { - return unicode_compose_pair(c0, c1); - } -} - -int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, - UnicodeNormalizationEnum n_type, - void *opaque, DynBufReallocFunc *realloc_func) -{ - int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; - bool is_compat; - DynBuf dbuf_s, *dbuf = &dbuf_s; - - is_compat = n_type >> 1; - - dbuf_init2(dbuf, opaque, realloc_func); - if (dbuf_realloc(dbuf, sizeof(int) * src_len)) - goto fail; - - /* common case: latin1 is unaffected by NFC */ - if (n_type == UNICODE_NFC) { - for(i = 0; i < src_len; i++) { - if (src[i] >= 0x100) - goto not_latin1; - } - buf = (int *)dbuf->buf; - memcpy(buf, src, src_len * sizeof(int)); - *pdst = (uint32_t *)buf; - return src_len; - not_latin1: ; - } - - to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); - if (dbuf_error(dbuf)) { - fail: - *pdst = NULL; - return -1; - } - buf = (int *)dbuf->buf; - buf_len = dbuf->size / sizeof(int); - - sort_cc(buf, buf_len); - - if (buf_len <= 1 || (n_type & 1) != 0) { - /* NFD / NFKD */ - *pdst = (uint32_t *)buf; - return buf_len; - } - - i = 1; - out_len = 1; - while (i < buf_len) { - /* find the starter character and test if it is blocked from - the character at 'i' */ - last_cc = unicode_get_cc(buf[i]); - starter_pos = out_len - 1; - while (starter_pos >= 0) { - cc = unicode_get_cc(buf[starter_pos]); - if (cc == 0) - break; - if (cc >= last_cc) - goto next; - last_cc = 256; - starter_pos--; - } - if (starter_pos >= 0 && - (p = compose_pair(buf[starter_pos], buf[i])) != 0) { - buf[starter_pos] = p; - i++; - } else { - next: - buf[out_len++] = buf[i++]; - } - } - *pdst = (uint32_t *)buf; - return out_len; -} - -/* char ranges for various unicode properties */ - -static int unicode_find_name(const char *name_table, const char *name) -{ - const char *p, *r; - int pos; - size_t name_len, len; - - p = name_table; - pos = 0; - name_len = strlen(name); - while (*p) { - for(;;) { - r = strchr(p, ','); - if (!r) - len = strlen(p); - else - len = r - p; - if (len == name_len && !memcmp(p, name, name_len)) - return pos; - p += len + 1; - if (!r) - break; - } - pos++; - } - return -1; -} - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_script(CharRange *cr, - const char *script_name, bool is_ext) -{ - int script_idx; - const uint8_t *p, *p_end; - uint32_t c, c1, b, n, v, v_len, i, type; - CharRange cr1_s = { 0 }, *cr1 = NULL; - CharRange cr2_s = { 0 }, *cr2 = &cr2_s; - bool is_common; - - script_idx = unicode_find_name(unicode_script_name_table, script_name); - if (script_idx < 0) - return -2; - /* Note: we remove the "Unknown" Script */ - script_idx += UNICODE_SCRIPT_Unknown + 1; - - is_common = (script_idx == UNICODE_SCRIPT_Common || - script_idx == UNICODE_SCRIPT_Inherited); - if (is_ext) { - cr1 = &cr1_s; - cr_init(cr1, cr->mem_opaque, cr->realloc_func); - cr_init(cr2, cr->mem_opaque, cr->realloc_func); - } else { - cr1 = cr; - } - - p = unicode_script_table; - p_end = unicode_script_table + countof(unicode_script_table); - c = 0; - while (p < p_end) { - b = *p++; - type = b >> 7; - n = b & 0x7f; - if (n < 96) { - } else if (n < 112) { - n = (n - 96) << 8; - n |= *p++; - n += 96; - } else { - n = (n - 112) << 16; - n |= *p++ << 8; - n |= *p++; - n += 96 + (1 << 12); - } - if (type == 0) - v = 0; - else - v = *p++; - c1 = c + n + 1; - if (v == script_idx) { - if (cr_add_interval(cr1, c, c1)) - goto fail; - } - c = c1; - } - - if (is_ext) { - /* add the script extensions */ - p = unicode_script_ext_table; - p_end = unicode_script_ext_table + countof(unicode_script_ext_table); - c = 0; - while (p < p_end) { - b = *p++; - if (b < 128) { - n = b; - } else if (b < 128 + 64) { - n = (b - 128) << 8; - n |= *p++; - n += 128; - } else { - n = (b - 128 - 64) << 16; - n |= *p++ << 8; - n |= *p++; - n += 128 + (1 << 14); - } - c1 = c + n + 1; - v_len = *p++; - if (is_common) { - if (v_len != 0) { - if (cr_add_interval(cr2, c, c1)) - goto fail; - } - } else { - for(i = 0; i < v_len; i++) { - if (p[i] == script_idx) { - if (cr_add_interval(cr2, c, c1)) - goto fail; - break; - } - } - } - p += v_len; - c = c1; - } - if (is_common) { - /* remove all the characters with script extensions */ - if (cr_invert(cr2)) - goto fail; - if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, - CR_OP_INTER)) - goto fail; - } else { - if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, - CR_OP_UNION)) - goto fail; - } - cr_free(cr1); - cr_free(cr2); - } - return 0; - fail: - if (is_ext) { - cr_free(cr1); - cr_free(cr2); - } - goto fail; -} - -#define M(id) (1U << UNICODE_GC_ ## id) - -static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) -{ - const uint8_t *p, *p_end; - uint32_t c, c0, b, n, v; - - p = unicode_gc_table; - p_end = unicode_gc_table + countof(unicode_gc_table); - c = 0; - while (p < p_end) { - b = *p++; - n = b >> 5; - v = b & 0x1f; - if (n == 7) { - n = *p++; - if (n < 128) { - n += 7; - } else if (n < 128 + 64) { - n = (n - 128) << 8; - n |= *p++; - n += 7 + 128; - } else { - n = (n - 128 - 64) << 16; - n |= *p++ << 8; - n |= *p++; - n += 7 + 128 + (1 << 14); - } - } - c0 = c; - c += n + 1; - if (v == 31) { - /* run of Lu / Ll */ - b = gc_mask & (M(Lu) | M(Ll)); - if (b != 0) { - if (b == (M(Lu) | M(Ll))) { - goto add_range; - } else { - c0 += ((gc_mask & M(Ll)) != 0); - for(; c0 < c; c0 += 2) { - if (cr_add_interval(cr, c0, c0 + 1)) - return -1; - } - } - } - } else if ((gc_mask >> v) & 1) { - add_range: - if (cr_add_interval(cr, c0, c)) - return -1; - } - } - return 0; -} - -static int unicode_prop1(CharRange *cr, int prop_idx) -{ - const uint8_t *p, *p_end; - uint32_t c, c0, b, bit; - - p = unicode_prop_table[prop_idx]; - p_end = p + unicode_prop_len_table[prop_idx]; - c = 0; - bit = 0; - while (p < p_end) { - c0 = c; - b = *p++; - if (b < 64) { - c += (b >> 3) + 1; - if (bit) { - if (cr_add_interval(cr, c0, c)) - return -1; - } - bit ^= 1; - c0 = c; - c += (b & 7) + 1; - } else if (b >= 0x80) { - c += b - 0x80 + 1; - } else if (b < 0x60) { - c += (((b - 0x40) << 8) | p[0]) + 1; - p++; - } else { - c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; - p += 2; - } - if (bit) { - if (cr_add_interval(cr, c0, c)) - return -1; - } - bit ^= 1; - } - return 0; -} - -#define CASE_U (1 << 0) -#define CASE_L (1 << 1) -#define CASE_F (1 << 2) - -/* use the case conversion table to generate range of characters. - CASE_U: set char if modified by uppercasing, - CASE_L: set char if modified by lowercasing, - CASE_F: set char if modified by case folding, - */ -static int unicode_case1(CharRange *cr, int case_mask) -{ -#define MR(x) (1 << RUN_TYPE_ ## x) - const uint32_t tab_run_mask[3] = { - MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | - MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3), - - MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2), - - MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3), - }; -#undef MR - uint32_t mask, v, code, type, len, i, idx; - - if (case_mask == 0) - return 0; - mask = 0; - for(i = 0; i < 3; i++) { - if ((case_mask >> i) & 1) - mask |= tab_run_mask[i]; - } - for(idx = 0; idx < countof(case_conv_table1); idx++) { - v = case_conv_table1[idx]; - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if ((mask >> type) & 1) { - // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); - switch(type) { - case RUN_TYPE_UL: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - code += ((case_mask & CASE_U) != 0); - for(i = 0; i < len; i += 2) { - if (cr_add_interval(cr, code + i, code + i + 1)) - return -1; - } - break; - case RUN_TYPE_LSU: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - if (!(case_mask & CASE_U)) { - if (cr_add_interval(cr, code, code + 1)) - return -1; - } - if (cr_add_interval(cr, code + 1, code + 2)) - return -1; - if (case_mask & CASE_U) { - if (cr_add_interval(cr, code + 2, code + 3)) - return -1; - } - break; - default: - def_case: - if (cr_add_interval(cr, code, code + len)) - return -1; - break; - } - } - } - return 0; -} - -static int point_cmp(const void *p1, const void *p2, void *arg) -{ - uint32_t v1 = *(uint32_t *)p1; - uint32_t v2 = *(uint32_t *)p2; - return (v1 > v2) - (v1 < v2); -} - -static void cr_sort_and_remove_overlap(CharRange *cr) -{ - uint32_t start, end, start1, end1, i, j; - - /* the resulting ranges are not necessarily sorted and may overlap */ - rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); - j = 0; - for(i = 0; i < cr->len; ) { - start = cr->points[i]; - end = cr->points[i + 1]; - i += 2; - while (i < cr->len) { - start1 = cr->points[i]; - end1 = cr->points[i + 1]; - if (start1 > end) { - /* |------| - * |-------| */ - break; - } else if (end1 <= end) { - /* |------| - * |--| */ - i += 2; - } else { - /* |------| - * |-------| */ - end = end1; - i += 2; - } - } - cr->points[j] = start; - cr->points[j + 1] = end; - j += 2; - } - cr->len = j; -} - -/* canonicalize a character set using the JS regex case folding rules - (see lre_canonicalize()) */ -int cr_regexp_canonicalize(CharRange *cr, bool is_unicode) -{ - CharRange cr_inter, cr_mask, cr_result, cr_sub; - uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; - - cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func); - cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func); - cr_init(&cr_result, cr->mem_opaque, cr->realloc_func); - cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func); - - if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U)) - goto fail; - if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) - goto fail; - - if (cr_invert(&cr_mask)) - goto fail; - if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) - goto fail; - - /* cr_inter = cr & cr_mask */ - /* cr_sub = cr & ~cr_mask */ - - /* use the case conversion table to compute the result */ - d_start = -1; - d_end = -1; - idx = 0; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - for(i = 0; i < cr_inter.len; i += 2) { - start = cr_inter.points[i]; - end = cr_inter.points[i + 1]; - - for(c = start; c < end; c++) { - for(;;) { - if (c >= code && c < code + len) - break; - idx++; - assert(idx < countof(case_conv_table1)); - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - } - d = lre_case_folding_entry(c, idx, v, is_unicode); - /* try to merge with the current interval */ - if (d_start == -1) { - d_start = d; - d_end = d + 1; - } else if (d_end == d) { - d_end++; - } else { - cr_add_interval(&cr_result, d_start, d_end); - d_start = d; - d_end = d + 1; - } - } - } - if (d_start != -1) { - if (cr_add_interval(&cr_result, d_start, d_end)) - goto fail; - } - - /* the resulting ranges are not necessarily sorted and may overlap */ - cr_sort_and_remove_overlap(&cr_result); - - /* or with the character not affected by the case folding */ - cr->len = 0; - if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION)) - goto fail; - - cr_free(&cr_inter); - cr_free(&cr_mask); - cr_free(&cr_result); - cr_free(&cr_sub); - return 0; - fail: - cr_free(&cr_inter); - cr_free(&cr_mask); - cr_free(&cr_result); - cr_free(&cr_sub); - return -1; -} - -typedef enum { - POP_GC, - POP_PROP, - POP_CASE, - POP_UNION, - POP_INTER, - POP_XOR, - POP_INVERT, - POP_END, -} PropOPEnum; - -#define POP_STACK_LEN_MAX 4 - -static int unicode_prop_ops(CharRange *cr, ...) -{ - va_list ap; - CharRange stack[POP_STACK_LEN_MAX]; - int stack_len, op, ret, i; - uint32_t a; - - va_start(ap, cr); - stack_len = 0; - for(;;) { - op = va_arg(ap, int); - switch(op) { - case POP_GC: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_general_category1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_PROP: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_prop1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_CASE: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_case1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_UNION: - case POP_INTER: - case POP_XOR: - { - CharRange *cr1, *cr2, *cr3; - assert(stack_len >= 2); - assert(stack_len < POP_STACK_LEN_MAX); - cr1 = &stack[stack_len - 2]; - cr2 = &stack[stack_len - 1]; - cr3 = &stack[stack_len++]; - cr_init(cr3, cr->mem_opaque, cr->realloc_func); - if (cr_op(cr3, cr1->points, cr1->len, - cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) - goto fail; - cr_free(cr1); - cr_free(cr2); - *cr1 = *cr3; - stack_len -= 2; - } - break; - case POP_INVERT: - assert(stack_len >= 1); - if (cr_invert(&stack[stack_len - 1])) - goto fail; - break; - case POP_END: - goto done; - default: - abort(); - } - } - done: - va_end(ap); - assert(stack_len == 1); - ret = cr_copy(cr, &stack[0]); - cr_free(&stack[0]); - return ret; - fail: - va_end(ap); - for(i = 0; i < stack_len; i++) - cr_free(&stack[i]); - return -1; -} - -static const uint32_t unicode_gc_mask_table[] = { - M(Lu) | M(Ll) | M(Lt), /* LC */ - M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ - M(Mn) | M(Mc) | M(Me), /* M */ - M(Nd) | M(Nl) | M(No), /* N */ - M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ - M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ - M(Zs) | M(Zl) | M(Zp), /* Z */ - M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ -}; - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_general_category(CharRange *cr, const char *gc_name) -{ - int gc_idx; - uint32_t gc_mask; - - gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); - if (gc_idx < 0) - return -2; - if (gc_idx <= UNICODE_GC_Co) { - gc_mask = (uint64_t)1 << gc_idx; - } else { - gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; - } - return unicode_general_category1(cr, gc_mask); -} - - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_prop(CharRange *cr, const char *prop_name) -{ - int prop_idx, ret; - - prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); - if (prop_idx < 0) - return -2; - prop_idx += UNICODE_PROP_ASCII_Hex_Digit; - - ret = 0; - switch(prop_idx) { - case UNICODE_PROP_ASCII: - if (cr_add_interval(cr, 0x00, 0x7f + 1)) - return -1; - break; - case UNICODE_PROP_Any: - if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) - return -1; - break; - case UNICODE_PROP_Assigned: - ret = unicode_prop_ops(cr, - POP_GC, M(Cn), - POP_INVERT, - POP_END); - break; - case UNICODE_PROP_Math: - ret = unicode_prop_ops(cr, - POP_GC, M(Sm), - POP_PROP, UNICODE_PROP_Other_Math, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Lowercase: - ret = unicode_prop_ops(cr, - POP_GC, M(Ll), - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Uppercase: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Cased: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Alphabetic: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Alphabetic, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Grapheme_Base: - ret = unicode_prop_ops(cr, - POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), - POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, - POP_UNION, - POP_INVERT, - POP_END); - break; - case UNICODE_PROP_Grapheme_Extend: - ret = unicode_prop_ops(cr, - POP_GC, M(Me) | M(Mn), - POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_XID_Start: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_PROP, UNICODE_PROP_XID_Start1, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_XID_Continue: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | - M(Mn) | M(Mc) | M(Nd) | M(Pc), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_ID_Continue, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_PROP, UNICODE_PROP_XID_Continue1, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_Changes_When_Uppercased: - ret = unicode_case1(cr, CASE_U); - break; - case UNICODE_PROP_Changes_When_Lowercased: - ret = unicode_case1(cr, CASE_L); - break; - case UNICODE_PROP_Changes_When_Casemapped: - ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); - break; - case UNICODE_PROP_Changes_When_Titlecased: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_U, - POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, - POP_XOR, - POP_END); - break; - case UNICODE_PROP_Changes_When_Casefolded: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_F, - POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, - POP_XOR, - POP_END); - break; - case UNICODE_PROP_Changes_When_NFKC_Casefolded: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_F, - POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, - POP_XOR, - POP_END); - break; - /* we use the existing tables */ - case UNICODE_PROP_ID_Continue: - ret = unicode_prop_ops(cr, - POP_PROP, UNICODE_PROP_ID_Start, - POP_PROP, UNICODE_PROP_ID_Continue1, - POP_XOR, - POP_END); - break; - default: - if (prop_idx >= countof(unicode_prop_table)) - return -2; - ret = unicode_prop1(cr, prop_idx); - break; - } - return ret; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode.h deleted file mode 100755 index 8e6f2a01..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/libunicode.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/list.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/list.h deleted file mode 100755 index b8dd7168..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/list.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/meson.build b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/meson.build deleted file mode 100755 index 1c178513..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/meson.build +++ /dev/null @@ -1,635 +0,0 @@ -project( - 'quickjs-ng', - 'c', - version: '0.11.0', - default_options: [ - 'c_std=gnu11,c11', - 'warning_level=3', - 'default_library=static', - ], - license: 'MIT', - license_files: 'LICENSE', - meson_version: '>=1.3.0', -) - -host_system = host_machine.system() -cc = meson.get_compiler('c') - -qjs_gcc_warning_args = [ - '-Wno-unsafe-buffer-usage', - '-Wno-sign-conversion', - '-Wno-nonportable-system-include-path', - '-Wno-implicit-int-conversion', - '-Wno-shorten-64-to-32', - '-Wno-reserved-macro-identifier', - '-Wno-reserved-identifier', - '-Wdeprecated-declarations', - - '-Wno-implicit-fallthrough', - '-Wno-sign-compare', - '-Wno-missing-field-initializers', - '-Wno-unused-parameter', - '-Wno-unused-but-set-variable', - '-Wno-array-bounds', - '-Wno-format-truncation', -] -qjs_gcc_args = [ - '-funsigned-char', -] - -if host_system == 'darwin' - # https://github.com/quickjs-ng/quickjs/issues/453 - qjs_gcc_warning_args += '-Wno-maybe-uninitialized' -endif - -# https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings -qjs_msvc_warning_args = [ - '/wd4018', # -Wno-sign-conversion - '/wd4061', # -Wno-implicit-fallthrough - '/wd4100', # -Wno-unused-parameter - '/wd4200', # -Wno-zero-length-array - '/wd4242', # -Wno-shorten-64-to-32 - '/wd4244', # -Wno-shorten-64-to-32 - '/wd4245', # -Wno-sign-compare - '/wd4267', # -Wno-shorten-64-to-32 - '/wd4388', # -Wno-sign-compare - '/wd4389', # -Wno-sign-compare - '/wd4710', # Function not inlined - '/wd4711', # Function was inlined - '/wd4820', # Padding added after construct - '/wd4996', # -Wdeprecated-declarations - '/wd5045', # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified -] -qjs_msvc_args = [ - '/experimental:c11atomics', - '/J', # -funsigned-char -] - -if cc.get_argument_syntax() == 'msvc' - add_project_arguments( - cc.get_supported_arguments(qjs_msvc_warning_args), - cc.get_id().contains('clang') ? cc.get_supported_arguments(qjs_gcc_warning_args) : [], - qjs_msvc_args, - language: 'c', - ) -else - add_project_arguments( - cc.get_supported_arguments(qjs_gcc_warning_args), - qjs_gcc_args, - language: 'c', - ) -endif - -if host_system == 'windows' - # Set a 8MB default stack size on Windows. - # It defaults to 1MB on MSVC, which is the same as our current JS stack size, - # so it will overflow and crash otherwise. - # On MinGW it defaults to 2MB. - stack_size = 8 * 1024 * 1024 - if cc.get_argument_syntax() == 'msvc' - add_project_link_arguments(f'/STACK:@stack_size@', language: 'c') - else - add_project_link_arguments(f'-Wl,--stack,@stack_size@', language: 'c') - endif -endif - -if meson.is_cross_build() - native_cc = meson.get_compiler('c', native: true) - - if native_cc.get_argument_syntax() == 'msvc' - # https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings - add_project_arguments( - native_cc.get_supported_arguments(qjs_msvc_warning_args), - native_cc.get_id().contains('clang') ? native_cc.get_supported_arguments(qjs_gcc_warning_args) : [], - qjs_msvc_args, - - language: 'c', - native: true, - ) - else - add_project_arguments( - native_cc.get_supported_arguments(qjs_gcc_warning_args), - qjs_gcc_args, - - language: 'c', - native: true, - ) - endif -endif -if get_option('debug') - add_project_arguments( - cc.get_supported_arguments('-fno-omit-frame-pointer'), - language: 'c', - ) -endif - -qjs_sys_deps = [] - -m_dep = cc.find_library('m', required: false) -qjs_sys_deps += m_dep -qjs_sys_deps += dependency('threads', required: false) -qjs_sys_deps += dependency('dl', required: false) - -qjs_srcs = files( - 'cutils.c', - 'dtoa.c', - 'libregexp.c', - 'libunicode.c', - 'quickjs.c', -) -qjs_hdrs = files( - 'quickjs.h', -) - -qjs_libc = get_option('libc') -qjs_libc_srcs = files('quickjs-libc.c') -qjs_libc_hdrs = files('quickjs-libc.h') - -if qjs_libc - qjs_hdrs += qjs_libc_hdrs -endif - -qjs_parser = get_option('parser') - -qjs_c_args = ['-D_GNU_SOURCE'] - -if host_system == 'windows' - qjs_c_args += ['-DWIN32_LEAN_AND_MEAN', '-D_WIN32_WINNT=0x0601'] -endif - -if not qjs_parser - qjs_c_args += ['-DQJS_DISABLE_PARSER'] -endif - -qjs_libc_lib = static_library( - 'quickjs-libc', - qjs_libc_srcs, - - dependencies: qjs_sys_deps, - c_args: qjs_c_args, - gnu_symbol_visibility: 'hidden', -) - -qjs_lib = library( - 'qjs', - qjs_srcs, - - # export public headers - generator( - find_program('cp', 'xcopy'), - output: ['@PLAINNAME@'], - arguments: ['@INPUT@', '@OUTPUT@'], - ).process(qjs_hdrs), - - dependencies: qjs_sys_deps, - link_whole: qjs_libc ? qjs_libc_lib : [], - c_args: qjs_c_args, - gnu_symbol_visibility: 'hidden', - - install: true, - version: meson.project_version(), -) - -qjs_export_variables = [ - f'have_parser=@qjs_parser@' -] - -qjs_dep = declare_dependency( - link_with: qjs_lib, - dependencies: qjs_sys_deps, - include_directories: qjs_lib.private_dir_include(), - variables: qjs_export_variables, -) - -if host_system == 'emscripten' - qjs_wasm_export_name = 'getQuickJs' - executable( - 'qjs_wasm', - qjs_srcs, - link_args: cc.get_supported_link_arguments( - # in emscripten 3.x, this will be set to 16k which is too small for quickjs. - '-sSTACK_SIZE=@0@'.format(2 * 1024 * 1024), # let it be 2m = 2 * 1024 * 1024, otherwise, stack overflow may be occured at bootstrap - '-sNO_INVOKE_RUN', - '-sNO_EXIT_RUNTIME', - '-sMODULARIZE', # do not mess the global - '-sEXPORT_ES6', # export js file to morden es module - '-sEXPORT_NAME=@0@'.format(qjs_wasm_export_name), # give a name - '-sTEXTDECODER=1', # it will be 2 if we use -Oz, and that will cause js -> c string convertion fail - '-sNO_DEFAULT_TO_CXX', # this project is pure c project, no need for c plus plus handle - '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap', - ), - dependencies: m_dep, - c_args: qjs_c_args, - ) -endif - -install_headers(qjs_hdrs, subdir: 'quickjs-ng') - -if not meson.is_subproject() - docdir = get_option('docdir') - datadir = get_option('datadir') - - if docdir == '' - docdir = datadir / 'doc' / meson.project_name() - endif - install_data( - 'LICENSE', - install_dir: docdir, - - install_tag: 'doc', - ) - install_subdir( - 'examples', - install_dir: docdir, - - strip_directory: false, - install_tag: 'doc', - ) -endif - -meson.override_dependency('quickjs-ng', qjs_dep) - -import('pkgconfig').generate( - qjs_lib, - subdirs: 'quickjs-ng', - name: 'quickjs-ng', - description: 'QuickJS, the Next Generation: a mighty JavaScript engine', - url: 'https://github.com/quickjs-ng/quickjs', - version: meson.project_version(), - variables: qjs_export_variables, -) - -if not qjs_parser - subdir_done() -endif - -# QuickJS bytecode compiler -qjsc_srcs = files( - 'qjsc.c', -) -qjsc_exe = executable( - 'qjsc', - qjsc_srcs, - - c_args: qjs_c_args, - link_with: qjs_libc ? [] : qjs_libc_lib, - dependencies: qjs_dep, - - install: true, -) - -mimalloc_dep = [] -mimalloc_sys_dep = dependency('mimalloc', required: get_option('cli_mimalloc')) -if mimalloc_sys_dep.found() - mimalloc_dep = declare_dependency( - dependencies: mimalloc_sys_dep, - compile_args: '-DQJS_USE_MIMALLOC', - ) -endif - -# QuickJS CLI -qjs_exe_srcs = files( - 'gen/repl.c', - 'gen/standalone.c', - 'qjs.c', -) -qjs_exe = executable( - 'qjs', - qjs_exe_srcs, - - c_args: qjs_c_args, - link_with: qjs_libc ? [] : qjs_libc_lib, - dependencies: [qjs_dep, mimalloc_dep], - export_dynamic: true, - - install: true, -) - -if meson.is_cross_build() - mimalloc_native_dep = [] - mimalloc_sys_native_dep = dependency('mimalloc', required: get_option('cli_mimalloc'), native: true) - if mimalloc_sys_dep.found() - mimalloc_native_dep = declare_dependency( - dependencies: mimalloc_sys_native_dep, - compile_args: '-DQJS_USE_MIMALLOC', - ) - endif - - qjs_sys_native_deps = [ - native_cc.find_library('m', required: false), - dependency('threads', required: false, native: true), - dependency('dl', required: false, native: true), - ] - qjs_native_lib = static_library( - 'qjs_native', - qjs_srcs, - qjs_libc_srcs, - - dependencies: qjs_sys_native_deps, - c_args: qjs_c_args, - gnu_symbol_visibility: 'hidden', - - build_by_default: false, - native: true, - install: false, - ) - - meson.override_find_program( - 'qjsc', - executable( - 'qjsc_native', - qjsc_srcs, - - c_args: qjs_c_args, - link_with: qjs_native_lib, - dependencies: qjs_sys_native_deps, - - build_by_default: false, - native: true, - install: false, - ), - ) - meson.override_find_program( - 'qjs', - executable( - 'qjs_native', - qjs_exe_srcs, - - c_args: qjs_c_args, - link_with: qjs_native_lib, - dependencies: [qjs_sys_native_deps, mimalloc_native_dep], - export_dynamic: true, - - build_by_default: false, - native: true, - install: false, - ), - ) -else - meson.override_find_program('qjsc', qjsc_exe) - meson.override_find_program('qjs', qjs_exe) -endif - -tests = get_option('tests').disable_auto_if(meson.is_subproject()) -examples = get_option('examples').disable_auto_if(meson.is_subproject()) - -if tests.allowed() - if host_system != 'emscripten' - # Test262 runner - run262_exe = executable( - 'run-test262', - 'run-test262.c', - qjs_libc_srcs, - - c_args: qjs_c_args, - dependencies: qjs_dep, - ) - - test( - 'test', - run262_exe, - args: ['-c', files('tests.conf')], - workdir: meson.current_source_dir(), - ) - - foreach bench : [ - 'empty_loop', - - 'date_now', - - 'prop_read', - 'prop_write', - 'prop_create', - 'prop_delete', - - 'array_read', - 'array_write', - 'array_prop_create', - 'array_length_decr', - 'array_hole_length_decr', - 'array_push', - 'array_pop', - - 'typed_array_read', - 'typed_array_write', - - 'global_read', - 'global_write', - 'global_write_strict', - - 'local_destruct', - 'global_destruct', - 'global_destruct_strict', - - 'func_call', - 'closure_var', - - 'int_arith', - 'float_arith', - - 'map_set', - 'map_delete', - 'weak_map_set', - 'weak_map_delete', - - 'array_for', - 'array_for_in', - 'array_for_of', - - 'math_min', - - 'object_null', - - 'regexp_ascii', - 'regexp_utf16', - - 'string_build1', - 'string_build2', - - 'string_slice1', - 'string_slice2', - 'string_slice3', - - 'sort_bench', - - 'int_to_string', - 'int_toString', - - 'float_to_string', - 'float_toString', - 'float_toFixed', - 'float_toPrecision', - 'float_toExponential', - - 'string_to_int', - 'string_to_float', - - 'bigint64_arith', - 'bigint256_arith', - ] - benchmark( - bench, - qjs_exe, - args: [files('tests/microbench.js'), bench], - ) - endforeach - endif - - # API test - test( - 'api', - executable( - 'api-test', - 'api-test.c', - - c_args: qjs_c_args, - dependencies: qjs_dep, - build_by_default: false, - ), - ) - - # Function.toString() test - test( - 'function_source', - executable( - 'function_source', - 'gen/function_source.c', - qjs_libc_srcs, - - c_args: qjs_c_args, - dependencies: qjs_dep, - build_by_default: false, - ), - ) -endif - -# Unicode generator -unicode_gen = executable( - 'unicode_gen', - 'cutils.c', - 'unicode_gen.c', - - c_args: qjs_c_args, - build_by_default: false, -) - -run_target( - 'libunicode-table.h', - command: [ - unicode_gen, - meson.current_source_dir() / 'unicode', - files('libunicode-table.h'), - ], -) - -# bytecode to c source code for builtin and examples -alias_target('codegen', - run_target( - 'codegen_repl.c', - command: [ - qjsc_exe, - '-ss', - '-o', files('gen/repl.c'), - '-m', - '-n', 'repl.js', - files('repl.js'), - ], - ), - run_target( - 'codegen_standalone.c', - command: [ - qjsc_exe, - '-ss', - '-o', files('gen/standalone.c'), - '-m', - '-n', 'standalone.js', - files('standalone.js'), - ], - ), - run_target( - 'codegen_function_source.c', - command: [ - qjsc_exe, - '-e', - '-o', files('gen/function_source.c'), - '-n', 'tests/function_source.js', - files('tests/function_source.js'), - ], - ), - run_target( - 'codegen_hello.c', - command: [ - qjsc_exe, - '-e', - '-o', files('gen/hello.c'), - '-n', 'examples/hello.js', - files('examples/hello.js'), - ], - ), - run_target( - 'codegen_hello_module.c', - command: [ - qjsc_exe, - '-e', - '-o', files('gen/hello_module.c'), - '-m', - '-n', 'examples/hello_module.js', - files('examples/hello_module.js'), - ], - ), - run_target( - 'codegen_test_fib.c', - command: [ - qjsc_exe, - '-e', - '-o', files('gen/test_fib.c'), - '-m', - '-n', 'examples/test_fib.js', - files('examples/test_fib.js'), - ], - ), - run_target( - 'codegen_builtin-array-fromasync.h', - command: [ - qjsc_exe, - '-C', - '-ss', - '-o', files('builtin-array-fromasync.h'), - '-n', 'builtin-array-fromasync.js', - files('builtin-array-fromasync.js'), - ], - ), -) - -if examples.allowed() - executable( - 'hello', - 'gen/hello.c', - qjs_libc_srcs, - - c_args: qjs_c_args, - dependencies: qjs_dep, - ) - - executable( - 'hello_module', - 'gen/hello_module.c', - qjs_libc_srcs, - - c_args: qjs_c_args, - dependencies: qjs_dep, - ) - - subdir('examples') - - executable( - 'test_fib', - 'examples/fib.c', - 'gen/test_fib.c', - qjs_libc_srcs, - - c_args: qjs_c_args, - dependencies: qjs_dep, - export_dynamic: true, - ) -endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/meson_options.txt b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/meson_options.txt deleted file mode 100755 index 20e661e1..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/meson_options.txt +++ /dev/null @@ -1,6 +0,0 @@ -option('tests', type: 'feature', description: 'build tests') -option('examples', type: 'feature', description: 'build examples') -option('libc', type: 'boolean', value: false, description: 'build qjs standard library modules as part of the library') -option('cli_mimalloc', type: 'feature', value: 'disabled', description: 'build qjs cli with mimalloc') -option('docdir', type: 'string', description: 'documentation directory') -option('parser', type: 'boolean', value: true, description: 'Enable JS source code parser') diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/qjs.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/qjs.c deleted file mode 100755 index 4aed6837..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/qjs.c +++ /dev/null @@ -1,748 +0,0 @@ -/* - * QuickJS stand alone interpreter - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "quickjs.h" -#include "quickjs-libc.h" - -#ifdef QJS_USE_MIMALLOC -#include -#endif - -extern const uint8_t qjsc_repl[]; -extern const uint32_t qjsc_repl_size; -extern const uint8_t qjsc_standalone[]; -extern const uint32_t qjsc_standalone_size; - -// Must match standalone.js -#define TRAILER_SIZE 12 -static const char trailer_magic[] = "quickjs2"; -static const int trailer_magic_size = sizeof(trailer_magic) - 1; -static const int trailer_size = TRAILER_SIZE; - -static int qjs__argc; -static char **qjs__argv; - - -static bool is_standalone(const char *exe) -{ - FILE *exe_f = fopen(exe, "rb"); - if (!exe_f) - return false; - if (fseek(exe_f, -trailer_size, SEEK_END) < 0) - goto fail; - uint8_t buf[TRAILER_SIZE]; - if (fread(buf, 1, trailer_size, exe_f) != trailer_size) - goto fail; - fclose(exe_f); - return !memcmp(buf, trailer_magic, trailer_magic_size); -fail: - fclose(exe_f); - return false; -} - -static JSValue load_standalone_module(JSContext *ctx) -{ - JSModuleDef *m; - JSValue obj, val; - obj = JS_ReadObject(ctx, qjsc_standalone, qjsc_standalone_size, JS_READ_OBJ_BYTECODE); - if (JS_IsException(obj)) - goto exception; - assert(JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE); - if (JS_ResolveModule(ctx, obj) < 0) { - JS_FreeValue(ctx, obj); - goto exception; - } - if (js_module_set_import_meta(ctx, obj, false, true) < 0) { - JS_FreeValue(ctx, obj); - goto exception; - } - val = JS_EvalFunction(ctx, JS_DupValue(ctx, obj)); - val = js_std_await(ctx, val); - - if (JS_IsException(val)) { - JS_FreeValue(ctx, obj); - exception: - js_std_dump_error(ctx); - exit(1); - } - JS_FreeValue(ctx, val); - - m = JS_VALUE_GET_PTR(obj); - JS_FreeValue(ctx, obj); - return JS_GetModuleNamespace(ctx, m); -} - -static int eval_buf(JSContext *ctx, const void *buf, int buf_len, - const char *filename, int eval_flags) -{ - bool use_realpath; - JSValue val; - int ret; - - if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { - /* for the modules, we compile then run to be able to set - import.meta */ - val = JS_Eval(ctx, buf, buf_len, filename, - eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); - if (!JS_IsException(val)) { - // ex. "" pr "/dev/stdin" - use_realpath = - !(*filename == '<' || !strncmp(filename, "/dev/", 5)); - if (js_module_set_import_meta(ctx, val, use_realpath, true) < 0) { - js_std_dump_error(ctx); - ret = -1; - goto end; - } - val = JS_EvalFunction(ctx, val); - } - val = js_std_await(ctx, val); - } else { - val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); - } - if (JS_IsException(val)) { - js_std_dump_error(ctx); - ret = -1; - } else { - ret = 0; - } -end: - JS_FreeValue(ctx, val); - return ret; -} - -static int eval_file(JSContext *ctx, const char *filename, int module) -{ - uint8_t *buf; - int ret, eval_flags; - size_t buf_len; - - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - perror(filename); - exit(1); - } - - if (module < 0) { - module = (js__has_suffix(filename, ".mjs") || - JS_DetectModule((const char *)buf, buf_len)); - } - if (module) - eval_flags = JS_EVAL_TYPE_MODULE; - else - eval_flags = JS_EVAL_TYPE_GLOBAL; - ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); - js_free(ctx, buf); - return ret; -} - -static int64_t parse_limit(const char *arg) { - char *p; - unsigned long unit = 1024; /* default to traditional KB */ - double d = strtod(arg, &p); - - if (p == arg) { - fprintf(stderr, "Invalid limit: %s\n", arg); - return -1; - } - - if (*p) { - switch (*p++) { - case 'b': case 'B': unit = 1UL << 0; break; - case 'k': case 'K': unit = 1UL << 10; break; /* IEC kibibytes */ - case 'm': case 'M': unit = 1UL << 20; break; /* IEC mebibytes */ - case 'g': case 'G': unit = 1UL << 30; break; /* IEC gigibytes */ - default: - fprintf(stderr, "Invalid limit: %s, unrecognized suffix, only k,m,g are allowed\n", arg); - return -1; - } - if (*p) { - fprintf(stderr, "Invalid limit: %s, only one suffix allowed\n", arg); - return -1; - } - } - - return (int64_t)(d * unit); -} - -static JSValue js_gc(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JS_RunGC(JS_GetRuntime(ctx)); - return JS_UNDEFINED; -} - -static JSValue js_navigator_get_userAgent(JSContext *ctx, JSValueConst this_val) -{ - char version[32]; - snprintf(version, sizeof(version), "quickjs-ng/%s", JS_GetVersion()); - return JS_NewString(ctx, version); -} - -static const JSCFunctionListEntry navigator_proto_funcs[] = { - JS_CGETSET_DEF2("userAgent", js_navigator_get_userAgent, NULL, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Navigator", JS_PROP_CONFIGURABLE), -}; - -static const JSCFunctionListEntry global_obj[] = { - JS_CFUNC_DEF("gc", 0, js_gc), -}; - -/* also used to initialize the worker context */ -static JSContext *JS_NewCustomContext(JSRuntime *rt) -{ - JSContext *ctx; - ctx = JS_NewContext(rt); - if (!ctx) - return NULL; - /* system modules */ - js_init_module_std(ctx, "qjs:std"); - js_init_module_os(ctx, "qjs:os"); - js_init_module_bjson(ctx, "qjs:bjson"); - - JSValue global = JS_GetGlobalObject(ctx); - JS_SetPropertyFunctionList(ctx, global, global_obj, countof(global_obj)); - JSValue args = JS_NewArray(ctx); - int i; - for(i = 0; i < qjs__argc; i++) { - JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, qjs__argv[i])); - } - JS_SetPropertyStr(ctx, global, "execArgv", args); - JS_SetPropertyStr(ctx, global, "argv0", JS_NewString(ctx, qjs__argv[0])); - JSValue navigator_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, navigator_proto, navigator_proto_funcs, countof(navigator_proto_funcs)); - JSValue navigator = JS_NewObjectProto(ctx, navigator_proto); - JS_DefinePropertyValueStr(ctx, global, "navigator", navigator, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE); - JS_FreeValue(ctx, global); - JS_FreeValue(ctx, navigator_proto); - - return ctx; -} - -struct trace_malloc_data { - uint8_t *base; -}; - -static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, - struct trace_malloc_data *dp) -{ - return ptr - dp->base; -} - -static void JS_PRINTF_FORMAT_ATTR(2, 3) js_trace_malloc_printf(void *opaque, JS_PRINTF_FORMAT const char *fmt, ...) -{ - va_list ap; - int c; - - va_start(ap, fmt); - while ((c = *fmt++) != '\0') { - if (c == '%') { - /* only handle %p and %zd */ - if (*fmt == 'p') { - uint8_t *ptr = va_arg(ap, void *); - if (ptr == NULL) { - printf("NULL"); - } else { - printf("H%+06lld.%zd", - js_trace_malloc_ptr_offset(ptr, opaque), - js__malloc_usable_size(ptr)); - } - fmt++; - continue; - } - if (fmt[0] == 'z' && fmt[1] == 'd') { - size_t sz = va_arg(ap, size_t); - printf("%zd", sz); - fmt += 2; - continue; - } - } - putc(c, stdout); - } - va_end(ap); -} - -static void js_trace_malloc_init(struct trace_malloc_data *s) -{ - free(s->base = malloc(8)); -} - -static void *js_trace_calloc(void *opaque, size_t count, size_t size) -{ - void *ptr; - ptr = calloc(count, size); - js_trace_malloc_printf(opaque, "C %zd %zd -> %p\n", count, size, ptr); - return ptr; -} - -static void *js_trace_malloc(void *opaque, size_t size) -{ - void *ptr; - ptr = malloc(size); - js_trace_malloc_printf(opaque, "A %zd -> %p\n", size, ptr); - return ptr; -} - -static void js_trace_free(void *opaque, void *ptr) -{ - if (!ptr) - return; - js_trace_malloc_printf(opaque, "F %p\n", ptr); - free(ptr); -} - -static void *js_trace_realloc(void *opaque, void *ptr, size_t size) -{ - js_trace_malloc_printf(opaque, "R %zd %p", size, ptr); - ptr = realloc(ptr, size); - js_trace_malloc_printf(opaque, " -> %p\n", ptr); - return ptr; -} - -static const JSMallocFunctions trace_mf = { - js_trace_calloc, - js_trace_malloc, - js_trace_free, - js_trace_realloc, - js__malloc_usable_size -}; - -#ifdef QJS_USE_MIMALLOC -static void *js_mi_calloc(void *opaque, size_t count, size_t size) -{ - return mi_calloc(count, size); -} - -static void *js_mi_malloc(void *opaque, size_t size) -{ - return mi_malloc(size); -} - -static void js_mi_free(void *opaque, void *ptr) -{ - if (!ptr) - return; - mi_free(ptr); -} - -static void *js_mi_realloc(void *opaque, void *ptr, size_t size) -{ - return mi_realloc(ptr, size); -} - -static const JSMallocFunctions mi_mf = { - js_mi_calloc, - js_mi_malloc, - js_mi_free, - js_mi_realloc, - mi_malloc_usable_size -}; -#endif - -#define PROG_NAME "qjs" - -void help(void) -{ - printf("QuickJS-ng version %s\n" - "usage: " PROG_NAME " [options] [file [args]]\n" - "-h --help list options\n" - "-e --eval EXPR evaluate EXPR\n" - "-i --interactive go to interactive mode\n" - "-C --script load as JS classic script (default=autodetect)\n" - "-m --module load as ES module (default=autodetect)\n" - "-I --include file include an additional file\n" - " --std make 'std', 'os' and 'bjson' available to script\n" - "-T --trace trace memory allocation\n" - "-d --dump dump the memory usage stats\n" - "-D --dump-flags flags for dumping debug data (see DUMP_* defines)\n" - "-c --compile FILE compile the given JS file as a standalone executable\n" - "-o --out FILE output file for standalone executables\n" - " --exe select the executable to use as the base, defaults to the current one\n" - " --memory-limit n limit the memory usage to 'n' Kbytes\n" - " --stack-size n limit the stack size to 'n' Kbytes\n" - "-q --quit just instantiate the interpreter and quit\n", JS_GetVersion()); - exit(1); -} - -int main(int argc, char **argv) -{ - JSRuntime *rt; - JSContext *ctx; - JSValue ret = JS_UNDEFINED; - struct trace_malloc_data trace_data = { NULL }; - int r = 0; - int optind = 1; - char exebuf[JS__PATH_MAX]; - size_t exebuf_size = sizeof(exebuf); - char *compile_file = NULL; - char *exe = NULL; - char *expr = NULL; - char *dump_flags_str = NULL; - char *out = NULL; - int standalone = 0; - int interactive = 0; - int dump_memory = 0; - int dump_flags = 0; - int trace_memory = 0; - int empty_run = 0; - int module = -1; - int load_std = 0; - char *include_list[32]; - int i, include_count = 0; - int64_t memory_limit = -1; - int64_t stack_size = -1; - - /* save for later */ - qjs__argc = argc; - qjs__argv = argv; - - /* check if this is a standalone executable */ - - if (!js_exepath(exebuf, &exebuf_size) && is_standalone(exebuf)) { - standalone = 1; - goto start; - } - - dump_flags_str = getenv("QJS_DUMP_FLAGS"); - dump_flags = dump_flags_str ? strtol(dump_flags_str, NULL, 16) : 0; - - /* cannot use getopt because we want to pass the command line to - the script */ - while (optind < argc && *argv[optind] == '-') { - char *arg = argv[optind] + 1; - const char *longopt = ""; - char *optarg = NULL; - /* a single - is not an option, it also stops argument scanning */ - if (!*arg) - break; - optind++; - if (*arg == '-') { - longopt = arg + 1; - optarg = strchr(longopt, '='); - if (optarg) - *optarg++ = '\0'; - arg += strlen(arg); - /* -- stops argument scanning */ - if (!*longopt) - break; - } - for (; *arg || *longopt; longopt = "") { - char opt = *arg; - if (opt) { - arg++; - if (!optarg && *arg) - optarg = arg; - } - if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { - help(); - continue; - } - if (opt == 'e' || !strcmp(longopt, "eval")) { - if (!optarg) { - if (optind >= argc) { - fprintf(stderr, "qjs: missing expression for -e\n"); - exit(1); - } - optarg = argv[optind++]; - } - expr = optarg; - break; - } - if (opt == 'I' || !strcmp(longopt, "include")) { - if (optind >= argc) { - fprintf(stderr, "expecting filename"); - exit(1); - } - if (include_count >= countof(include_list)) { - fprintf(stderr, "too many included files"); - exit(1); - } - include_list[include_count++] = argv[optind++]; - continue; - } - if (opt == 'i' || !strcmp(longopt, "interactive")) { - interactive++; - continue; - } - if (opt == 'm' || !strcmp(longopt, "module")) { - module = 1; - continue; - } - if (opt == 'C' || !strcmp(longopt, "script")) { - module = 0; - continue; - } - if (opt == 'd' || !strcmp(longopt, "dump")) { - dump_memory++; - continue; - } - if (opt == 'D' || !strcmp(longopt, "dump-flags")) { - dump_flags = optarg ? strtol(optarg, NULL, 16) : 0; - break; - } - if (opt == 'T' || !strcmp(longopt, "trace")) { - trace_memory++; - continue; - } - if (!strcmp(longopt, "std")) { - load_std = 1; - continue; - } - if (opt == 'q' || !strcmp(longopt, "quit")) { - empty_run++; - continue; - } - if (!strcmp(longopt, "memory-limit")) { - if (!optarg) { - if (optind >= argc) { - fprintf(stderr, "expecting memory limit"); - exit(1); - } - optarg = argv[optind++]; - } - memory_limit = parse_limit(optarg); - break; - } - if (!strcmp(longopt, "stack-size")) { - if (!optarg) { - if (optind >= argc) { - fprintf(stderr, "expecting stack size"); - exit(1); - } - optarg = argv[optind++]; - } - stack_size = parse_limit(optarg); - break; - } - if (opt == 'c' || !strcmp(longopt, "compile")) { - if (!optarg) { - if (optind >= argc) { - fprintf(stderr, "qjs: missing file for -c\n"); - exit(1); - } - optarg = argv[optind++]; - } - compile_file = optarg; - break; - } - if (opt == 'o' || !strcmp(longopt, "out")) { - if (!optarg) { - if (optind >= argc) { - fprintf(stderr, "qjs: missing file for -o\n"); - exit(1); - } - optarg = argv[optind++]; - } - out = optarg; - break; - } - if (!strcmp(longopt, "exe")) { - if (!optarg) { - if (optind >= argc) { - fprintf(stderr, "qjs: missing file for --exe\n"); - exit(1); - } - optarg = argv[optind++]; - } - exe = optarg; - break; - } - if (opt) { - fprintf(stderr, "qjs: unknown option '-%c'\n", opt); - } else { - fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); - } - help(); - } - } - - if (compile_file && !out) - help(); - -start: - - if (trace_memory) { - js_trace_malloc_init(&trace_data); - rt = JS_NewRuntime2(&trace_mf, &trace_data); - } else { -#ifdef QJS_USE_MIMALLOC - rt = JS_NewRuntime2(&mi_mf, NULL); -#else - rt = JS_NewRuntime(); -#endif - } - if (!rt) { - fprintf(stderr, "qjs: cannot allocate JS runtime\n"); - exit(2); - } - if (memory_limit >= 0) - JS_SetMemoryLimit(rt, (size_t)memory_limit); - if (stack_size >= 0) - JS_SetMaxStackSize(rt, (size_t)stack_size); - if (dump_flags != 0) - JS_SetDumpFlags(rt, dump_flags); - js_std_set_worker_new_context_func(JS_NewCustomContext); - js_std_init_handlers(rt); - ctx = JS_NewCustomContext(rt); - if (!ctx) { - fprintf(stderr, "qjs: cannot allocate JS context\n"); - exit(2); - } - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); - - /* exit on unhandled promise rejections */ - JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, NULL); - - if (!empty_run) { - js_std_add_helpers(ctx, argc - optind, argv + optind); - - /* make 'std' and 'os' visible to non module code */ - if (load_std) { - const char *str = - "import * as bjson from 'qjs:bjson';\n" - "import * as std from 'qjs:std';\n" - "import * as os from 'qjs:os';\n" - "globalThis.bjson = bjson;\n" - "globalThis.std = std;\n" - "globalThis.os = os;\n"; - eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); - } - - for(i = 0; i < include_count; i++) { - if (eval_file(ctx, include_list[i], 0)) - goto fail; - } - - if (standalone) { - JSValue ns = load_standalone_module(ctx); - if (JS_IsException(ns)) - goto fail; - JSValue func = JS_GetPropertyStr(ctx, ns, "runStandalone"); - JS_FreeValue(ctx, ns); - if (JS_IsException(func)) - goto fail; - ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); - JS_FreeValue(ctx, func); - } else if (compile_file) { - JSValue ns = load_standalone_module(ctx); - if (JS_IsException(ns)) - goto fail; - JSValue func = JS_GetPropertyStr(ctx, ns, "compileStandalone"); - JS_FreeValue(ctx, ns); - if (JS_IsException(func)) - goto fail; - JSValue args[3]; - args[0] = JS_NewString(ctx, compile_file); - args[1] = JS_NewString(ctx, out); - args[2] = exe != NULL ? JS_NewString(ctx, exe) : JS_UNDEFINED; - ret = JS_Call(ctx, func, JS_UNDEFINED, 3, (JSValueConst *)args); - JS_FreeValue(ctx, func); - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - JS_FreeValue(ctx, args[2]); - } else if (expr) { - int flags = module ? JS_EVAL_TYPE_MODULE : 0; - if (eval_buf(ctx, expr, strlen(expr), "", flags)) - goto fail; - } else if (optind >= argc) { - /* interactive mode */ - interactive = 1; - } else { - const char *filename; - filename = argv[optind]; - if (eval_file(ctx, filename, module)) - goto fail; - } - if (interactive) { - JS_SetHostPromiseRejectionTracker(rt, NULL, NULL); - js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); - } - if (standalone || compile_file) { - if (JS_IsException(ret)) { - r = 1; - } else { - JS_FreeValue(ctx, ret); - r = js_std_loop(ctx); - } - } else { - r = js_std_loop(ctx); - } - if (r) { - js_std_dump_error(ctx); - goto fail; - } - } - - if (dump_memory) { - JSMemoryUsage stats; - JS_ComputeMemoryUsage(rt, &stats); - JS_DumpMemoryUsage(stdout, &stats, rt); - } - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - if (empty_run && dump_memory) { - clock_t t[5]; - double best[5] = {0}; - int i, j; - for (i = 0; i < 100; i++) { - t[0] = clock(); - rt = JS_NewRuntime(); - t[1] = clock(); - ctx = JS_NewContext(rt); - t[2] = clock(); - JS_FreeContext(ctx); - t[3] = clock(); - JS_FreeRuntime(rt); - t[4] = clock(); - for (j = 4; j > 0; j--) { - double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; - if (i == 0 || best[j] > ms) - best[j] = ms; - } - } - printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", - best[1] + best[2] + best[3] + best[4], - best[1], best[2], best[3], best[4]); - } - return 0; - fail: - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 1; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/qjsc.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/qjsc.c deleted file mode 100755 index 395bcb34..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/qjsc.c +++ /dev/null @@ -1,673 +0,0 @@ -/* - * QuickJS command line compiler - * - * Copyright (c) 2018-2024 Fabrice Bellard - * 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 -#include - -#include "cutils.h" -#include "quickjs-libc.h" - -typedef enum { - OUTPUT_C, - OUTPUT_C_MAIN, - OUTPUT_RAW, -} OutputTypeEnum; - -typedef struct { - char *name; - char *short_name; - int flags; -} namelist_entry_t; - -typedef struct namelist_t { - namelist_entry_t *array; - int count; - int size; -} namelist_t; - -static namelist_t cname_list; -static namelist_t cmodule_list; -static namelist_t init_module_list; -static OutputTypeEnum output_type; -static FILE *outfile; -static const char *c_ident_prefix = "qjsc_"; -static int strip; - -void namelist_add(namelist_t *lp, const char *name, const char *short_name, - int flags) -{ - namelist_entry_t *e; - if (lp->count == lp->size) { - size_t newsize = lp->size + (lp->size >> 1) + 4; - namelist_entry_t *a = - realloc(lp->array, sizeof(lp->array[0]) * newsize); - /* XXX: check for realloc failure */ - lp->array = a; - lp->size = newsize; - } - e = &lp->array[lp->count++]; - e->name = strdup(name); - if (short_name) - e->short_name = strdup(short_name); - else - e->short_name = NULL; - e->flags = flags; -} - -void namelist_free(namelist_t *lp) -{ - while (lp->count > 0) { - namelist_entry_t *e = &lp->array[--lp->count]; - free(e->name); - free(e->short_name); - } - free(lp->array); - lp->array = NULL; - lp->size = 0; -} - -namelist_entry_t *namelist_find(namelist_t *lp, const char *name) -{ - int i; - for(i = 0; i < lp->count; i++) { - namelist_entry_t *e = &lp->array[i]; - if (!strcmp(e->name, name)) - return e; - } - return NULL; -} - -static void get_c_name(char *buf, size_t buf_size, const char *file) -{ - const char *p, *r; - size_t len, i; - int c; - char *q; - - p = strrchr(file, '/'); - if (!p) - p = file; - else - p++; - r = strrchr(p, '.'); - if (!r) - len = strlen(p); - else - len = r - p; - js__pstrcpy(buf, buf_size, c_ident_prefix); - q = buf + strlen(buf); - for(i = 0; i < len; i++) { - c = p[i]; - if (!((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z'))) { - c = '_'; - } - if ((q - buf) < buf_size - 1) - *q++ = c; - } - *q = '\0'; -} - -static void dump_hex(FILE *f, const uint8_t *buf, size_t len) -{ - size_t i, col; - col = 0; - for(i = 0; i < len; i++) { - fprintf(f, " 0x%02x,", buf[i]); - if (++col == 8) { - fprintf(f, "\n"); - col = 0; - } - } - if (col != 0) - fprintf(f, "\n"); -} - -static void output_object_code(JSContext *ctx, - FILE *fo, JSValue obj, const char *c_name, - bool load_only) -{ - uint8_t *out_buf; - size_t out_buf_len; - int flags = JS_WRITE_OBJ_BYTECODE; - - if (strip) { - flags |= JS_WRITE_OBJ_STRIP_SOURCE; - if (strip > 1) - flags |= JS_WRITE_OBJ_STRIP_DEBUG; - } - - out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); - if (!out_buf) { - js_std_dump_error(ctx); - exit(1); - } - - namelist_add(&cname_list, c_name, NULL, load_only); - - if (output_type == OUTPUT_RAW) { - fwrite(out_buf, 1, out_buf_len, fo); - } else { - fprintf(fo, "const uint32_t %s_size = %u;\n\n", - c_name, (unsigned int)out_buf_len); - fprintf(fo, "const uint8_t %s[%u] = {\n", - c_name, (unsigned int)out_buf_len); - dump_hex(fo, out_buf, out_buf_len); - fprintf(fo, "};\n\n"); - } - - js_free(ctx, out_buf); -} - -static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) -{ - /* should never be called when compiling JS code */ - abort(); - return -1; // pacify compiler -} - -static void find_unique_cname(char *cname, size_t cname_size) -{ - char cname1[1024]; - int suffix_num; - size_t len, max_len; - assert(cname_size >= 32); - /* find a C name not matching an existing module C name by - adding a numeric suffix */ - len = strlen(cname); - max_len = cname_size - 16; - if (len > max_len) - cname[max_len] = '\0'; - suffix_num = 1; - for(;;) { - snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); - if (!namelist_find(&cname_list, cname1)) - break; - suffix_num++; - } - js__pstrcpy(cname, cname_size, cname1); -} - -JSModuleDef *jsc_module_loader(JSContext *ctx, - const char *module_name, void *opaque) -{ - JSModuleDef *m; - namelist_entry_t *e; - - /* check if it is a declared C or system module */ - e = namelist_find(&cmodule_list, module_name); - if (e) { - /* add in the static init module list */ - namelist_add(&init_module_list, e->name, e->short_name, 0); - /* create a dummy module */ - m = JS_NewCModule(ctx, module_name, js_module_dummy_init); - } else if (js__has_suffix(module_name, ".so")) { - JS_ThrowReferenceError(ctx, "%s: dynamically linking to shared libraries not supported", - module_name); - return NULL; - } else { - size_t buf_len; - uint8_t *buf; - JSValue func_val; - char cname[1000]; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - get_c_name(cname, sizeof(cname), module_name); - if (namelist_find(&cname_list, cname)) { - find_unique_cname(cname, sizeof(cname)); - } - output_object_code(ctx, outfile, func_val, cname, true); - - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - } - return m; -} - -static void compile_file(JSContext *ctx, FILE *fo, - const char *filename, - const char *script_name, - const char *c_name1, - int module) -{ - uint8_t *buf; - char c_name[1024]; - int eval_flags; - JSValue obj; - size_t buf_len; - - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - fprintf(stderr, "Could not load '%s'\n", filename); - exit(1); - } - eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; - if (module < 0) { - module = (js__has_suffix(filename, ".mjs") || - JS_DetectModule((const char *)buf, buf_len)); - } - if (module) - eval_flags |= JS_EVAL_TYPE_MODULE; - else - eval_flags |= JS_EVAL_TYPE_GLOBAL; - obj = JS_Eval(ctx, (const char *)buf, buf_len, script_name ? script_name : filename, eval_flags); - if (JS_IsException(obj)) { - js_std_dump_error(ctx); - exit(1); - } - js_free(ctx, buf); - if (c_name1) { - js__pstrcpy(c_name, sizeof(c_name), c_name1); - } else { - get_c_name(c_name, sizeof(c_name), filename); - } - output_object_code(ctx, fo, obj, c_name, false); - JS_FreeValue(ctx, obj); -} - -static const char main_c_template1[] = - "int main(int argc, char **argv)\n" - "{\n" - " int r;\n" - " JSRuntime *rt;\n" - " JSContext *ctx;\n" - " r = 0;\n" - " rt = JS_NewRuntime();\n" - " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" - " js_std_init_handlers(rt);\n" - ; - -static const char main_c_template2[] = - " r = js_std_loop(ctx);\n" - " if (r) {\n" - " js_std_dump_error(ctx);\n" - " }\n" - " js_std_free_handlers(rt);\n" - " JS_FreeContext(ctx);\n" - " JS_FreeRuntime(rt);\n" - " return r;\n" - "}\n"; - -#define PROG_NAME "qjsc" - -void help(void) -{ - printf("QuickJS-ng Compiler version %s\n" - "usage: " PROG_NAME " [options] [files]\n" - "\n" - "options are:\n" - "-b output raw bytecode instead of C code\n" - "-e output main() and bytecode in a C file\n" - "-o output set the output filename\n" - "-n script_name set the script name (as used in stack traces)\n" - "-N cname set the C name of the generated data\n" - "-C compile as JS classic script (default=autodetect)\n" - "-m compile as ES module (default=autodetect)\n" - "-D module_name compile a dynamically loaded module or worker\n" - "-M module_name[,cname] add initialization code for an external C module\n" - "-p prefix set the prefix of the generated C names\n" - "-P do not add default system modules\n" - "-s strip the source code, specify twice to also strip debug info\n" - "-S n set the maximum stack size to 'n' bytes (default=%d)\n", - JS_GetVersion(), - JS_DEFAULT_STACK_SIZE); - exit(1); -} - -// TODO(bnoordhuis) share with qjs.c maybe -static int64_t parse_limit(const char *arg) { - char *p; - unsigned long unit = 1; // bytes for backcompat; qjs defaults to kilobytes - double d = strtod(arg, &p); - - if (p == arg) { - fprintf(stderr, "qjsc: invalid limit: %s\n", arg); - return -1; - } - - if (*p) { - switch (*p++) { - case 'b': case 'B': unit = 1UL << 0; break; - case 'k': case 'K': unit = 1UL << 10; break; /* IEC kibibytes */ - case 'm': case 'M': unit = 1UL << 20; break; /* IEC mebibytes */ - case 'g': case 'G': unit = 1UL << 30; break; /* IEC gigibytes */ - default: - fprintf(stderr, "qjsc: invalid limit: %s, unrecognized suffix, only k,m,g are allowed\n", arg); - return -1; - } - if (*p) { - fprintf(stderr, "qjsc: invalid limit: %s, only one suffix allowed\n", arg); - return -1; - } - } - - return (int64_t)(d * unit); -} - -static void check_hasarg(int optind, int argc, int opt) -{ - if (optind >= argc) { - fprintf(stderr, "qjsc: missing file for -%c\n", opt); - exit(1); - } -} - -int main(int argc, char **argv) -{ - int optind = 1; - int i; - const char *out_filename, *cname, *script_name; - char cfilename[1024]; - FILE *fo; - JSRuntime *rt; - JSContext *ctx; - int module; - size_t stack_size; - namelist_t dynamic_module_list; - bool load_system_modules = true; - - out_filename = NULL; - script_name = NULL; - output_type = OUTPUT_C; - cname = NULL; - module = -1; - strip = 0; - stack_size = 0; - memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); - - - while (optind < argc && *argv[optind] == '-') { - char *arg = argv[optind] + 1; - const char *longopt = ""; - char *optarg = NULL; - /* a single - is not an option, it also stops argument scanning */ - if (!*arg) - break; - optind++; - if (*arg == '-') { - longopt = arg + 1; - optarg = strchr(longopt, '='); - if (optarg) - *optarg++ = '\0'; - arg += strlen(arg); - /* -- stops argument scanning */ - if (!*longopt) - break; - } - for (; *arg || *longopt; longopt = "") { - char opt = *arg; - if (opt) { - arg++; - if (!optarg && *arg) - optarg = arg; - } - if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { - help(); - continue; - } - if (opt == 'b') { - output_type = OUTPUT_RAW; - continue; - } - if (opt == 'o') { - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - out_filename = optarg; - continue; - } - if (opt == 'e') { - output_type = OUTPUT_C_MAIN; - continue; - } - if (opt == 'n') { - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - script_name = optarg; - continue; - } - if (opt == 'N') { - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - cname = optarg; - continue; - } - if (opt == 'C') { - module = 0; - continue; - } - if (opt == 'm') { - module = 1; - continue; - } - if (opt == 'M') { - char *p; - char path[1024]; - char cname[1024]; - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - js__pstrcpy(path, sizeof(path), optarg); - p = strchr(path, ','); - if (p) { - *p = '\0'; - js__pstrcpy(cname, sizeof(cname), p + 1); - } else { - get_c_name(cname, sizeof(cname), path); - } - namelist_add(&cmodule_list, path, cname, 0); - continue; - } - if (opt == 'D') { - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - namelist_add(&dynamic_module_list, optarg, NULL, 0); - continue; - } - if (opt == 'P') { - load_system_modules = false; - continue; - } - if (opt == 's') { - strip++; - continue; - } - if (opt == 'p') { - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - c_ident_prefix = optarg; - continue; - } - if (opt == 'S') { - if (!optarg) { - check_hasarg(optind, argc, opt); - optarg = argv[optind++]; - } - stack_size = parse_limit(optarg); - continue; - } - help(); - } - } - - if (load_system_modules) { - /* add system modules */ - namelist_add(&cmodule_list, "qjs:std", "std", 0); - namelist_add(&cmodule_list, "qjs:os", "os", 0); - namelist_add(&cmodule_list, "qjs:bjson", "bjson", 0); - namelist_add(&cmodule_list, "std", "std", 0); - namelist_add(&cmodule_list, "os", "os", 0); - namelist_add(&cmodule_list, "bjson", "bjson", 0); - } - - if (optind >= argc) - help(); - - if (!out_filename) - out_filename = "out.c"; - - js__pstrcpy(cfilename, sizeof(cfilename), out_filename); - - if (output_type == OUTPUT_RAW) - fo = fopen(cfilename, "wb"); - else - fo = fopen(cfilename, "w"); - - if (!fo) { - perror(cfilename); - exit(1); - } - outfile = fo; - - rt = JS_NewRuntime(); - ctx = JS_NewContext(rt); - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); - - if (output_type != OUTPUT_RAW) { - fprintf(fo, "/* File generated automatically by the QuickJS-ng compiler. */\n" - "\n" - ); - } - - if (output_type == OUTPUT_C_MAIN) { - fprintf(fo, "#include \"quickjs-libc.h\"\n" - "\n" - ); - } else if (output_type == OUTPUT_C) { - fprintf(fo, "#include \n" - "\n" - ); - } - - for(i = optind; i < argc; i++) { - const char *filename = argv[i]; - compile_file(ctx, fo, filename, script_name, cname, module); - cname = NULL; - } - - for(i = 0; i < dynamic_module_list.count; i++) { - if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) { - fprintf(stderr, "Could not load dynamic module '%s'\n", - dynamic_module_list.array[i].name); - exit(1); - } - } - - if (output_type == OUTPUT_C_MAIN) { - fprintf(fo, - "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" - "{\n" - " JSContext *ctx = JS_NewContext(rt);\n" - " if (!ctx)\n" - " return NULL;\n"); - /* add the precompiled modules (XXX: could modify the module - loader instead) */ - for(i = 0; i < init_module_list.count; i++) { - namelist_entry_t *e = &init_module_list.array[i]; - /* initialize the static C modules */ - - fprintf(fo, - " {\n" - " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" - " js_init_module_%s(ctx, \"%s\");\n" - " }\n", - e->short_name, e->short_name, e->name); - } - for(i = 0; i < cname_list.count; i++) { - namelist_entry_t *e = &cname_list.array[i]; - if (e->flags) { - fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n", - e->name, e->name); - } - } - fprintf(fo, - " return ctx;\n" - "}\n\n"); - - fputs(main_c_template1, fo); - - if (stack_size != 0) { - fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n", - (unsigned int)stack_size); - } - - /* add the module loader */ - fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n"); - - fprintf(fo, - " ctx = JS_NewCustomContext(rt);\n" - " js_std_add_helpers(ctx, argc, argv);\n"); - - for(i = 0; i < cname_list.count; i++) { - namelist_entry_t *e = &cname_list.array[i]; - if (!e->flags) { - fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", - e->name, e->name); - } - } - fputs(main_c_template2, fo); - } - - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - fclose(fo); - - namelist_free(&cname_list); - namelist_free(&cmodule_list); - namelist_free(&init_module_list); - return 0; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-atom.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-atom.h deleted file mode 100755 index 2a123284..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-atom.h +++ /dev/null @@ -1,265 +0,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") -/* 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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-c-atomics.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-c-atomics.h deleted file mode 100755 index 8fc6b720..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-c-atomics.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-libc.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-libc.c deleted file mode 100755 index 2c7ac770..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-libc.c +++ /dev/null @@ -1,4685 +0,0 @@ -/* - * QuickJS C library - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 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. - */ -#include "quickjs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#include -#endif -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif -#if defined(_WIN32) -#include -#include -#include -#include -#include -#include -#include -#define popen _popen -#define pclose _pclose -#define rmdir _rmdir -#define getcwd _getcwd -#define chdir _chdir -#else -#include -#include -#if !defined(__wasi__) -#include -#include -#include -#include -#include -#endif - -#if defined(__APPLE__) -typedef sig_t sighandler_t; -#include -#include -#define environ (*_NSGetEnviron()) -#endif - -#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) -typedef sig_t sighandler_t; -extern char **environ; -#endif - -#endif /* _WIN32 */ - -#include "cutils.h" -#include "list.h" -#include "quickjs-libc.h" - -#if JS_HAVE_THREADS -#include "quickjs-c-atomics.h" -#define USE_WORKER // enable os.Worker -#endif - -#ifndef S_IFBLK -#define S_IFBLK 0 -#endif - -#ifndef S_IFIFO -#define S_IFIFO 0 -#endif - -#ifndef MAX_SAFE_INTEGER // already defined in amalgamation builds -#define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) -#endif - -#ifndef QJS_NATIVE_MODULE_SUFFIX -#ifdef _WIN32 -#define QJS_NATIVE_MODULE_SUFFIX ".dll" -#else -#define QJS_NATIVE_MODULE_SUFFIX ".so" -#endif -#endif - -/* TODO: - - add socket calls -*/ - -typedef struct { - struct list_head link; - int fd; - JSValue rw_func[2]; -} JSOSRWHandler; - -typedef struct { - struct list_head link; - int sig_num; - JSValue func; -} JSOSSignalHandler; - -typedef struct { - struct list_head link; - int64_t timer_id; - uint8_t repeats:1; - int64_t timeout; - int64_t delay; - JSValue func; -} JSOSTimer; - -typedef struct { - struct list_head link; - JSValue promise; - JSValue reason; -} JSRejectedPromiseEntry; - -#ifdef USE_WORKER - -typedef struct { - struct list_head link; - uint8_t *data; - size_t data_len; - /* list of SharedArrayBuffers, necessary to free the message */ - uint8_t **sab_tab; - size_t sab_tab_len; -} JSWorkerMessage; - -typedef struct JSWaker { -#ifdef _WIN32 - HANDLE handle; -#else - int read_fd; - int write_fd; -#endif -} JSWaker; - -typedef struct { - int ref_count; - js_mutex_t mutex; - struct list_head msg_queue; /* list of JSWorkerMessage.link */ - JSWaker waker; -} JSWorkerMessagePipe; - -typedef struct { - struct list_head link; - JSWorkerMessagePipe *recv_pipe; - JSValue on_message_func; -} JSWorkerMessageHandler; - -#endif // USE_WORKER - -typedef struct JSThreadState { - struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ - struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ - struct list_head os_timers; /* list of JSOSTimer.link */ - struct list_head port_list; /* list of JSWorkerMessageHandler.link */ - struct list_head rejected_promise_list; /* list of JSRejectedPromiseEntry.link */ - int eval_script_recurse; /* only used in the main thread */ - int64_t next_timer_id; /* for setTimeout / setInterval */ - bool can_js_os_poll; - /* not used in the main thread */ -#ifdef USE_WORKER - JSWorkerMessagePipe *recv_pipe, *send_pipe; -#else - void *recv_pipe; -#endif // USE_WORKER - JSClassID std_file_class_id; - JSClassID worker_class_id; -} JSThreadState; - -static uint64_t os_pending_signals; - -static void *js_std_dbuf_realloc(void *opaque, void *ptr, size_t size) -{ - JSRuntime *rt = opaque; - return js_realloc_rt(rt, ptr, size); -} - -static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) -{ - dbuf_init2(s, JS_GetRuntime(ctx), js_std_dbuf_realloc); -} - -static bool my_isdigit(int c) -{ - return (c >= '0' && c <= '9'); -} - -static JSThreadState *js_get_thread_state(JSRuntime *rt) -{ - return (JSThreadState *)js_std_cmd(/*GetOpaque*/0, rt); -} - -static void js_set_thread_state(JSRuntime *rt, JSThreadState *ts) -{ - js_std_cmd(/*SetOpaque*/1, rt, ts); -} - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif // __GNUC__ -static JSValue js_printf_internal(JSContext *ctx, - int argc, JSValueConst *argv, FILE *fp) -{ - char fmtbuf[32]; - uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; - JSValue res; - DynBuf dbuf; - const char *fmt_str = NULL; - const uint8_t *fmt, *fmt_end; - const uint8_t *p; - char *q; - int i, c, len, mod; - size_t fmt_len; - int32_t int32_arg; - int64_t int64_arg; - double double_arg; - const char *string_arg; - - js_std_dbuf_init(ctx, &dbuf); - - if (argc > 0) { - fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); - if (!fmt_str) - goto fail; - - i = 1; - fmt = (const uint8_t *)fmt_str; - fmt_end = fmt + fmt_len; - while (fmt < fmt_end) { - for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) - continue; - dbuf_put(&dbuf, p, fmt - p); - if (fmt >= fmt_end) - break; - q = fmtbuf; - *q++ = *fmt++; /* copy '%' */ - - /* flags */ - for(;;) { - c = *fmt; - if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' || - c == '\'') { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = c; - fmt++; - } else { - break; - } - } - /* width */ - if (*fmt == '*') { - if (i >= argc) - goto missing; - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); - fmt++; - } else { - while (my_isdigit(*fmt)) { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - } - } - if (*fmt == '.') { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - if (*fmt == '*') { - if (i >= argc) - goto missing; - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); - fmt++; - } else { - while (my_isdigit(*fmt)) { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - } - } - } - - /* we only support the "l" modifier for 64 bit numbers */ - mod = ' '; - if (*fmt == 'l') { - mod = *fmt++; - } - - /* type */ - c = *fmt++; - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = c; - *q = '\0'; - - switch (c) { - case 'c': - if (i >= argc) - goto missing; - if (JS_IsString(argv[i])) { - // TODO(chqrlie) need an API to wrap charCodeAt and codePointAt */ - string_arg = JS_ToCString(ctx, argv[i++]); - if (!string_arg) - goto fail; - int32_arg = utf8_decode((const uint8_t *)string_arg, &p); - JS_FreeCString(ctx, string_arg); - } else { - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - } - // XXX: throw an exception? - if ((unsigned)int32_arg > 0x10FFFF) - int32_arg = 0xFFFD; - /* ignore conversion flags, width and precision */ - len = utf8_encode(cbuf, int32_arg); - dbuf_put(&dbuf, cbuf, len); - break; - - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - if (i >= argc) - goto missing; - if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++])) - goto fail; - if (mod == 'l') { - /* 64 bit number */ -#if defined(_WIN32) - if (q >= fmtbuf + sizeof(fmtbuf) - 3) - goto invalid; - q[2] = q[-1]; - q[-1] = 'I'; - q[0] = '6'; - q[1] = '4'; - q[3] = '\0'; - dbuf_printf(&dbuf, fmtbuf, (int64_t)int64_arg); -#else - if (q >= fmtbuf + sizeof(fmtbuf) - 2) - goto invalid; - q[1] = q[-1]; - q[-1] = q[0] = 'l'; - q[2] = '\0'; - dbuf_printf(&dbuf, fmtbuf, (long long)int64_arg); -#endif - } else { - dbuf_printf(&dbuf, fmtbuf, (int)int64_arg); - } - break; - - case 's': - if (i >= argc) - goto missing; - /* XXX: handle strings containing null characters */ - string_arg = JS_ToCString(ctx, argv[i++]); - if (!string_arg) - goto fail; - dbuf_printf(&dbuf, fmtbuf, string_arg); - JS_FreeCString(ctx, string_arg); - break; - - case 'e': - case 'f': - case 'g': - case 'a': - case 'E': - case 'F': - case 'G': - case 'A': - if (i >= argc) - goto missing; - if (JS_ToFloat64(ctx, &double_arg, argv[i++])) - goto fail; - dbuf_printf(&dbuf, fmtbuf, double_arg); - break; - - case '%': - dbuf_putc(&dbuf, '%'); - break; - - default: - /* XXX: should support an extension mechanism */ - invalid: - JS_ThrowTypeError(ctx, "invalid conversion specifier in format string"); - goto fail; - missing: - JS_ThrowReferenceError(ctx, "missing argument for conversion specifier"); - goto fail; - } - } - JS_FreeCString(ctx, fmt_str); - } - if (dbuf.error) { - res = JS_ThrowOutOfMemory(ctx); - } else { - if (fp) { - len = fwrite(dbuf.buf, 1, dbuf.size, fp); - res = JS_NewInt32(ctx, len); - } else { - res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); - } - } - dbuf_free(&dbuf); - return res; - -fail: - JS_FreeCString(ctx, fmt_str); - dbuf_free(&dbuf); - return JS_EXCEPTION; -} -#ifdef __GNUC__ -#pragma GCC diagnostic pop // ignored "-Wformat-nonliteral" -#endif // __GNUC__ - -uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) -{ - FILE *f; - size_t n, len; - uint8_t *p, *buf, tmp[8192]; - - f = fopen(filename, "rb"); - if (!f) - return NULL; - buf = NULL; - len = 0; - do { - n = fread(tmp, 1, sizeof(tmp), f); - if (ctx) { - p = js_realloc(ctx, buf, len + n + 1); - } else { - p = realloc(buf, len + n + 1); - } - if (!p) { - if (ctx) { - js_free(ctx, buf); - } else { - free(buf); - } - fclose(f); - return NULL; - } - memcpy(&p[len], tmp, n); - buf = p; - len += n; - buf[len] = '\0'; - } while (n == sizeof(tmp)); - fclose(f); - *pbuf_len = len; - return buf; -} - -/* load and evaluate a file */ -static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint8_t *buf; - const char *filename; - JSValue ret; - size_t buf_len; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load '%s'", filename); - JS_FreeCString(ctx, filename); - return JS_EXCEPTION; - } - ret = JS_Eval(ctx, (char *)buf, buf_len, filename, - JS_EVAL_TYPE_GLOBAL); - js_free(ctx, buf); - JS_FreeCString(ctx, filename); - return ret; -} - -static int get_bool_option(JSContext *ctx, bool *pbool, - JSValueConst obj, - const char *option) -{ - JSValue val; - val = JS_GetPropertyStr(ctx, obj, option); - if (JS_IsException(val)) - return -1; - if (!JS_IsUndefined(val)) { - *pbool = JS_ToBool(ctx, val); - } - JS_FreeValue(ctx, val); - return 0; -} - -static void free_buf(JSRuntime *rt, void *opaque, void *ptr) { - js_free_rt(rt, ptr); -} - -/* load a file as a UTF-8 encoded string or Uint8Array */ -static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint8_t *buf; - const char *filename; - JSValueConst options_obj; - JSValue ret; - size_t buf_len; - bool binary = false; - - if (argc >= 2) { - options_obj = argv[1]; - if (get_bool_option(ctx, &binary, options_obj, - "binary")) - return JS_EXCEPTION; - } - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - buf = js_load_file(ctx, &buf_len, filename); - JS_FreeCString(ctx, filename); - if (!buf) - return JS_NULL; - if (binary) { - ret = JS_NewUint8Array(ctx, buf, buf_len, free_buf, NULL, false); - } else { - ret = JS_NewStringLen(ctx, (char *)buf, buf_len); - js_free(ctx, buf); - } - - return ret; -} - -static JSValue js_std_writeFile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - const char *mode; - const void *buf; - size_t len, n; - JSValueConst data; - JSValue val, ret, unref; - bool release; - FILE *fp; - - ret = JS_EXCEPTION; - len = 0; - buf = ""; - mode = "w"; - data = argv[1]; - unref = JS_UNDEFINED; - release = false; - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - if (JS_IsObject(data)) { - val = JS_GetPropertyStr(ctx, data, "buffer"); - if (JS_IsException(val)) - goto exception; - if (JS_IsArrayBuffer(val)) { - data = unref = val; - } else { - JS_FreeValue(ctx, val); - } - } - if (JS_IsArrayBuffer(data)) { - buf = JS_GetArrayBuffer(ctx, &len, data); - mode = "wb"; - } else if (!JS_IsUndefined(data)) { - buf = JS_ToCStringLen(ctx, &len, data); - release = true; - } - if (!buf) - goto exception; - fp = fopen(filename, mode); - if (!fp) { - JS_ThrowPlainError(ctx, "error opening %s for writing", filename); - goto exception; - } - n = fwrite(buf, len, 1, fp); - fclose(fp); - if (n != 1) { - JS_ThrowPlainError(ctx, "error writing to %s", filename); - goto exception; - } - ret = JS_UNDEFINED; -exception: - JS_FreeCString(ctx, filename); - if (release) - JS_FreeCString(ctx, buf); - JS_FreeValue(ctx, unref); - return ret; -} - -typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, - const char *module_name); - - -#if defined(_WIN32) -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JSModuleDef *m; - HINSTANCE hd; - JSInitModuleFunc *init; - char *filename = NULL; - size_t len = strlen(module_name); - bool is_absolute = len > 2 && ((module_name[0] >= 'A' && module_name[0] <= 'Z') || - (module_name[0] >= 'a' && module_name[0] <= 'z')) && module_name[1] == ':'; - bool is_relative = len > 2 && module_name[0] == '.' && (module_name[1] == '/' || module_name[1] == '\\'); - if (is_absolute || is_relative) { - filename = (char *)module_name; - } else { - filename = js_malloc(ctx, len + 2 + 1); - if (!filename) - return NULL; - strcpy(filename, "./"); - strcpy(filename + 2, module_name); - } - hd = LoadLibraryA(filename); - if (filename != module_name) - js_free(ctx, filename); - if (hd == NULL) { - JS_ThrowReferenceError(ctx, "js_load_module '%s' error: %lu", - module_name, GetLastError()); - goto fail; - } - init = (JSInitModuleFunc *)(uintptr_t)GetProcAddress(hd, "js_init_module"); - if (!init) { - JS_ThrowReferenceError(ctx, "js_init_module '%s' not found: %lu", - module_name, GetLastError()); - goto fail; - } - m = init(ctx, module_name); - if (!m) { - JS_ThrowReferenceError(ctx, "js_call_module '%s' initialization error", - module_name); - fail: - if (hd != NULL) - FreeLibrary(hd); - return NULL; - } - return m; -} -#elif defined(__wasi__) -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); - return NULL; -} -#else -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JSModuleDef *m; - void *hd; - JSInitModuleFunc *init; - char *filename; - - if (!strchr(module_name, '/')) { - /* must add a '/' so that the DLL is not searched in the - system library paths */ - filename = js_malloc(ctx, strlen(module_name) + 2 + 1); - if (!filename) - return NULL; - strcpy(filename, "./"); - strcpy(filename + 2, module_name); - } else { - filename = (char *)module_name; - } - - /* C module */ - hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); - if (filename != module_name) - js_free(ctx, filename); - if (!hd) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library: %s", - module_name, dlerror()); - goto fail; - } - - *(void **) (&init) = dlsym(hd, "js_init_module"); - if (!init) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", - module_name); - goto fail; - } - - m = init(ctx, module_name); - if (!m) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", - module_name); - fail: - if (hd) - dlclose(hd); - return NULL; - } - return m; -} -#endif /* !_WIN32 */ - -int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - bool use_realpath, bool is_main) -{ - JSModuleDef *m; - char buf[JS__PATH_MAX + 16]; - JSValue meta_obj; - JSAtom module_name_atom; - const char *module_name; - - assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); - m = JS_VALUE_GET_PTR(func_val); - - module_name_atom = JS_GetModuleName(ctx, m); - module_name = JS_AtomToCString(ctx, module_name_atom); - JS_FreeAtom(ctx, module_name_atom); - if (!module_name) - return -1; - if (!strchr(module_name, ':')) { - strcpy(buf, "file://"); -#if !defined(_WIN32) && !defined(__wasi__) - /* realpath() cannot be used with modules compiled with qjsc - because the corresponding module source code is not - necessarily present */ - if (use_realpath) { - char *res = realpath(module_name, buf + strlen(buf)); - if (!res) { - JS_ThrowTypeError(ctx, "realpath failure"); - JS_FreeCString(ctx, module_name); - return -1; - } - } else -#endif - { - js__pstrcat(buf, sizeof(buf), module_name); - } - } else { - js__pstrcpy(buf, sizeof(buf), module_name); - } - JS_FreeCString(ctx, module_name); - - meta_obj = JS_GetImportMeta(ctx, m); - if (JS_IsException(meta_obj)) - return -1; - JS_DefinePropertyValueStr(ctx, meta_obj, "url", - JS_NewString(ctx, buf), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, meta_obj, "main", - JS_NewBool(ctx, is_main), - JS_PROP_C_W_E); - JS_FreeValue(ctx, meta_obj); - return 0; -} - -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque) -{ - JSModuleDef *m; - - if (js__has_suffix(module_name, QJS_NATIVE_MODULE_SUFFIX)) { - m = js_module_loader_so(ctx, module_name); - } else { - size_t buf_len; - uint8_t *buf; - JSValue func_val; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - if (js_module_set_import_meta(ctx, func_val, true, false) < 0) { - JS_FreeValue(ctx, func_val); - return NULL; - } - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - } - return m; -} - -static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int status; - if (JS_ToInt32(ctx, &status, argv[0])) - status = -1; - exit(status); - return JS_UNDEFINED; -} - -static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name, *str; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - str = getenv(name); - JS_FreeCString(ctx, name); - if (!str) - return JS_UNDEFINED; - else - return JS_NewString(ctx, str); -} - -#if defined(_WIN32) -static void setenv(const char *name, const char *value, int overwrite) -{ - char *str; - size_t name_len, value_len; - name_len = strlen(name); - value_len = strlen(value); - str = malloc(name_len + 1 + value_len + 1); - memcpy(str, name, name_len); - str[name_len] = '='; - memcpy(str + name_len + 1, value, value_len); - str[name_len + 1 + value_len] = '\0'; - _putenv(str); - free(str); -} - -static void unsetenv(const char *name) -{ - setenv(name, "", true); -} -#endif /* _WIN32 */ - -static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name, *value; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - value = JS_ToCString(ctx, argv[1]); - if (!value) { - JS_FreeCString(ctx, name); - return JS_EXCEPTION; - } - setenv(name, value, true); - JS_FreeCString(ctx, name); - JS_FreeCString(ctx, value); - return JS_UNDEFINED; -} - -static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - unsetenv(name); - JS_FreeCString(ctx, name); - return JS_UNDEFINED; -} - -/* return an object containing the list of the available environment - variables. */ -static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - char **envp; - const char *name, *p, *value; - JSValue obj; - uint32_t idx; - size_t name_len; - JSAtom atom; - int ret; - - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - envp = environ; - for(idx = 0; envp[idx] != NULL; idx++) { - name = envp[idx]; - p = strchr(name, '='); - name_len = p - name; - if (!p) - continue; - value = p + 1; - atom = JS_NewAtomLen(ctx, name, name_len); - if (atom == JS_ATOM_NULL) - goto fail; - ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), - JS_PROP_C_W_E); - JS_FreeAtom(ctx, atom); - if (ret < 0) - goto fail; - } - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JS_RunGC(JS_GetRuntime(ctx)); - return JS_UNDEFINED; -} - -static int interrupt_handler(JSRuntime *rt, void *opaque) -{ - return (os_pending_signals >> SIGINT) & 1; -} - -static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - const char *str = NULL; - size_t len; - JSValue ret, obj; - JSValueConst options_obj, arg; - bool backtrace_barrier = false; - bool eval_function = false; - bool eval_module = false; - bool compile_only = false; - bool compile_module = false; - bool is_async = false; - int flags; - - if (argc >= 2) { - options_obj = argv[1]; - if (get_bool_option(ctx, &backtrace_barrier, options_obj, - "backtrace_barrier")) - return JS_EXCEPTION; - if (get_bool_option(ctx, &eval_function, options_obj, - "eval_function")) - return JS_EXCEPTION; - if (get_bool_option(ctx, &eval_module, options_obj, - "eval_module")) - return JS_EXCEPTION; - if (get_bool_option(ctx, &compile_only, options_obj, - "compile_only")) - return JS_EXCEPTION; - if (get_bool_option(ctx, &compile_module, options_obj, - "compile_module")) - return JS_EXCEPTION; - if (get_bool_option(ctx, &is_async, options_obj, - "async")) - return JS_EXCEPTION; - } - - if (eval_module) { - arg = argv[0]; - if (JS_VALUE_GET_TAG(arg) != JS_TAG_MODULE) - return JS_ThrowTypeError(ctx, "not a module"); - - if (JS_ResolveModule(ctx, arg) < 0) - return JS_EXCEPTION; - - if (js_module_set_import_meta(ctx, arg, false, false) < 0) - return JS_EXCEPTION; - - return JS_EvalFunction(ctx, JS_DupValue(ctx, arg)); - } - - if (!eval_function) { - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; - } - if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { - /* install the interrupt handler */ - JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); - } - flags = compile_module ? JS_EVAL_TYPE_MODULE : JS_EVAL_TYPE_GLOBAL; - if (backtrace_barrier) - flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; - if (compile_only) - flags |= JS_EVAL_FLAG_COMPILE_ONLY; - if (is_async) - flags |= JS_EVAL_FLAG_ASYNC; - if (eval_function) { - obj = JS_DupValue(ctx, argv[0]); - ret = JS_EvalFunction(ctx, obj); // takes ownership of |obj| - } else { - ret = JS_Eval(ctx, str, len, "", flags); - } - JS_FreeCString(ctx, str); - if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { - /* remove the interrupt handler */ - JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); - os_pending_signals &= ~((uint64_t)1 << SIGINT); - /* convert the uncatchable "interrupted" error into a normal error - so that it can be caught by the REPL */ - if (JS_IsException(ret)) - JS_ResetUncatchableError(ctx); - } - return ret; -} - -typedef struct { - FILE *f; - bool is_popen; -} JSSTDFile; - -static bool is_stdio(FILE *f) -{ - return f == stdin || f == stdout || f == stderr; -} - -static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) -{ - JSThreadState *ts = js_get_thread_state(rt); - JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); - if (s) { - if (s->f && !is_stdio(s->f)) { -#if !defined(__wasi__) - if (s->is_popen) - pclose(s->f); - else -#endif - fclose(s->f); - } - js_free_rt(rt, s); - } -} - -static ssize_t js_get_errno(ssize_t ret) -{ - if (ret == -1) - ret = -errno; - return ret; -} - -static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int err; - if (JS_ToInt32(ctx, &err, argv[0])) - return JS_EXCEPTION; - return JS_NewString(ctx, strerror(err)); -} - -static JSValue js_new_std_file(JSContext *ctx, FILE *f, bool is_popen) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSSTDFile *s; - JSValue obj; - obj = JS_NewObjectClass(ctx, ts->std_file_class_id); - if (JS_IsException(obj)) - return obj; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - s->is_popen = is_popen; - s->f = f; - JS_SetOpaque(obj, s); - return obj; -} - -static void js_set_error_object(JSContext *ctx, JSValueConst obj, int err) -{ - if (!JS_IsUndefined(obj)) { - JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); - } -} - -static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename, *mode = NULL; - FILE *f; - int err; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rwa+bx")] != '\0') { - JS_ThrowTypeError(ctx, "invalid file mode"); - goto fail; - } - - f = fopen(filename, mode); - if (!f) - err = errno; - else - err = 0; - if (argc >= 3) - js_set_error_object(ctx, argv[2], err); - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, false); - fail: - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -#if !defined(__wasi__) -static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename, *mode = NULL; - FILE *f; - int err; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (strcmp(mode, "r") && strcmp(mode, "w")) { - JS_ThrowTypeError(ctx, "invalid file mode"); - goto fail; - } - - f = popen(filename, mode); - if (!f) - err = errno; - else - err = 0; - if (argc >= 3) - js_set_error_object(ctx, argv[2], err); - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, true); - fail: - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} -#endif // !defined(__wasi__) - -static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *mode; - FILE *f; - int fd, err; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rwa+")] != '\0') { - JS_ThrowTypeError(ctx, "invalid file mode"); - goto fail; - } - - f = fdopen(fd, mode); - if (!f) - err = errno; - else - err = 0; - if (argc >= 3) - js_set_error_object(ctx, argv[2], err); - JS_FreeCString(ctx, mode); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, false); - fail: - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -#if !defined(__wasi__) -static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f; - f = tmpfile(); - if (argc >= 1) - js_set_error_object(ctx, argv[0], f ? 0 : errno); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, false); -} -#endif - -static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_printf_internal(ctx, argc, argv, NULL); -} - -static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_printf_internal(ctx, argc, argv, stdout); -} - -static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSSTDFile *s = JS_GetOpaque2(ctx, obj, ts->std_file_class_id); - if (!s) - return NULL; - if (!s->f) { - JS_ThrowTypeError(ctx, "invalid file handle"); - return NULL; - } - return s->f; -} - -static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f; - int i; - const char *str; - size_t len; - - if (magic == 0) { - f = stdout; - } else { - f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - } - - for(i = 0; i < argc; i++) { - str = JS_ToCStringLen(ctx, &len, argv[i]); - if (!str) - return JS_EXCEPTION; - fwrite(str, 1, len, f); - JS_FreeCString(ctx, str); - } - return JS_UNDEFINED; -} - -static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSSTDFile *s = JS_GetOpaque2(ctx, this_val, ts->std_file_class_id); - int err; - if (!s) - return JS_EXCEPTION; - if (!s->f) - return JS_ThrowTypeError(ctx, "invalid file handle"); - if (is_stdio(s->f)) - return JS_ThrowTypeError(ctx, "cannot close stdio"); -#if !defined(__wasi__) - if (s->is_popen) - err = js_get_errno(pclose(s->f)); - else -#endif - err = js_get_errno(fclose(s->f)); - s->f = NULL; - return JS_NewInt32(ctx, err); -} - -static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return js_printf_internal(ctx, argc, argv, f); -} - -static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - fflush(f); - return JS_UNDEFINED; -} - -static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_bigint) -{ - FILE *f = js_std_file_get(ctx, this_val); - int64_t pos; - if (!f) - return JS_EXCEPTION; -#if defined(__linux__) || defined(__GLIBC__) - pos = ftello(f); -#else - pos = ftell(f); -#endif - if (is_bigint) - return JS_NewBigInt64(ctx, pos); - else - return JS_NewInt64(ctx, pos); -} - -static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int64_t pos; - int whence, ret; - if (!f) - return JS_EXCEPTION; - if (JS_ToInt64Ext(ctx, &pos, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &whence, argv[1])) - return JS_EXCEPTION; -#if defined(__linux__) || defined(__GLIBC__) - ret = fseeko(f, pos, whence); -#else - ret = fseek(f, pos, whence); -#endif - if (ret < 0) - ret = -errno; - return JS_NewInt32(ctx, ret); -} - -static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewBool(ctx, feof(f)); -} - -static JSValue js_std_file_error(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewBool(ctx, ferror(f)); -} - -static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - clearerr(f); - return JS_UNDEFINED; -} - -static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewInt32(ctx, fileno(f)); -} - -static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f = js_std_file_get(ctx, this_val); - uint64_t pos, len; - size_t size, ret; - uint8_t *buf; - - if (!f) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &pos, argv[1])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[2])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[0]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); - if (magic) - ret = fwrite(buf + pos, 1, len, f); - else - ret = fread(buf + pos, 1, len, f); - return JS_NewInt64(ctx, ret); -} - -/* XXX: could use less memory and go faster */ -static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - DynBuf dbuf; - JSValue obj; - - if (!f) - return JS_EXCEPTION; - - js_std_dbuf_init(ctx, &dbuf); - for(;;) { - c = fgetc(f); - if (c == EOF) { - if (dbuf.size == 0) { - /* EOF */ - dbuf_free(&dbuf); - return JS_NULL; - } else { - break; - } - } - if (c == '\n') - break; - if (dbuf_putc(&dbuf, c)) { - dbuf_free(&dbuf); - return JS_ThrowOutOfMemory(ctx); - } - } - obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); - dbuf_free(&dbuf); - return obj; -} - -/* XXX: could use less memory and go faster */ -static JSValue js_std_file_readAs(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - DynBuf dbuf; - JSValue obj; - uint64_t max_size64; - size_t max_size; - JSValueConst max_size_val; - - if (!f) - return JS_EXCEPTION; - - if (argc >= 1) - max_size_val = argv[0]; - else - max_size_val = JS_UNDEFINED; - max_size = (size_t)-1; - if (!JS_IsUndefined(max_size_val)) { - if (JS_ToIndex(ctx, &max_size64, max_size_val)) - return JS_EXCEPTION; - if (max_size64 < max_size) - max_size = max_size64; - } - - js_std_dbuf_init(ctx, &dbuf); - while (max_size != 0) { - c = fgetc(f); - if (c == EOF) - break; - if (dbuf_putc(&dbuf, c)) { - dbuf_free(&dbuf); - return JS_EXCEPTION; - } - max_size--; - } - if (magic) { - obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); - } else { - obj = JS_NewArrayBufferCopy(ctx, dbuf.buf, dbuf.size); - } - dbuf_free(&dbuf); - return obj; -} - -static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewInt32(ctx, fgetc(f)); -} - -static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - if (!f) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &c, argv[0])) - return JS_EXCEPTION; - c = fputc(c, f); - return JS_NewInt32(ctx, c); -} - -/* urlGet */ -#if !defined(__wasi__) - -#define URL_GET_PROGRAM "curl -s -i --" -#define URL_GET_BUF_SIZE 4096 - -static int http_get_header_line(FILE *f, char *buf, size_t buf_size, - DynBuf *dbuf) -{ - int c; - char *p; - - p = buf; - for(;;) { - c = fgetc(f); - if (c < 0) - return -1; - if ((p - buf) < buf_size - 1) - *p++ = c; - if (dbuf) - dbuf_putc(dbuf, c); - if (c == '\n') - break; - } - *p = '\0'; - return 0; -} - -static int http_get_status(const char *buf) -{ - const char *p = buf; - while (*p != ' ' && *p != '\0') - p++; - if (*p != ' ') - return 0; - while (*p == ' ') - p++; - return atoi(p); -} - -static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *url; - DynBuf cmd_buf; - DynBuf data_buf_s, *data_buf = &data_buf_s; - DynBuf header_buf_s, *header_buf = &header_buf_s; - char *buf; - size_t i, len; - int status; - JSValue response = JS_UNDEFINED, ret_obj; - JSValueConst options_obj; - FILE *f; - bool binary_flag, full_flag; - - url = JS_ToCString(ctx, argv[0]); - if (!url) - return JS_EXCEPTION; - - binary_flag = false; - full_flag = false; - - if (argc >= 2) { - options_obj = argv[1]; - - if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) - goto fail_obj; - - if (get_bool_option(ctx, &full_flag, options_obj, "full")) { - fail_obj: - JS_FreeCString(ctx, url); - return JS_EXCEPTION; - } - } - - js_std_dbuf_init(ctx, &cmd_buf); - dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); - for(i = 0; url[i] != '\0'; i++) { - unsigned char c = url[i]; - switch (c) { - case '\'': - /* shell single quoted string does not support \' */ - dbuf_putstr(&cmd_buf, "'\\''"); - break; - case '[': case ']': case '{': case '}': case '\\': - /* prevent interpretation by curl as range or set specification */ - dbuf_putc(&cmd_buf, '\\'); - /* FALLTHROUGH */ - default: - dbuf_putc(&cmd_buf, c); - break; - } - } - JS_FreeCString(ctx, url); - dbuf_putstr(&cmd_buf, "'"); - dbuf_putc(&cmd_buf, '\0'); - if (dbuf_error(&cmd_buf)) { - dbuf_free(&cmd_buf); - return JS_EXCEPTION; - } - // printf("%s\n", (char *)cmd_buf.buf); - f = popen((char *)cmd_buf.buf, "r"); - dbuf_free(&cmd_buf); - if (!f) { - return JS_ThrowTypeError(ctx, "could not start curl"); - } - - js_std_dbuf_init(ctx, data_buf); - js_std_dbuf_init(ctx, header_buf); - - buf = js_malloc(ctx, URL_GET_BUF_SIZE); - if (!buf) - goto fail; - - /* get the HTTP status */ - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { - status = 0; - goto bad_header; - } - status = http_get_status(buf); - if (!full_flag && !(status >= 200 && status <= 299)) { - goto bad_header; - } - - /* wait until there is an empty line */ - for(;;) { - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { - bad_header: - response = JS_NULL; - goto done; - } - if (!strcmp(buf, "\r\n")) - break; - } - if (dbuf_error(header_buf)) - goto fail; - header_buf->size -= 2; /* remove the trailing CRLF */ - - /* download the data */ - for(;;) { - len = fread(buf, 1, URL_GET_BUF_SIZE, f); - if (len == 0) - break; - dbuf_put(data_buf, (uint8_t *)buf, len); - } - if (dbuf_error(data_buf)) - goto fail; - if (binary_flag) { - response = JS_NewArrayBufferCopy(ctx, - data_buf->buf, data_buf->size); - } else { - response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); - } - if (JS_IsException(response)) - goto fail; - done: - js_free(ctx, buf); - buf = NULL; - pclose(f); - f = NULL; - dbuf_free(data_buf); - data_buf = NULL; - - if (full_flag) { - ret_obj = JS_NewObject(ctx); - if (JS_IsException(ret_obj)) - goto fail; - JS_DefinePropertyValueStr(ctx, ret_obj, "response", - response, - JS_PROP_C_W_E); - if (!JS_IsNull(response)) { - JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders", - JS_NewStringLen(ctx, (char *)header_buf->buf, - header_buf->size), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, ret_obj, "status", - JS_NewInt32(ctx, status), - JS_PROP_C_W_E); - } - } else { - ret_obj = response; - } - dbuf_free(header_buf); - return ret_obj; - fail: - if (f) - pclose(f); - js_free(ctx, buf); - if (data_buf) - dbuf_free(data_buf); - if (header_buf) - dbuf_free(header_buf); - JS_FreeValue(ctx, response); - return JS_EXCEPTION; -} -#endif // !defined(__wasi__) - -static JSClassDef js_std_file_class = { - "FILE", - .finalizer = js_std_file_finalizer, -}; - -static const JSCFunctionListEntry js_std_error_props[] = { - /* various errno values */ -#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) - DEF(EINVAL), - DEF(EIO), - DEF(EACCES), - DEF(EEXIST), - DEF(ENOSPC), - DEF(ENOSYS), - DEF(EBUSY), - DEF(ENOENT), - DEF(EPERM), - DEF(EPIPE), - DEF(EBADF), -#undef DEF -}; - -static const JSCFunctionListEntry js_std_funcs[] = { - JS_CFUNC_DEF("exit", 1, js_std_exit ), - JS_CFUNC_DEF("gc", 0, js_std_gc ), - JS_CFUNC_DEF("evalScript", 1, js_evalScript ), - JS_CFUNC_DEF("loadScript", 1, js_loadScript ), - JS_CFUNC_DEF("getenv", 1, js_std_getenv ), - JS_CFUNC_DEF("setenv", 1, js_std_setenv ), - JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), - JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), -#if !defined(__wasi__) - JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), -#endif - JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), - JS_CFUNC_DEF("writeFile", 2, js_std_writeFile ), - JS_CFUNC_DEF("strerror", 1, js_std_strerror ), - - /* FILE I/O */ - JS_CFUNC_DEF("open", 2, js_std_open ), -#if !defined(__wasi__) - JS_CFUNC_DEF("popen", 2, js_std_popen ), - JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), -#endif - JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), - JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ), - JS_CFUNC_DEF("printf", 1, js_std_printf ), - JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ), - JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), - JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), -}; - -static const JSCFunctionListEntry js_std_file_proto_funcs[] = { - JS_CFUNC_DEF("close", 0, js_std_file_close ), - JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), - JS_CFUNC_DEF("printf", 1, js_std_file_printf ), - JS_CFUNC_DEF("flush", 0, js_std_file_flush ), - JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ), - JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ), - JS_CFUNC_DEF("seek", 2, js_std_file_seek ), - JS_CFUNC_DEF("eof", 0, js_std_file_eof ), - JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ), - JS_CFUNC_DEF("error", 0, js_std_file_error ), - JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ), - JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ), - JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ), - JS_CFUNC_DEF("getline", 0, js_std_file_getline ), - JS_CFUNC_MAGIC_DEF("readAsArrayBuffer", 0, js_std_file_readAs, 0 ), - JS_CFUNC_MAGIC_DEF("readAsString", 0, js_std_file_readAs, 1 ), - JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ), - JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ), - /* setvbuf, ... */ -}; - -static int js_std_init(JSContext *ctx, JSModuleDef *m) -{ - JSValue proto; - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - - /* FILE class */ - /* the class ID is created once */ - JS_NewClassID(rt, &ts->std_file_class_id); - /* the class is created once per runtime */ - JS_NewClass(rt, ts->std_file_class_id, &js_std_file_class); - proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, - countof(js_std_file_proto_funcs)); - JS_SetClassProto(ctx, ts->std_file_class_id, proto); - - JS_SetModuleExportList(ctx, m, js_std_funcs, - countof(js_std_funcs)); - JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, false)); - JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, false)); - JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, false)); - return 0; -} - -JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_std_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); - JS_AddModuleExport(ctx, m, "in"); - JS_AddModuleExport(ctx, m, "out"); - JS_AddModuleExport(ctx, m, "err"); - return m; -} - -/**********************************************************/ -/* 'os' object */ - -static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - int flags, mode, ret; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &flags, argv[1])) - goto fail; - if (argc >= 3 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32(ctx, &mode, argv[2])) { - fail: - JS_FreeCString(ctx, filename); - return JS_EXCEPTION; - } - } else { - mode = 0666; - } -#if defined(_WIN32) - /* force binary mode by default */ - if (!(flags & O_TEXT)) - flags |= O_BINARY; -#endif - ret = js_get_errno(open(filename, flags, mode)); - JS_FreeCString(ctx, filename); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, ret; - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - ret = js_get_errno(close(fd)); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, whence; - int64_t pos, ret; - bool is_bigint; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - is_bigint = JS_IsBigInt(argv[1]); - if (JS_ToInt64Ext(ctx, &pos, argv[1])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &whence, argv[2])) - return JS_EXCEPTION; - ret = lseek(fd, pos, whence); - if (ret == -1) - ret = -errno; - if (is_bigint) - return JS_NewBigInt64(ctx, ret); - else - return JS_NewInt64(ctx, ret); -} - -static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - int fd; - uint64_t pos, len; - size_t size; - ssize_t ret; - uint8_t *buf; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &pos, argv[2])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[3])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[1]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); - if (magic) - ret = js_get_errno(write(fd, buf + pos, len)); - else - ret = js_get_errno(read(fd, buf + pos, len)); - return JS_NewInt64(ctx, ret); -} - -static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - return JS_NewBool(ctx, (isatty(fd) != 0)); -} - -#if defined(_WIN32) -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - HANDLE handle; - CONSOLE_SCREEN_BUFFER_INFO info; - JSValue obj; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - handle = (HANDLE)_get_osfhandle(fd); - - if (!GetConsoleScreenBufferInfo(handle, &info)) - return JS_NULL; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); - return obj; -} - -/* Windows 10 built-in VT100 emulation */ -#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 -#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 - -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - HANDLE handle; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - handle = (HANDLE)_get_osfhandle(fd); - SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); - _setmode(fd, _O_BINARY); - if (fd == 0) { - handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ - SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); - } - return JS_UNDEFINED; -} -#elif !defined(__wasi__) -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - struct winsize ws; - JSValue obj; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && - ws.ws_col >= 4 && ws.ws_row >= 4) { - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); - return obj; - } else { - return JS_NULL; - } -} - -static struct termios oldtty; - -static void term_exit(void) -{ - tcsetattr(0, TCSANOW, &oldtty); -} - -/* XXX: should add a way to go back to normal mode */ -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - struct termios tty; - int fd; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - - memset(&tty, 0, sizeof(tty)); - tcgetattr(fd, &tty); - oldtty = tty; - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP - |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); - tty.c_cflag &= ~(CSIZE|PARENB); - tty.c_cflag |= CS8; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - - tcsetattr(fd, TCSANOW, &tty); - - atexit(term_exit); - return JS_UNDEFINED; -} - -#endif /* !_WIN32 && !__wasi__ */ - -static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - int ret; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; -#if defined(_WIN32) - { - struct stat st; - if (stat(filename, &st) == 0 && (st.st_mode & _S_IFDIR)) { - ret = rmdir(filename); - } else { - ret = unlink(filename); - } - } -#else - ret = remove(filename); -#endif - ret = js_get_errno(ret); - JS_FreeCString(ctx, filename); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *oldpath, *newpath; - int ret; - - oldpath = JS_ToCString(ctx, argv[0]); - if (!oldpath) - return JS_EXCEPTION; - newpath = JS_ToCString(ctx, argv[1]); - if (!newpath) { - JS_FreeCString(ctx, oldpath); - return JS_EXCEPTION; - } - ret = js_get_errno(rename(oldpath, newpath)); - JS_FreeCString(ctx, oldpath); - JS_FreeCString(ctx, newpath); - return JS_NewInt32(ctx, ret); -} - -static bool is_main_thread(JSRuntime *rt) -{ - JSThreadState *ts = js_get_thread_state(rt); - return !ts->recv_pipe; -} - -static JSOSRWHandler *find_rh(JSThreadState *ts, int fd) -{ - JSOSRWHandler *rh; - struct list_head *el; - - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == fd) - return rh; - } - return NULL; -} - -static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) -{ - int i; - list_del(&rh->link); - for(i = 0; i < 2; i++) { - JS_FreeValueRT(rt, rh->rw_func[i]); - } - js_free_rt(rt, rh); -} - -static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSOSRWHandler *rh; - int fd; - JSValueConst func; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - func = argv[1]; - if (JS_IsNull(func)) { - rh = find_rh(ts, fd); - if (rh) { - JS_FreeValue(ctx, rh->rw_func[magic]); - rh->rw_func[magic] = JS_NULL; - if (JS_IsNull(rh->rw_func[0]) && - JS_IsNull(rh->rw_func[1])) { - /* remove the entry */ - free_rw_handler(JS_GetRuntime(ctx), rh); - } - } - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - rh = find_rh(ts, fd); - if (!rh) { - rh = js_mallocz(ctx, sizeof(*rh)); - if (!rh) - return JS_EXCEPTION; - rh->fd = fd; - rh->rw_func[0] = JS_NULL; - rh->rw_func[1] = JS_NULL; - list_add_tail(&rh->link, &ts->os_rw_handlers); - } - JS_FreeValue(ctx, rh->rw_func[magic]); - rh->rw_func[magic] = JS_DupValue(ctx, func); - } - return JS_UNDEFINED; -} - -static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) -{ - JSOSSignalHandler *sh; - struct list_head *el; - list_for_each(el, &ts->os_signal_handlers) { - sh = list_entry(el, JSOSSignalHandler, link); - if (sh->sig_num == sig_num) - return sh; - } - return NULL; -} - -static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) -{ - list_del(&sh->link); - JS_FreeValueRT(rt, sh->func); - js_free_rt(rt, sh); -} - -static void os_signal_handler(int sig_num) -{ - os_pending_signals |= ((uint64_t)1 << sig_num); -} - -#if defined(_WIN32) -typedef void (*sighandler_t)(int sig_num); -#endif - -static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSOSSignalHandler *sh; - uint32_t sig_num; - JSValueConst func; - sighandler_t handler; - - if (!is_main_thread(rt)) - return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); - - if (JS_ToUint32(ctx, &sig_num, argv[0])) - return JS_EXCEPTION; - if (sig_num >= 64) - return JS_ThrowRangeError(ctx, "invalid signal number"); - func = argv[1]; - /* func = null: SIG_DFL, func = undefined, SIG_IGN */ - if (JS_IsNull(func) || JS_IsUndefined(func)) { - sh = find_sh(ts, sig_num); - if (sh) { - free_sh(JS_GetRuntime(ctx), sh); - } - if (JS_IsNull(func)) - handler = SIG_DFL; - else - handler = SIG_IGN; - signal(sig_num, handler); - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - sh = find_sh(ts, sig_num); - if (!sh) { - sh = js_mallocz(ctx, sizeof(*sh)); - if (!sh) - return JS_EXCEPTION; - sh->sig_num = sig_num; - list_add_tail(&sh->link, &ts->os_signal_handlers); - } - JS_FreeValue(ctx, sh->func); - sh->func = JS_DupValue(ctx, func); - signal(sig_num, os_signal_handler); - } - return JS_UNDEFINED; -} - -#if !defined(_WIN32) && !defined(__wasi__) -static JSValue js_os_cputime(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - struct rusage ru; - int64_t cputime; - - cputime = 0; - if (!getrusage(RUSAGE_SELF, &ru)) - cputime = (int64_t)ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec; - - return JS_NewInt64(ctx, cputime); -} -#endif - -static JSValue js_os_exepath(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - char buf[JS__PATH_MAX]; - size_t len = sizeof(buf); - if (js_exepath(buf, &len)) - return JS_UNDEFINED; - return JS_NewStringLen(ctx, buf, len); -} - -static JSValue js_os_now(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewInt64(ctx, js__hrtime_ns() / 1000); -} - -static uint64_t js__hrtime_ms(void) -{ - return js__hrtime_ns() / (1000 * 1000); -} - -static void free_timer(JSRuntime *rt, JSOSTimer *th) -{ - list_del(&th->link); - JS_FreeValueRT(rt, th->func); - js_free_rt(rt, th); -} - -// TODO(bnoordhuis) accept string as first arg and eval at timer expiry -// TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 -static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - int64_t delay; - JSValueConst func; - JSOSTimer *th; - - func = argv[0]; - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - if (JS_ToInt64(ctx, &delay, argv[1])) - return JS_EXCEPTION; - if (delay < 1) - delay = 1; - th = js_mallocz(ctx, sizeof(*th)); - if (!th) - return JS_EXCEPTION; - th->timer_id = ts->next_timer_id++; - if (ts->next_timer_id > MAX_SAFE_INTEGER) - ts->next_timer_id = 1; - th->repeats = (magic > 0); - th->timeout = js__hrtime_ms() + delay; - th->delay = delay; - th->func = JS_DupValue(ctx, func); - list_add_tail(&th->link, &ts->os_timers); - return JS_NewInt64(ctx, th->timer_id); -} - -static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) -{ - struct list_head *el; - if (timer_id <= 0) - return NULL; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - if (th->timer_id == timer_id) - return th; - } - return NULL; -} - -static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSOSTimer *th; - int64_t timer_id; - - if (JS_ToInt64(ctx, &timer_id, argv[0])) - return JS_EXCEPTION; - th = find_timer_by_id(ts, timer_id); - if (!th) - return JS_UNDEFINED; - free_timer(rt, th); - return JS_UNDEFINED; -} - -/* return a promise */ -static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - int64_t delay; - JSOSTimer *th; - JSValue promise, resolving_funcs[2]; - - if (JS_ToInt64(ctx, &delay, argv[0])) - return JS_EXCEPTION; - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - - th = js_mallocz(ctx, sizeof(*th)); - if (!th) { - JS_FreeValue(ctx, promise); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return JS_EXCEPTION; - } - th->timer_id = -1; - th->timeout = js__hrtime_ms() + delay; - th->func = JS_DupValue(ctx, resolving_funcs[0]); - list_add_tail(&th->link, &ts->os_timers); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; -} - -static int call_handler(JSContext *ctx, JSValue func) -{ - int r; - JSValue ret, func1; - /* 'func' might be destroyed when calling itself (if it frees the - handler), so must take extra care */ - func1 = JS_DupValue(ctx, func); - ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); - JS_FreeValue(ctx, func1); - r = 0; - if (JS_IsException(ret)) - r = -1; - JS_FreeValue(ctx, ret); - return r; -} - -static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, int *min_delay) -{ - JSValue func; - JSOSTimer *th; - int64_t cur_time, delay; - struct list_head *el; - int r; - - if (list_empty(&ts->os_timers)) { - *min_delay = -1; - return 0; - } - - cur_time = js__hrtime_ms(); - *min_delay = INT32_MAX; - - list_for_each(el, &ts->os_timers) { - th = list_entry(el, JSOSTimer, link); - delay = th->timeout - cur_time; - if (delay > 0) { - *min_delay = min_int(*min_delay, delay); - } else { - *min_delay = 0; - func = JS_DupValueRT(rt, th->func); - if (th->repeats) - th->timeout = cur_time + th->delay; - else - free_timer(rt, th); - r = call_handler(ctx, func); - JS_FreeValueRT(rt, func); - return r; - } - } - - return 0; -} - -#ifdef USE_WORKER - -#ifdef _WIN32 - -static int js_waker_init(JSWaker *w) -{ - w->handle = CreateEvent(NULL, TRUE, FALSE, NULL); - return w->handle ? 0 : -1; -} - -static void js_waker_signal(JSWaker *w) -{ - SetEvent(w->handle); -} - -static void js_waker_clear(JSWaker *w) -{ - ResetEvent(w->handle); -} - -static void js_waker_close(JSWaker *w) -{ - CloseHandle(w->handle); - w->handle = INVALID_HANDLE_VALUE; -} - -#else // !_WIN32 - -static int js_waker_init(JSWaker *w) -{ - int fds[2]; - - if (pipe(fds) < 0) - return -1; - w->read_fd = fds[0]; - w->write_fd = fds[1]; - return 0; -} - -static void js_waker_signal(JSWaker *w) -{ - int ret; - - for(;;) { - ret = write(w->write_fd, "", 1); - if (ret == 1) - break; - if (ret < 0 && (errno != EAGAIN || errno != EINTR)) - break; - } -} - -static void js_waker_clear(JSWaker *w) -{ - uint8_t buf[16]; - int ret; - - for(;;) { - ret = read(w->read_fd, buf, sizeof(buf)); - if (ret >= 0) - break; - if (errno != EAGAIN && errno != EINTR) - break; - } -} - -static void js_waker_close(JSWaker *w) -{ - close(w->read_fd); - close(w->write_fd); - w->read_fd = -1; - w->write_fd = -1; -} - -#endif // _WIN32 - -static void js_free_message(JSWorkerMessage *msg); - -/* return 1 if a message was handled, 0 if no message */ -static int handle_posted_message(JSRuntime *rt, JSContext *ctx, - JSWorkerMessageHandler *port) -{ - JSWorkerMessagePipe *ps = port->recv_pipe; - int ret; - struct list_head *el; - JSWorkerMessage *msg; - JSValue obj, data_obj, func, retval; - - js_mutex_lock(&ps->mutex); - if (!list_empty(&ps->msg_queue)) { - el = ps->msg_queue.next; - msg = list_entry(el, JSWorkerMessage, link); - - /* remove the message from the queue */ - list_del(&msg->link); - - // drain read end of pipe - if (list_empty(&ps->msg_queue)) - js_waker_clear(&ps->waker); - - js_mutex_unlock(&ps->mutex); - - data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, - JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); - - js_free_message(msg); - - if (JS_IsException(data_obj)) - goto fail; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, data_obj); - goto fail; - } - JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E); - - /* 'func' might be destroyed when calling itself (if it frees the - handler), so must take extra care */ - func = JS_DupValue(ctx, port->on_message_func); - retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj); - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, func); - if (JS_IsException(retval)) { - fail: - js_std_dump_error(ctx); - } else { - JS_FreeValue(ctx, retval); - } - ret = 1; - } else { - js_mutex_unlock(&ps->mutex); - ret = 0; - } - return ret; -} - -#endif // USE_WORKER - -#if defined(_WIN32) -static int js_os_poll(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - int min_delay, count; - JSOSRWHandler *rh; - struct list_head *el; - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 - - /* XXX: handle signals if useful */ - - if (js_os_run_timers(rt, ctx, ts, &min_delay)) - return -1; - if (min_delay == 0) - return 0; // expired timer - if (min_delay < 0) - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) - return -1; /* no more events */ - - count = 0; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) - handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin - if (count == (int)countof(handles)) - break; - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (JS_IsNull(port->on_message_func)) - continue; - handles[count++] = port->recv_pipe->waker.handle; - if (count == (int)countof(handles)) - break; - } - - if (count > 0) { - DWORD ret, timeout = INFINITE; - if (min_delay != -1) - timeout = min_delay; - ret = WaitForMultipleObjects(count, handles, FALSE, timeout); - if (ret < count) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - return call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - } - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - if (ps->waker.handle == handles[ret]) { - if (handle_posted_message(rt, ctx, port)) - goto done; - } - } - } - } - } else { - Sleep(min_delay); - } -done: - return 0; -} -#else // !defined(_WIN32) -static int js_os_poll(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - int r, w, ret, nfds, min_delay; - JSOSRWHandler *rh; - struct list_head *el; - struct pollfd *pfd, *pfds, pfds_local[64]; - - /* only check signals in the main thread */ - if (!ts->recv_pipe && - unlikely(os_pending_signals != 0)) { - JSOSSignalHandler *sh; - uint64_t mask; - - list_for_each(el, &ts->os_signal_handlers) { - sh = list_entry(el, JSOSSignalHandler, link); - mask = (uint64_t)1 << sh->sig_num; - if (os_pending_signals & mask) { - os_pending_signals &= ~mask; - return call_handler(ctx, sh->func); - } - } - } - - if (js_os_run_timers(rt, ctx, ts, &min_delay)) - return -1; - if (min_delay == 0) - return 0; // expired timer - if (min_delay < 0) - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) - return -1; /* no more events */ - - nfds = 0; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - nfds += (!JS_IsNull(rh->rw_func[0]) || !JS_IsNull(rh->rw_func[1])); - } - -#ifdef USE_WORKER - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - nfds += !JS_IsNull(port->on_message_func); - } -#endif // USE_WORKER - - pfd = pfds = pfds_local; - if (nfds > (int)countof(pfds_local)) { - pfd = pfds = js_malloc(ctx, nfds * sizeof(*pfd)); - if (!pfd) - return -1; - } - - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - r = POLLIN * !JS_IsNull(rh->rw_func[0]); - w = POLLOUT * !JS_IsNull(rh->rw_func[1]); - if (r || w) - *pfd++ = (struct pollfd){rh->fd, r|w, 0}; - } - -#ifdef USE_WORKER - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - *pfd++ = (struct pollfd){ps->waker.read_fd, POLLIN, 0}; - } - } -#endif // USE_WORKER - - // FIXME(bnoordhuis) the loop below is quadratic in theory but - // linear-ish in practice because we bail out on the first hit, - // i.e., it's probably good enough for now - ret = 0; - nfds = poll(pfds, nfds, min_delay); - for (pfd = pfds; nfds-- > 0; pfd++) { - rh = find_rh(ts, pfd->fd); - if (rh) { - r = (POLLERR|POLLHUP|POLLNVAL|POLLIN) * !JS_IsNull(rh->rw_func[0]); - w = (POLLERR|POLLHUP|POLLNVAL|POLLOUT) * !JS_IsNull(rh->rw_func[1]); - if (r & pfd->revents) { - ret = call_handler(ctx, rh->rw_func[0]); - goto done; - /* must stop because the list may have been modified */ - } - if (w & pfd->revents) { - ret = call_handler(ctx, rh->rw_func[1]); - goto done; - /* must stop because the list may have been modified */ - } - } else { -#ifdef USE_WORKER - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - if (pfd->fd == ps->waker.read_fd) { - if (handle_posted_message(rt, ctx, port)) - goto done; - } - } - } -#endif // USE_WORKER - } - } -done: - if (pfds != pfds_local) - js_free(ctx, pfds); - return ret; -} -#endif // defined(_WIN32) - - -static JSValue make_obj_error(JSContext *ctx, - JSValue obj, - int err) -{ - JSValue arr; - if (JS_IsException(obj)) - return obj; - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return JS_EXCEPTION; - JS_DefinePropertyValueUint32(ctx, arr, 0, obj, - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), - JS_PROP_C_W_E); - return arr; -} - -static JSValue make_string_error(JSContext *ctx, - const char *buf, - int err) -{ - return make_obj_error(ctx, JS_NewString(ctx, buf), err); -} - -/* return [cwd, errorcode] */ -static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - char buf[JS__PATH_MAX]; - int err; - - if (!getcwd(buf, sizeof(buf))) { - buf[0] = '\0'; - err = errno; - } else { - err = 0; - } - return make_string_error(ctx, buf, err); -} - -static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *target; - int err; - - target = JS_ToCString(ctx, argv[0]); - if (!target) - return JS_EXCEPTION; - err = js_get_errno(chdir(target)); - JS_FreeCString(ctx, target); - return JS_NewInt32(ctx, err); -} - -static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int mode, ret; - const char *path; - - if (argc >= 2) { - if (JS_ToInt32(ctx, &mode, argv[1])) - return JS_EXCEPTION; - } else { - mode = 0777; - } - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if defined(_WIN32) - (void)mode; - ret = js_get_errno(mkdir(path)); -#else - ret = js_get_errno(mkdir(path, mode)); -#endif - JS_FreeCString(ctx, path); - return JS_NewInt32(ctx, ret); -} - -/* return [array, errorcode] */ -static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ -#ifdef _WIN32 - const char *path; - JSValue obj; - int err; - uint32_t len; - HANDLE h; - WIN32_FIND_DATAA d; - char s[1024]; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) { - JS_FreeCString(ctx, path); - return JS_EXCEPTION; - } - snprintf(s, sizeof(s), "%s/*", path); - JS_FreeCString(ctx, path); - err = 0; - h = FindFirstFileA(s, &d); - if (h == INVALID_HANDLE_VALUE) - err = GetLastError(); - if (err) - goto done; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewString(ctx, "."), - JS_PROP_C_W_E); - for (len = 1; FindNextFileA(h, &d); len++) { - JS_DefinePropertyValueUint32(ctx, obj, len, - JS_NewString(ctx, d.cFileName), - JS_PROP_C_W_E); - } - FindClose(h); -done: - return make_obj_error(ctx, obj, err); -#else - const char *path; - DIR *f; - struct dirent *d; - JSValue obj; - int err; - uint32_t len; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) { - JS_FreeCString(ctx, path); - return JS_EXCEPTION; - } - f = opendir(path); - if (!f) - err = errno; - else - err = 0; - JS_FreeCString(ctx, path); - if (!f) - goto done; - len = 0; - for(;;) { - errno = 0; - d = readdir(f); - if (!d) { - err = errno; - break; - } - JS_DefinePropertyValueUint32(ctx, obj, len++, - JS_NewString(ctx, d->d_name), - JS_PROP_C_W_E); - } - closedir(f); - done: - return make_obj_error(ctx, obj, err); -#endif -} - -#if !defined(_WIN32) -static int64_t timespec_to_ms(const struct timespec *tv) -{ - return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); -} -#endif - -/* return [obj, errcode] */ -static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_lstat) -{ - const char *path; - int err, res; - struct stat st; - JSValue obj; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if defined(_WIN32) - res = stat(path, &st); -#else - if (is_lstat) - res = lstat(path, &st); - else - res = stat(path, &st); -#endif - err = (res < 0) ? errno : 0; - JS_FreeCString(ctx, path); - if (res < 0) { - obj = JS_NULL; - } else { - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - JS_DefinePropertyValueStr(ctx, obj, "dev", - JS_NewInt64(ctx, st.st_dev), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ino", - JS_NewInt64(ctx, st.st_ino), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mode", - JS_NewInt32(ctx, st.st_mode), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "nlink", - JS_NewInt64(ctx, st.st_nlink), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "uid", - JS_NewInt64(ctx, st.st_uid), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "gid", - JS_NewInt64(ctx, st.st_gid), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "rdev", - JS_NewInt64(ctx, st.st_rdev), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "size", - JS_NewInt64(ctx, st.st_size), - JS_PROP_C_W_E); -#if !defined(_WIN32) - JS_DefinePropertyValueStr(ctx, obj, "blocks", - JS_NewInt64(ctx, st.st_blocks), - JS_PROP_C_W_E); -#endif -#if defined(_WIN32) - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, (int64_t)st.st_atime * 1000), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000), - JS_PROP_C_W_E); -#elif defined(__APPLE__) - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), - JS_PROP_C_W_E); -#else - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), - JS_PROP_C_W_E); -#endif - } - return make_obj_error(ctx, obj, err); -} - -#if !defined(_WIN32) -static void ms_to_timeval(struct timeval *tv, uint64_t v) -{ - tv->tv_sec = v / 1000; - tv->tv_usec = (v % 1000) * 1000; -} -#endif - -static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - int64_t atime, mtime; - int ret; - - if (JS_ToInt64(ctx, &atime, argv[1])) - return JS_EXCEPTION; - if (JS_ToInt64(ctx, &mtime, argv[2])) - return JS_EXCEPTION; - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if defined(_WIN32) - { - struct _utimbuf times; - times.actime = atime / 1000; - times.modtime = mtime / 1000; - ret = js_get_errno(_utime(path, ×)); - } -#else - { - struct timeval times[2]; - ms_to_timeval(×[0], atime); - ms_to_timeval(×[1], mtime); - ret = js_get_errno(utimes(path, times)); - } -#endif - JS_FreeCString(ctx, path); - return JS_NewInt32(ctx, ret); -} - -/* sleep(delay_ms) */ -static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t delay; - int ret; - - if (JS_ToInt64(ctx, &delay, argv[0])) - return JS_EXCEPTION; - if (delay < 0) - delay = 0; -#if defined(_WIN32) - { - if (delay > INT32_MAX) - delay = INT32_MAX; - Sleep(delay); - ret = 0; - } -#else - { - struct timespec ts; - - ts.tv_sec = delay / 1000; - ts.tv_nsec = (delay % 1000) * 1000000; - ret = js_get_errno(nanosleep(&ts, NULL)); - } -#endif - return JS_NewInt32(ctx, ret); -} - -#if defined(_WIN32) -static char *realpath(const char *path, char *buf) -{ - if (!_fullpath(buf, path, JS__PATH_MAX)) { - errno = ENOENT; - return NULL; - } else { - return buf; - } -} -#endif - -#if !defined(__wasi__) -/* return [path, errorcode] */ -static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - char buf[JS__PATH_MAX], *res; - int err; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - res = realpath(path, buf); - JS_FreeCString(ctx, path); - if (!res) { - buf[0] = '\0'; - err = errno; - } else { - err = 0; - } - return make_string_error(ctx, buf, err); -} -#endif - -#if !defined(_WIN32) && !defined(__wasi__) && !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) -static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *target, *linkpath; - int err; - - target = JS_ToCString(ctx, argv[0]); - if (!target) - return JS_EXCEPTION; - linkpath = JS_ToCString(ctx, argv[1]); - if (!linkpath) { - JS_FreeCString(ctx, target); - return JS_EXCEPTION; - } - err = js_get_errno(symlink(target, linkpath)); - JS_FreeCString(ctx, target); - JS_FreeCString(ctx, linkpath); - return JS_NewInt32(ctx, err); -} - -/* return [path, errorcode] */ -static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - char buf[JS__PATH_MAX]; - int err; - ssize_t res; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - res = readlink(path, buf, sizeof(buf) - 1); - if (res < 0) { - buf[0] = '\0'; - err = errno; - } else { - buf[res] = '\0'; - err = 0; - } - JS_FreeCString(ctx, path); - return make_string_error(ctx, buf, err); -} - -static char **build_envp(JSContext *ctx, JSValue obj) -{ - uint32_t len, i; - JSPropertyEnum *tab; - char **envp, *pair; - const char *key, *str; - JSValue val; - size_t key_len, str_len; - - if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) - return NULL; - envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); - if (!envp) - goto fail; - for(i = 0; i < len; i++) { - val = JS_GetProperty(ctx, obj, tab[i].atom); - if (JS_IsException(val)) - goto fail; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) - goto fail; - key = JS_AtomToCString(ctx, tab[i].atom); - if (!key) { - JS_FreeCString(ctx, str); - goto fail; - } - key_len = strlen(key); - str_len = strlen(str); - pair = js_malloc(ctx, key_len + str_len + 2); - if (!pair) { - JS_FreeCString(ctx, key); - JS_FreeCString(ctx, str); - goto fail; - } - memcpy(pair, key, key_len); - pair[key_len] = '='; - memcpy(pair + key_len + 1, str, str_len); - pair[key_len + 1 + str_len] = '\0'; - envp[i] = pair; - JS_FreeCString(ctx, key); - JS_FreeCString(ctx, str); - } - done: - for(i = 0; i < len; i++) - JS_FreeAtom(ctx, tab[i].atom); - js_free(ctx, tab); - return envp; - fail: - if (envp) { - for(i = 0; i < len; i++) - js_free(ctx, envp[i]); - js_free(ctx, envp); - envp = NULL; - } - goto done; -} - -/* execvpe is not available on non GNU systems */ -static int my_execvpe(const char *filename, char **argv, char **envp) -{ - char *path, *p, *p_next, *p1; - char buf[JS__PATH_MAX]; - size_t filename_len, path_len; - bool eacces_error; - - filename_len = strlen(filename); - if (filename_len == 0) { - errno = ENOENT; - return -1; - } - if (strchr(filename, '/')) - return execve(filename, argv, envp); - - path = getenv("PATH"); - if (!path) - path = (char *)"/bin:/usr/bin"; - eacces_error = false; - p = path; - for(p = path; p != NULL; p = p_next) { - p1 = strchr(p, ':'); - if (!p1) { - p_next = NULL; - path_len = strlen(p); - } else { - p_next = p1 + 1; - path_len = p1 - p; - } - /* path too long */ - if ((path_len + 1 + filename_len + 1) > JS__PATH_MAX) - continue; - memcpy(buf, p, path_len); - buf[path_len] = '/'; - memcpy(buf + path_len + 1, filename, filename_len); - buf[path_len + 1 + filename_len] = '\0'; - - execve(buf, argv, envp); - - switch(errno) { - case EACCES: - eacces_error = true; - break; - case ENOENT: - case ENOTDIR: - break; - default: - return -1; - } - } - if (eacces_error) - errno = EACCES; - return -1; -} - -static void (*js_os_exec_closefrom)(int); - -#if !defined(EMSCRIPTEN) && !defined(__wasi__) - -static js_once_t js_os_exec_once = JS_ONCE_INIT; - -static void js_os_exec_once_init(void) -{ - *(void **) (&js_os_exec_closefrom) = dlsym(RTLD_DEFAULT, "closefrom"); -} - -#endif - -/* exec(args[, options]) -> exitcode */ -static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst options, args = argv[0]; - JSValue val, ret_val; - const char **exec_argv, *file = NULL, *str, *cwd = NULL; - char **envp = environ; - uint32_t exec_argc, i; - int ret, pid, status; - bool block_flag = true, use_path = true; - static const char *std_name[3] = { "stdin", "stdout", "stderr" }; - int std_fds[3]; - uint32_t uid = -1, gid = -1; - int ngroups = -1; - gid_t groups[64]; - - val = JS_GetPropertyStr(ctx, args, "length"); - if (JS_IsException(val)) - return JS_EXCEPTION; - ret = JS_ToUint32(ctx, &exec_argc, val); - JS_FreeValue(ctx, val); - if (ret) - return JS_EXCEPTION; - /* arbitrary limit to avoid overflow */ - if (exec_argc < 1 || exec_argc > 65535) { - return JS_ThrowTypeError(ctx, "invalid number of arguments"); - } - exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); - if (!exec_argv) - return JS_EXCEPTION; - for(i = 0; i < exec_argc; i++) { - val = JS_GetPropertyUint32(ctx, args, i); - if (JS_IsException(val)) - goto exception; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) - goto exception; - exec_argv[i] = str; - } - exec_argv[exec_argc] = NULL; - - for(i = 0; i < 3; i++) - std_fds[i] = i; - - /* get the options, if any */ - if (argc >= 2) { - options = argv[1]; - - if (get_bool_option(ctx, &block_flag, options, "block")) - goto exception; - if (get_bool_option(ctx, &use_path, options, "usePath")) - goto exception; - - val = JS_GetPropertyStr(ctx, options, "file"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - file = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!file) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "cwd"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - cwd = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!cwd) - goto exception; - } - - /* stdin/stdout/stderr handles */ - for(i = 0; i < 3; i++) { - val = JS_GetPropertyStr(ctx, options, std_name[i]); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - int fd; - ret = JS_ToInt32(ctx, &fd, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - std_fds[i] = fd; - } - } - - val = JS_GetPropertyStr(ctx, options, "env"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - envp = build_envp(ctx, val); - JS_FreeValue(ctx, val); - if (!envp) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "uid"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - ret = JS_ToUint32(ctx, &uid, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "gid"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - ret = JS_ToUint32(ctx, &gid, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "groups"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - int64_t idx, len; - JSValue prop; - uint32_t id; - ngroups = 0; - if (JS_GetLength(ctx, val, &len)) { - JS_FreeValue(ctx, val); - goto exception; - } - for (idx = 0; idx < len; idx++) { - prop = JS_GetPropertyInt64(ctx, val, idx); - if (JS_IsException(prop)) - break; - if (JS_IsUndefined(prop)) - continue; - ret = JS_ToUint32(ctx, &id, prop); - JS_FreeValue(ctx, prop); - if (ret) - break; - if (ngroups == countof(groups)) { - JS_ThrowRangeError(ctx, "too many groups"); - break; - } - groups[ngroups++] = id; - } - JS_FreeValue(ctx, val); - if (idx < len) - goto exception; - } - - } - -#if !defined(EMSCRIPTEN) && !defined(__wasi__) - // should happen pre-fork because it calls dlsym() - // and that's not an async-signal-safe function - js_once(&js_os_exec_once, js_os_exec_once_init); -#endif - - pid = fork(); - if (pid < 0) { - JS_ThrowTypeError(ctx, "fork error"); - goto exception; - } - if (pid == 0) { - /* child */ - /* remap the stdin/stdout/stderr handles if necessary */ - for(i = 0; i < 3; i++) { - if (std_fds[i] != i) { - if (dup2(std_fds[i], i) < 0) - _exit(127); - } - } - - if (js_os_exec_closefrom) { - js_os_exec_closefrom(3); - } else { - int fd_max = sysconf(_SC_OPEN_MAX); - for(i = 3; i < fd_max; i++) - close(i); - } - - if (cwd) { - if (chdir(cwd) < 0) - _exit(127); - } - if (ngroups != -1) { - if (setgroups(ngroups, groups) < 0) - _exit(127); - } - if (uid != -1) { - if (setuid(uid) < 0) - _exit(127); - } - if (gid != -1) { - if (setgid(gid) < 0) - _exit(127); - } - - if (!file) - file = exec_argv[0]; - if (use_path) - ret = my_execvpe(file, (char **)exec_argv, envp); - else - ret = execve(file, (char **)exec_argv, envp); - _exit(127); - } - /* parent */ - if (block_flag) { - for(;;) { - ret = waitpid(pid, &status, 0); - if (ret == pid) { - if (WIFEXITED(status)) { - ret = WEXITSTATUS(status); - break; - } else if (WIFSIGNALED(status)) { - ret = -WTERMSIG(status); - break; - } - } - } - } else { - ret = pid; - } - ret_val = JS_NewInt32(ctx, ret); - done: - JS_FreeCString(ctx, file); - JS_FreeCString(ctx, cwd); - for(i = 0; i < exec_argc; i++) - JS_FreeCString(ctx, exec_argv[i]); - js_free(ctx, exec_argv); - if (envp != environ) { - char **p; - p = envp; - while (*p != NULL) { - js_free(ctx, *p); - p++; - } - js_free(ctx, envp); - } - return ret_val; - exception: - ret_val = JS_EXCEPTION; - goto done; -} - -/* getpid() -> pid */ -static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewInt32(ctx, getpid()); -} - -/* waitpid(pid, block) -> [pid, status] */ -static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, status, options, ret; - JSValue obj; - - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &options, argv[1])) - return JS_EXCEPTION; - - ret = waitpid(pid, &status, options); - if (ret < 0) { - ret = -errno; - status = 0; - } - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), - JS_PROP_C_W_E); - return obj; -} - -/* pipe() -> [read_fd, write_fd] or null if error */ -static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pipe_fds[2], ret; - JSValue obj; - - ret = pipe(pipe_fds); - if (ret < 0) - return JS_NULL; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), - JS_PROP_C_W_E); - return obj; -} - -/* kill(pid, sig) */ -static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, sig, ret; - - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &sig, argv[1])) - return JS_EXCEPTION; - ret = js_get_errno(kill(pid, sig)); - return JS_NewInt32(ctx, ret); -} - -/* dup(fd) */ -static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, ret; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - ret = js_get_errno(dup(fd)); - return JS_NewInt32(ctx, ret); -} - -/* dup2(fd) */ -static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, fd2, ret; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &fd2, argv[1])) - return JS_EXCEPTION; - ret = js_get_errno(dup2(fd, fd2)); - return JS_NewInt32(ctx, ret); -} - -#endif /* !_WIN32 */ - -#ifdef USE_WORKER - -/* Worker */ - -typedef struct { - JSWorkerMessagePipe *recv_pipe; - JSWorkerMessagePipe *send_pipe; - JSWorkerMessageHandler *msg_handler; -} JSWorkerData; - -typedef struct { - char *filename; /* module filename */ - char *basename; /* module base name */ - JSWorkerMessagePipe *recv_pipe, *send_pipe; -} WorkerFuncArgs; - -typedef struct { - int ref_count; - uint64_t buf[]; -} JSSABHeader; - -static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); - -static int atomic_add_int(int *ptr, int v) -{ - return atomic_fetch_add((_Atomic uint32_t*)ptr, v) + v; -} - -/* shared array buffer allocator */ -static void *js_sab_alloc(void *opaque, size_t size) -{ - JSSABHeader *sab; - sab = malloc(sizeof(JSSABHeader) + size); - if (!sab) - return NULL; - sab->ref_count = 1; - return sab->buf; -} - -static void js_sab_free(void *opaque, void *ptr) -{ - JSSABHeader *sab; - int ref_count; - sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); - ref_count = atomic_add_int(&sab->ref_count, -1); - assert(ref_count >= 0); - if (ref_count == 0) { - free(sab); - } -} - -static void js_sab_dup(void *opaque, void *ptr) -{ - JSSABHeader *sab; - sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); - atomic_add_int(&sab->ref_count, 1); -} - -static JSWorkerMessagePipe *js_new_message_pipe(void) -{ - JSWorkerMessagePipe *ps; - - ps = malloc(sizeof(*ps)); - if (!ps) - return NULL; - if (js_waker_init(&ps->waker)) { - free(ps); - return NULL; - } - ps->ref_count = 1; - init_list_head(&ps->msg_queue); - js_mutex_init(&ps->mutex); - return ps; -} - -static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) -{ - atomic_add_int(&ps->ref_count, 1); - return ps; -} - -static void js_free_message(JSWorkerMessage *msg) -{ - size_t i; - /* free the SAB */ - for(i = 0; i < msg->sab_tab_len; i++) { - js_sab_free(NULL, msg->sab_tab[i]); - } - free(msg->sab_tab); - free(msg->data); - free(msg); -} - -static void js_free_message_pipe(JSWorkerMessagePipe *ps) -{ - struct list_head *el, *el1; - JSWorkerMessage *msg; - int ref_count; - - if (!ps) - return; - - ref_count = atomic_add_int(&ps->ref_count, -1); - assert(ref_count >= 0); - if (ref_count == 0) { - list_for_each_safe(el, el1, &ps->msg_queue) { - msg = list_entry(el, JSWorkerMessage, link); - js_free_message(msg); - } - js_mutex_destroy(&ps->mutex); - js_waker_close(&ps->waker); - free(ps); - } -} - -static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) -{ - if (port) { - js_free_message_pipe(port->recv_pipe); - JS_FreeValueRT(rt, port->on_message_func); - list_del(&port->link); - js_free_rt(rt, port); - } -} - -static void js_worker_finalizer(JSRuntime *rt, JSValueConst val) -{ - JSThreadState *ts = js_get_thread_state(rt); - JSWorkerData *worker = JS_GetOpaque(val, ts->worker_class_id); - if (worker) { - js_free_message_pipe(worker->recv_pipe); - js_free_message_pipe(worker->send_pipe); - js_free_port(rt, worker->msg_handler); - js_free_rt(rt, worker); - } -} - -static JSClassDef js_worker_class = { - "Worker", - .finalizer = js_worker_finalizer, -}; - -static void worker_func(void *opaque) -{ - WorkerFuncArgs *args = opaque; - JSRuntime *rt; - JSThreadState *ts; - JSContext *ctx; - JSValue val; - - rt = JS_NewRuntime(); - if (rt == NULL) { - fprintf(stderr, "JS_NewRuntime failure"); - exit(1); - } - js_std_init_handlers(rt); - - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); - - /* set the pipe to communicate with the parent */ - ts = js_get_thread_state(rt); - ts->recv_pipe = args->recv_pipe; - ts->send_pipe = args->send_pipe; - - /* function pointer to avoid linking the whole JS_NewContext() if - not needed */ - ctx = js_worker_new_context_func(rt); - if (ctx == NULL) { - fprintf(stderr, "JS_NewContext failure"); - } - - JS_SetCanBlock(rt, true); - - js_std_add_helpers(ctx, -1, NULL); - - val = JS_LoadModule(ctx, args->basename, args->filename); - free(args->filename); - free(args->basename); - free(args); - val = js_std_await(ctx, val); - if (JS_IsException(val)) - js_std_dump_error(ctx); - JS_FreeValue(ctx, val); - - js_std_loop(ctx); - - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, - JSWorkerMessagePipe *recv_pipe, - JSWorkerMessagePipe *send_pipe) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSValue obj = JS_UNDEFINED, proto; - JSWorkerData *s; - - /* create the object */ - if (JS_IsUndefined(new_target)) { - proto = JS_GetClassProto(ctx, ts->worker_class_id); - } else { - proto = JS_GetPropertyStr(ctx, new_target, "prototype"); - if (JS_IsException(proto)) - goto fail; - } - obj = JS_NewObjectProtoClass(ctx, proto, ts->worker_class_id); - JS_FreeValue(ctx, proto); - if (JS_IsException(obj)) - goto fail; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - goto fail; - s->recv_pipe = js_dup_message_pipe(recv_pipe); - s->send_pipe = js_dup_message_pipe(send_pipe); - - JS_SetOpaque(obj, s); - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - WorkerFuncArgs *args = NULL; - js_thread_t thr; - JSValue obj = JS_UNDEFINED; - int ret; - const char *filename = NULL, *basename; - JSAtom basename_atom; - - /* XXX: in order to avoid problems with resource liberation, we - don't support creating workers inside workers */ - if (!is_main_thread(rt)) - return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker"); - - /* base name, assuming the calling function is a normal JS - function */ - basename_atom = JS_GetScriptOrModuleName(ctx, 1); - if (basename_atom == JS_ATOM_NULL) { - return JS_ThrowTypeError(ctx, "could not determine calling script or module name"); - } - basename = JS_AtomToCString(ctx, basename_atom); - JS_FreeAtom(ctx, basename_atom); - if (!basename) - goto fail; - - /* module name */ - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - - args = malloc(sizeof(*args)); - if (!args) - goto oom_fail; - memset(args, 0, sizeof(*args)); - args->filename = strdup(filename); - args->basename = strdup(basename); - - /* ports */ - args->recv_pipe = js_new_message_pipe(); - if (!args->recv_pipe) - goto oom_fail; - args->send_pipe = js_new_message_pipe(); - if (!args->send_pipe) - goto oom_fail; - - obj = js_worker_ctor_internal(ctx, new_target, - args->send_pipe, args->recv_pipe); - if (JS_IsException(obj)) - goto fail; - - ret = js_thread_create(&thr, worker_func, args, JS_THREAD_CREATE_DETACHED); - if (ret != 0) { - JS_ThrowTypeError(ctx, "could not create worker"); - goto fail; - } - JS_FreeCString(ctx, basename); - JS_FreeCString(ctx, filename); - return obj; - oom_fail: - JS_ThrowOutOfMemory(ctx); - fail: - JS_FreeCString(ctx, basename); - JS_FreeCString(ctx, filename); - if (args) { - free(args->filename); - free(args->basename); - js_free_message_pipe(args->recv_pipe); - js_free_message_pipe(args->send_pipe); - free(args); - } - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); - JSWorkerMessagePipe *ps; - size_t data_len, i; - uint8_t *data; - JSWorkerMessage *msg; - JSSABTab sab_tab; - - if (!worker) - return JS_EXCEPTION; - - data = JS_WriteObject2(ctx, &data_len, argv[0], - JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, - &sab_tab); - if (!data) - return JS_EXCEPTION; - - msg = malloc(sizeof(*msg)); - if (!msg) - goto fail; - msg->data = NULL; - msg->sab_tab = NULL; - - /* must reallocate because the allocator may be different */ - msg->data = malloc(data_len); - if (!msg->data) - goto fail; - memcpy(msg->data, data, data_len); - msg->data_len = data_len; - - if (sab_tab.len > 0) { - msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab.len); - if (!msg->sab_tab) - goto fail; - memcpy(msg->sab_tab, sab_tab.tab, sizeof(msg->sab_tab[0]) * sab_tab.len); - } - msg->sab_tab_len = sab_tab.len; - - js_free(ctx, data); - js_free(ctx, sab_tab.tab); - - /* increment the SAB reference counts */ - for(i = 0; i < msg->sab_tab_len; i++) { - js_sab_dup(NULL, msg->sab_tab[i]); - } - - ps = worker->send_pipe; - js_mutex_lock(&ps->mutex); - /* indicate that data is present */ - if (list_empty(&ps->msg_queue)) - js_waker_signal(&ps->waker); - list_add_tail(&msg->link, &ps->msg_queue); - js_mutex_unlock(&ps->mutex); - return JS_UNDEFINED; - fail: - if (msg) { - free(msg->data); - free(msg->sab_tab); - free(msg); - } - js_free(ctx, data); - js_free(ctx, sab_tab.tab); - return JS_EXCEPTION; - -} - -static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, - JSValueConst func) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); - JSWorkerMessageHandler *port; - - if (!worker) - return JS_EXCEPTION; - - port = worker->msg_handler; - if (JS_IsNull(func)) { - if (port) { - js_free_port(rt, port); - worker->msg_handler = NULL; - } - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - if (!port) { - port = js_mallocz(ctx, sizeof(*port)); - if (!port) - return JS_EXCEPTION; - port->recv_pipe = js_dup_message_pipe(worker->recv_pipe); - port->on_message_func = JS_NULL; - list_add_tail(&port->link, &ts->port_list); - worker->msg_handler = port; - } - JS_FreeValue(ctx, port->on_message_func); - port->on_message_func = JS_DupValue(ctx, func); - } - return JS_UNDEFINED; -} - -static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); - JSWorkerMessageHandler *port; - if (!worker) - return JS_EXCEPTION; - port = worker->msg_handler; - if (port) { - return JS_DupValue(ctx, port->on_message_func); - } else { - return JS_NULL; - } -} - -static const JSCFunctionListEntry js_worker_proto_funcs[] = { - JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ), - JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ), -}; - -#endif /* USE_WORKER */ - -void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) -{ -#ifdef USE_WORKER - js_worker_new_context_func = func; -#endif -} - -#if defined(_WIN32) -#define OS_PLATFORM "win32" -#elif defined(__APPLE__) -#define OS_PLATFORM "darwin" -#elif defined(EMSCRIPTEN) -#define OS_PLATFORM "js" -#elif defined(__CYGWIN__) -#define OS_PLATFORM "cygwin" -#elif defined(__linux__) -#define OS_PLATFORM "linux" -#elif defined(__OpenBSD__) -#define OS_PLATFORM "openbsd" -#elif defined(__NetBSD__) -#define OS_PLATFORM "netbsd" -#elif defined(__FreeBSD__) -#define OS_PLATFORM "freebsd" -#elif defined(__wasi__) -#define OS_PLATFORM "wasi" -#elif defined(__GNU__) -#define OS_PLATFORM "hurd" -#else -#define OS_PLATFORM "unknown" -#endif - -#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) - -static const JSCFunctionListEntry js_os_funcs[] = { - JS_CFUNC_DEF("open", 2, js_os_open ), - OS_FLAG(O_RDONLY), - OS_FLAG(O_WRONLY), - OS_FLAG(O_RDWR), - OS_FLAG(O_APPEND), - OS_FLAG(O_CREAT), - OS_FLAG(O_EXCL), - OS_FLAG(O_TRUNC), -#if defined(_WIN32) - OS_FLAG(O_BINARY), - OS_FLAG(O_TEXT), -#endif - JS_CFUNC_DEF("close", 1, js_os_close ), - JS_CFUNC_DEF("seek", 3, js_os_seek ), - JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), - JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), - JS_CFUNC_DEF("isatty", 1, js_os_isatty ), -#if !defined(__wasi__) - JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), - JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), -#endif - JS_CFUNC_DEF("remove", 1, js_os_remove ), - JS_CFUNC_DEF("rename", 2, js_os_rename ), - JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), - JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), - JS_CFUNC_DEF("signal", 2, js_os_signal ), - OS_FLAG(SIGINT), - OS_FLAG(SIGABRT), - OS_FLAG(SIGFPE), - OS_FLAG(SIGILL), - OS_FLAG(SIGSEGV), - OS_FLAG(SIGTERM), -#if !defined(_WIN32) && !defined(__wasi__) - OS_FLAG(SIGQUIT), - OS_FLAG(SIGPIPE), - OS_FLAG(SIGALRM), - OS_FLAG(SIGUSR1), - OS_FLAG(SIGUSR2), - OS_FLAG(SIGCHLD), - OS_FLAG(SIGCONT), - OS_FLAG(SIGSTOP), - OS_FLAG(SIGTSTP), - OS_FLAG(SIGTTIN), - OS_FLAG(SIGTTOU), - JS_CFUNC_DEF("cputime", 0, js_os_cputime ), -#endif - JS_CFUNC_DEF("exePath", 0, js_os_exepath ), - JS_CFUNC_DEF("now", 0, js_os_now ), - JS_CFUNC_MAGIC_DEF("setTimeout", 2, js_os_setTimeout, 0 ), - JS_CFUNC_MAGIC_DEF("setInterval", 2, js_os_setTimeout, 1 ), - // per spec: both functions can cancel timeouts and intervals - JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), - JS_CFUNC_DEF("clearInterval", 1, js_os_clearTimeout ), - JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ), - JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), - JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), - JS_CFUNC_DEF("chdir", 0, js_os_chdir ), - JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), - JS_CFUNC_DEF("readdir", 1, js_os_readdir ), - /* st_mode constants */ - OS_FLAG(S_IFMT), - OS_FLAG(S_IFIFO), - OS_FLAG(S_IFCHR), - OS_FLAG(S_IFDIR), - OS_FLAG(S_IFBLK), - OS_FLAG(S_IFREG), -#if !defined(_WIN32) - OS_FLAG(S_IFSOCK), - OS_FLAG(S_IFLNK), - OS_FLAG(S_ISGID), - OS_FLAG(S_ISUID), -#endif - JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), - JS_CFUNC_DEF("utimes", 3, js_os_utimes ), - JS_CFUNC_DEF("sleep", 1, js_os_sleep ), -#if !defined(__wasi__) - JS_CFUNC_DEF("realpath", 1, js_os_realpath ), -#endif -#if !defined(_WIN32) && !defined(__wasi__) && !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) - JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), - JS_CFUNC_DEF("symlink", 2, js_os_symlink ), - JS_CFUNC_DEF("readlink", 1, js_os_readlink ), - JS_CFUNC_DEF("exec", 1, js_os_exec ), - JS_CFUNC_DEF("getpid", 0, js_os_getpid ), - JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), - OS_FLAG(WNOHANG), - JS_CFUNC_DEF("pipe", 0, js_os_pipe ), - JS_CFUNC_DEF("kill", 2, js_os_kill ), - JS_CFUNC_DEF("dup", 1, js_os_dup ), - JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), -#endif -}; - -static int js_os_init(JSContext *ctx, JSModuleDef *m) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - - ts->can_js_os_poll = true; - -#ifdef USE_WORKER - { - JSValue proto, obj; - /* Worker class */ - JS_NewClassID(rt, &ts->worker_class_id); - JS_NewClass(rt, ts->worker_class_id, &js_worker_class); - proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); - - obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, - JS_CFUNC_constructor, 0); - JS_SetConstructor(ctx, obj, proto); - - JS_SetClassProto(ctx, ts->worker_class_id, proto); - - /* set 'Worker.parent' if necessary */ - if (ts->recv_pipe && ts->send_pipe) { - JS_DefinePropertyValueStr(ctx, obj, "parent", - js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), - JS_PROP_C_W_E); - } - - JS_SetModuleExport(ctx, m, "Worker", obj); - } -#endif /* USE_WORKER */ - - return JS_SetModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); -} - -JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_os_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); -#ifdef USE_WORKER - JS_AddModuleExport(ctx, m, "Worker"); -#endif - return m; -} - -/**********************************************************/ - -static JSValue js_print(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ -#ifdef _WIN32 - HANDLE handle; - DWORD mode; -#endif - const char *s; - JSValueConst v; - DynBuf b; - int i; - - dbuf_init(&b); - for(i = 0; i < argc; i++) { - v = argv[i]; - s = JS_ToCString(ctx, v); - if (!s && JS_IsObject(v)) { - JS_FreeValue(ctx, JS_GetException(ctx)); - JSValue t = JS_ToObjectString(ctx, v); - s = JS_ToCString(ctx, t); - JS_FreeValue(ctx, t); - } - if (s) { - dbuf_printf(&b, "%s%s", &" "[!i], s); - JS_FreeCString(ctx, s); - } else { - dbuf_printf(&b, "%s", &" "[!i]); - JS_FreeValue(ctx, JS_GetException(ctx)); - } - } - dbuf_putc(&b, '\n'); -#ifdef _WIN32 - // use WriteConsoleA with CP_UTF8 for better Unicode handling vis-a-vis - // the mangling that happens when going through msvcrt's stdio layer, - // *except* when stdout is redirected to something that is not a console - handle = (HANDLE)_get_osfhandle(/*STDOUT_FILENO*/1); // don't CloseHandle - if (GetFileType(handle) != FILE_TYPE_CHAR) - goto fallback; - if (!GetConsoleMode(handle, &mode)) - goto fallback; - handle = GetStdHandle(STD_OUTPUT_HANDLE); - if (handle == INVALID_HANDLE_VALUE) - goto fallback; - mode = GetConsoleOutputCP(); - SetConsoleOutputCP(CP_UTF8); - WriteConsoleA(handle, b.buf, b.size, NULL, NULL); - SetConsoleOutputCP(mode); - FlushFileBuffers(handle); - goto done; -fallback: -#endif - fwrite(b.buf, 1, b.size, stdout); - fflush(stdout); - goto done; // avoid unused label warning -done: - dbuf_free(&b); - return JS_UNDEFINED; -} - -void js_std_add_helpers(JSContext *ctx, int argc, char **argv) -{ - JSValue global_obj, console, args; - int i; - - /* XXX: should these global definitions be enumerable? */ - global_obj = JS_GetGlobalObject(ctx); - - console = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, console, "log", - JS_NewCFunction(ctx, js_print, "log", 1)); - JS_SetPropertyStr(ctx, global_obj, "console", console); - - /* same methods as the mozilla JS shell */ - if (argc >= 0) { - args = JS_NewArray(ctx); - for(i = 0; i < argc; i++) { - JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); - } - JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); - } - - JS_SetPropertyStr(ctx, global_obj, "print", - JS_NewCFunction(ctx, js_print, "print", 1)); - - JS_FreeValue(ctx, global_obj); -} - -static void js_std_finalize(JSRuntime *rt, void *arg) -{ - JSThreadState *ts = arg; - js_set_thread_state(rt, NULL); - js_free_rt(rt, ts); -} - -void js_std_init_handlers(JSRuntime *rt) -{ - JSThreadState *ts; - - ts = js_mallocz_rt(rt, sizeof(*ts)); - if (!ts) { - fprintf(stderr, "Could not allocate memory for the worker"); - exit(1); - } - init_list_head(&ts->os_rw_handlers); - init_list_head(&ts->os_signal_handlers); - init_list_head(&ts->os_timers); - init_list_head(&ts->port_list); - init_list_head(&ts->rejected_promise_list); - - ts->next_timer_id = 1; - - js_set_thread_state(rt, ts); - JS_AddRuntimeFinalizer(rt, js_std_finalize, ts); - -#ifdef USE_WORKER - /* set the SharedArrayBuffer memory handlers */ - { - JSSharedArrayBufferFunctions sf; - memset(&sf, 0, sizeof(sf)); - sf.sab_alloc = js_sab_alloc; - sf.sab_free = js_sab_free; - sf.sab_dup = js_sab_dup; - JS_SetSharedArrayBufferFunctions(rt, &sf); - } -#endif -} - -static void free_rp(JSRuntime *rt, JSRejectedPromiseEntry *rp) -{ - list_del(&rp->link); - JS_FreeValueRT(rt, rp->promise); - JS_FreeValueRT(rt, rp->reason); - js_free_rt(rt, rp); -} - -void js_std_free_handlers(JSRuntime *rt) -{ - JSThreadState *ts = js_get_thread_state(rt); - struct list_head *el, *el1; - - list_for_each_safe(el, el1, &ts->os_rw_handlers) { - JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); - free_rw_handler(rt, rh); - } - - list_for_each_safe(el, el1, &ts->os_signal_handlers) { - JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); - free_sh(rt, sh); - } - - list_for_each_safe(el, el1, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - free_timer(rt, th); - } - - list_for_each_safe(el, el1, &ts->rejected_promise_list) { - JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); - free_rp(rt, rp); - } - -#ifdef USE_WORKER - /* XXX: free port_list ? */ - js_free_message_pipe(ts->recv_pipe); - js_free_message_pipe(ts->send_pipe); -#endif -} - -static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val) -{ - const char *str; - - str = JS_ToCString(ctx, val); - if (str) { - fprintf(f, "%s\n", str); - JS_FreeCString(ctx, str); - } else { - fprintf(f, "[exception]\n"); - } -} - -static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val) -{ - JSValue val; - bool is_error; - - is_error = JS_IsError(exception_val); - js_dump_obj(ctx, stderr, exception_val); - if (is_error) { - val = JS_GetPropertyStr(ctx, exception_val, "stack"); - } else { - js_std_cmd(/*ErrorBackTrace*/2, ctx, &val); - } - if (!JS_IsUndefined(val)) { - js_dump_obj(ctx, stderr, val); - JS_FreeValue(ctx, val); - } -} - -void js_std_dump_error(JSContext *ctx) -{ - JSValue exception_val; - - exception_val = JS_GetException(ctx); - js_std_dump_error1(ctx, exception_val); - JS_FreeValue(ctx, exception_val); -} - -static JSRejectedPromiseEntry *find_rejected_promise(JSContext *ctx, JSThreadState *ts, - JSValueConst promise) -{ - struct list_head *el; - - list_for_each(el, &ts->rejected_promise_list) { - JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); - if (JS_IsSameValue(ctx, rp->promise, promise)) - return rp; - } - return NULL; -} - -void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, - JSValueConst reason, - bool is_handled, void *opaque) -{ - - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSRejectedPromiseEntry *rp = find_rejected_promise(ctx, ts, promise); - - if (is_handled) { - /* the rejection is handled, so the entry can be removed if present */ - if (rp) - free_rp(rt, rp); - } else { - /* add a new entry if needed */ - if (!rp) { - rp = js_malloc_rt(rt, sizeof(*rp)); - if (rp) { - rp->promise = JS_DupValue(ctx, promise); - rp->reason = JS_DupValue(ctx, reason); - list_add_tail(&rp->link, &ts->rejected_promise_list); - } - } - } -} - -/* check if there are pending promise rejections. It must be done - asynchrously in case a rejected promise is handled later. Currently - we do it once the application is about to sleep. It could be done - more often if needed. */ -static void js_std_promise_rejection_check(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - struct list_head *el; - - if (unlikely(!list_empty(&ts->rejected_promise_list))) { - list_for_each(el, &ts->rejected_promise_list) { - JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); - fprintf(stderr, "Possibly unhandled promise rejection: "); - js_std_dump_error1(ctx, rp->reason); - fflush(stderr); - } - exit(1); - } -} - -/* main loop which calls the user JS callbacks */ -int js_std_loop(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSContext *ctx1; - int err; - - for(;;) { - /* execute the pending jobs */ - for(;;) { - err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); - if (err <= 0) { - if (err < 0) - goto done; - break; - } - } - - js_std_promise_rejection_check(ctx); - - if (!ts->can_js_os_poll || js_os_poll(ctx)) - break; - } -done: - return JS_HasException(ctx); -} - -/* Wait for a promise and execute pending jobs while waiting for - it. Return the promise result or JS_EXCEPTION in case of promise - rejection. */ -JSValue js_std_await(JSContext *ctx, JSValue obj) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSValue ret; - int state; - - for(;;) { - state = JS_PromiseState(ctx, obj); - if (state == JS_PROMISE_FULFILLED) { - ret = JS_PromiseResult(ctx, obj); - JS_FreeValue(ctx, obj); - break; - } else if (state == JS_PROMISE_REJECTED) { - ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); - JS_FreeValue(ctx, obj); - break; - } else if (state == JS_PROMISE_PENDING) { - JSContext *ctx1; - int err; - err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); - if (err < 0) { - js_std_dump_error(ctx1); - } - if (err == 0) - js_std_promise_rejection_check(ctx); - if (ts->can_js_os_poll) - js_os_poll(ctx); - } else { - /* not a promise */ - ret = obj; - break; - } - } - return ret; -} - -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int load_only) -{ - JSValue obj, val; - obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); - if (JS_IsException(obj)) - goto exception; - if (load_only) { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - if (js_module_set_import_meta(ctx, obj, false, false) < 0) - goto exception; - } - JS_FreeValue(ctx, obj); - } else { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - if (JS_ResolveModule(ctx, obj) < 0) { - JS_FreeValue(ctx, obj); - goto exception; - } - if (js_module_set_import_meta(ctx, obj, false, true) < 0) - goto exception; - val = JS_EvalFunction(ctx, obj); - val = js_std_await(ctx, val); - } else { - val = JS_EvalFunction(ctx, obj); - } - if (JS_IsException(val)) { - exception: - js_std_dump_error(ctx); - exit(1); - } - JS_FreeValue(ctx, val); - } -} - -static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint8_t *buf; - uint64_t pos, len; - JSValue obj; - size_t size; - int flags; - - if (JS_ToIndex(ctx, &pos, argv[1])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[2])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &flags, argv[3])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[0]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "array buffer overflow"); - obj = JS_ReadObject(ctx, buf + pos, len, flags); - return obj; -} - -static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - size_t len; - uint8_t *buf; - JSValue array; - int flags; - - if (JS_ToInt32(ctx, &flags, argv[1])) - return JS_EXCEPTION; - buf = JS_WriteObject(ctx, &len, argv[0], flags); - if (!buf) - return JS_EXCEPTION; - array = JS_NewArrayBufferCopy(ctx, buf, len); - js_free(ctx, buf); - return array; -} - - -static const JSCFunctionListEntry js_bjson_funcs[] = { - JS_CFUNC_DEF("read", 4, js_bjson_read ), - JS_CFUNC_DEF("write", 2, js_bjson_write ), -#define DEF(x) JS_PROP_INT32_DEF(#x, JS_##x, JS_PROP_CONFIGURABLE) - DEF(READ_OBJ_BYTECODE), - DEF(READ_OBJ_REFERENCE), - DEF(READ_OBJ_SAB), - DEF(WRITE_OBJ_BYTECODE), - DEF(WRITE_OBJ_REFERENCE), - DEF(WRITE_OBJ_SAB), - DEF(WRITE_OBJ_STRIP_DEBUG), - DEF(WRITE_OBJ_STRIP_SOURCE), -#undef DEF -}; - -static int js_bjson_init(JSContext *ctx, JSModuleDef *m) -{ - return JS_SetModuleExportList(ctx, m, js_bjson_funcs, - countof(js_bjson_funcs)); -} - -JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_bjson_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs)); - return m; -} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-libc.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-libc.h deleted file mode 100755 index 6af5a639..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-libc.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * QuickJS C library - * - * 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 QUICKJS_LIBC_H -#define QUICKJS_LIBC_H - -#include -#include -#include - -#include "quickjs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define JS_EXTERN __attribute__((visibility("default"))) -#else -#define JS_EXTERN /* nothing */ -#endif - -JS_EXTERN JSModuleDef *js_init_module_std(JSContext *ctx, - const char *module_name); -JS_EXTERN JSModuleDef *js_init_module_os(JSContext *ctx, - const char *module_name); -JS_EXTERN JSModuleDef *js_init_module_bjson(JSContext *ctx, - const char *module_name); -JS_EXTERN void js_std_add_helpers(JSContext *ctx, int argc, char **argv); -JS_EXTERN int js_std_loop(JSContext *ctx); -JS_EXTERN JSValue js_std_await(JSContext *ctx, JSValue obj); -JS_EXTERN void js_std_init_handlers(JSRuntime *rt); -JS_EXTERN void js_std_free_handlers(JSRuntime *rt); -JS_EXTERN void js_std_dump_error(JSContext *ctx); -JS_EXTERN uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, - const char *filename); -JS_EXTERN int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - bool use_realpath, bool is_main); -JS_EXTERN JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque); -JS_EXTERN void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, - size_t buf_len, int flags); -JS_EXTERN void js_std_promise_rejection_tracker(JSContext *ctx, - JSValueConst promise, - JSValueConst reason, - bool is_handled, - void *opaque); -JS_EXTERN void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); - -#undef JS_EXTERN - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_LIBC_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-opcode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-opcode.h deleted file mode 100755 index bd5be754..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs-opcode.h +++ /dev/null @@ -1,371 +0,0 @@ -/* - * 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, 1, 1, none) /* dynamic module import */ - -DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ -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( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ - -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 */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs.c b/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs.c deleted file mode 100755 index c6815fb3..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source_ng/quickjs.c +++ /dev/null @@ -1,58317 +0,0 @@ -/* - * 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 -#include - -#include "cutils.h" -#include "list.h" -#include "quickjs.h" -#include "libregexp.h" -#include "dtoa.h" - -#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 - -// 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__ -#include "quickjs-c-atomics.h" -#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_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 - -#define __exception __attribute__((warn_unused_result)) - -typedef struct JSShape JSShape; -typedef struct JSString JSString; -typedef struct JSString JSAtomStruct; - -#define JS_VALUE_GET_STRING(v) ((JSString *)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; - JSModuleLoaderFunc *module_loader_func; - 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 list_head var_ref_list; /* list of JSVarRef.link */ - uint8_t *cur_pc; /* only used in bytecode functions : PC of the - instruction after the call */ - uint32_t arg_count : 31; - uint32_t is_strict_mode : 1; - /* 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 : 4; /* used by the GC */ - 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; - }; - }; - JSValue *pvalue; /* pointer to the value, either on the stack or - to 'value' */ - JSValue value; /* used when the variable is no longer on the stack */ -} 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, -}; - -/* 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; - int binary_object_size; - - JSShape *array_shape; /* initial shape for Array 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, -} JSStringKind; - -#define JS_ATOM_HASH_MASK ((1 << 29) - 1) - -struct JSString { - JSRefCountHeader header; /* must come first, 32-bit */ - uint32_t len : 31; - uint8_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 : 29; - uint8_t kind : 1; - uint8_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; - -static inline void *strv(JSString *p) -{ - JSStringSlice *slice; - - 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; - } - abort(); - return NULL; -} - -static inline uint8_t *str8(JSString *p) -{ - return strv(p); -} - -static inline uint16_t *str16(JSString *p) -{ - return strv(p); -} - -typedef struct JSClosureVar { - uint8_t is_local : 1; - uint8_t is_arg : 1; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* 8 bits available */ - uint16_t var_idx; /* is_local = true: 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 */ - /* 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 : 24; /* 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 */ - JSContext *realm; /* function realm */ - JSValue *cpool; /* constant pool (self pointer) */ - int cpool_count; - int closure_var_count; - 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 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 */ -} 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 */ -}; - -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; - /* If true, the shape may have small array index properties 'n' with 0 - <= n <= 2^31-1. If false, the shape is guaranteed not to have - small array index properties */ - uint8_t has_small_array_index; - 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; /* corresponds to header.mark/gc_obj_type */ - - 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 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 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_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - union { - uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - } u1; - union { - JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_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, -#include "quickjs-atom.h" -#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" -#include "quickjs-atom.h" -#undef DEF -; - -typedef enum OPCodeFormat { -#define FMT(f) OP_FMT_ ## f, -#define DEF(id, size, n_pop, n_push, f) -#include "quickjs-opcode.h" -#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) -#include "quickjs-opcode.h" -#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, -#include "quickjs-opcode.h" -#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_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 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_oob(JSObject *p); -static uint32_t typed_array_length(JSObject *p); -static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); -static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); -static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, - bool is_arg); -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); -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 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 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, NULL, NULL }, /* 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_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_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_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) -{ - if (str->atom_type) { - JS_FreeAtomStruct(rt, str); - } else { -#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS - list_del(&str->link); -#endif - if (str->kind == JS_STRING_KIND_SLICE) { - JSStringSlice *slice = (void *)&str[1]; - js_free_string(rt, slice->parent); // safe, recurses only 1 level - } - 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); -} - -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); - - 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 __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; -} - -static 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_putc_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_putc_slow(s, c); -} - -/* 0 <= c <= 0x10ffff */ -static int string_buffer_putc(StringBuffer *s, uint32_t c) -{ - if (unlikely(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); -} - -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; - - if (s->error_status) { - /* prevent exception overload */ - return -1; - } - if (unlikely(JS_VALUE_GET_TAG(v) != 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; - - if (s->error_status) { - /* prevent exception overload */ - JS_FreeValue(s->ctx, v); - return -1; - } - if (unlikely(JS_VALUE_GET_TAG(v) != 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_NewTwoByteString(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; -} - -/* 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; - JSObject *p; - uint8_t *q; - - if (JS_VALUE_GET_TAG(val1) == JS_TAG_STRING) { - val = js_dup(val1); - goto go; - } - - val = JS_ToString(ctx, val1); - if (!JS_IsException(val)) - goto go; - - // 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) - goto fail; - - p = JS_VALUE_GET_OBJ(val1); - if (p->class_id != JS_CLASS_ERROR) - goto fail; - - val = JS_GetProperty(ctx, val1, JS_ATOM_message); - if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) { - JS_FreeValue(ctx, val); - goto fail; - } - -go: - 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; -} - -void JS_FreeCString(JSContext *ctx, const char *ptr) -{ - if (!ptr) - return; - /* purposely removing constness */ - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, (JSString *)ptr - 1)); -} - -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; -} - -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); -} - -/* op1 and op2 are converted to strings. For convience, op1 or op2 = - JS_EXCEPTION are accepted and return JS_EXCEPTION. */ -static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) -{ - JSValue ret; - JSString *p1, *p2; - - if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { - op1 = JS_ToStringFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - } - if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { - op2 = JS_ToStringFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - } - 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; -} - -/* 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; - sh->has_small_array_index = false; - 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; - sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); - /* 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"); -} - -static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) -{ - JSObject *p; - - 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->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: - 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; - /* 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_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); - 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); -} - -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; - 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); - sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); - 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); -} - -// 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); -} - -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 force_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 force_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 { - list_del(&var_ref->header.link); /* still on the stack */ - } - 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_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_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; - 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: - 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); -} - -#include "builtin-array-fromasync.h" - -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 obj = JS_ReadObject(ctx, qjsc_builtin_array_fromasync, - sizeof(qjsc_builtin_array_fromasync), - JS_READ_OBJ_BYTECODE); - if (JS_IsException(obj)) - return JS_EXCEPTION; - JSValue fun = JS_EvalFunction(ctx, obj); - if (JS_IsException(fun)) - return JS_EXCEPTION; - assert(JS_IsFunction(ctx, fun)); - JSValue args[] = { - 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), - }; - JSValue result = JS_Call(ctx, fun, JS_UNDEFINED, - countof(args), vc(args)); - for (size_t i = 0; i < countof(args); i++) - JS_FreeValue(ctx, args[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; - } - } - 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; - 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; - 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_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; - - 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 Array or 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; - JSValue *tab; - uint32_t i, len, new_count; - - 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; - } - - 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) { - /* Special case deleting the last element of a fast Array */ - if (idx == p->u.array.count - 1) { - 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)) { - JSObject *p1; - JSShape *sh1; - - /* fast path to add an element to the array */ - if (idx != (uint32_t)p->u.array.count || - !p->fast_array || !p->extensible) - goto slow_path; - /* check if prototype chain has a numeric property */ - p1 = p->shape->proto; - while (p1 != NULL) { - sh1 = p1->shape; - if (p1->class_id == JS_CLASS_ARRAY) { - if (unlikely(!p1->fast_array)) - goto slow_path; - } else if (p1->class_id == JS_CLASS_OBJECT) { - if (unlikely(sh1->has_small_array_index)) - goto slow_path; - } else { - goto slow_path; - } - p1 = sh1->proto; - } - /* 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_UINT8C_ARRAY: - if (JS_ToUint8ClampFree(ctx, &v, val)) - goto ta_cvt_fail; - /* 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 (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 (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 (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 (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 (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 (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 (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); - } - 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; -} - -/* use for strict variable access: test if the variable exists */ -static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) -{ - JSObject *p; - JSShapeProperty *prs; - int ret; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property1(p, prop); - if (prs) { - ret = true; - } else { - ret = JS_HasProperty(ctx, ctx->global_obj, prop); - if (ret < 0) - return -1; - } - return ret; -} - -/* flag = 0: normal variable write - flag = 1: initialize lexical variable - flag = 2: normal variable write, strict check was done before -*/ -static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, - int flag) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - int flags; - - /* 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; - } - flags = JS_PROP_THROW_STRICT; - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; - return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); -} - -/* 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_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 = INF; - 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: - { - 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_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_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: - 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 (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { - 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 (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { - JSString *p1, *p2; - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = js_string_compare(p1, p2); - 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 ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || - (tag2 == JS_TAG_STRING && 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 (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; - } - } - 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) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || - (tag2 == JS_TAG_OBJECT && - (tag_is_number(tag1) || tag1 == JS_TAG_STRING || 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: - { - JSString *p1, *p2; - if (tag1 != tag2) { - res = false; - } else { - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = js_string_eq(p1, p2); - } - } - 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: - 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 *pr; - JSObject *p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (!pr) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - pr->u.value = js_int32(argc); - - /* initialize the fast array part */ - tab = NULL; - if (argc > 0) { - tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - for(i = 0; i < argc; i++) { - tab[i] = js_dup(argv[i]); - } - } - p->u.array.u.values = tab; - p->u.array.count = argc; - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, - js_dup(ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* add callee property to throw a TypeError in strict mode */ - JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, - ctx->throw_type_error, ctx->throw_type_error, - JS_PROP_HAS_GET | JS_PROP_HAS_SET); - return val; -} - -#define GLOBAL_VAR_OFFSET 0x40000000 -#define ARGUMENT_VAR_OFFSET 0x20000000 - -/* 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 *pr; - JSObject *p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_MAPPED_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (!pr) - goto fail; - pr->u.value = js_int32(argc); - - for(i = 0; i < arg_count; i++) { - JSVarRef *var_ref; - var_ref = get_var_ref(ctx, sf, i, true); - if (!var_ref) - goto fail; - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); - if (!pr) { - free_var_ref(ctx->rt, var_ref); - goto fail; - } - pr->u.var_ref = var_ref; - } - - /* the arguments not mapped to the arguments of the function can - be normal properties */ - for(i = arg_count; i < argc; i++) { - if (JS_DefinePropertyValueUint32(ctx, val, i, - js_dup(argv[i]), - JS_PROP_C_W_E) < 0) - goto fail; - } - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, - js_dup(ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* callee returns this function in non strict mode */ - JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, - js_dup(ctx->rt->current_stack_frame->cur_func), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - 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; -} - -static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, - int var_idx, bool is_arg) -{ - JSVarRef *var_ref; - struct list_head *el; - JSValue *pvalue; - - if (is_arg) - pvalue = &sf->arg_buf[var_idx]; - else - pvalue = &sf->var_buf[var_idx]; - - list_for_each(el, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_ref->pvalue == pvalue) { - 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; - list_add_tail(&var_ref->header.link, &sf->var_ref_list); - var_ref->pvalue = pvalue; - var_ref->value = JS_UNDEFINED; - 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; - if (cv->is_local) { - /* reuse the existing variable reference if it already exists */ - var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); - if (!var_ref) - goto fail; - } else { - var_ref = cur_var_refs[cv->var_idx]; - var_ref->header.ref_count++; - } - 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_refs(JSRuntime *rt, JSStackFrame *sf) -{ - struct list_head *el, *el1; - JSVarRef *var_ref; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - 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_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) -{ - JSValue *pvalue; - struct list_head *el, *el1; - JSVarRef *var_ref; - - pvalue = &sf->var_buf[var_idx]; - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_ref->pvalue == pvalue) { - var_ref->value = js_dup(*var_ref->pvalue); - var_ref->pvalue = &var_ref->value; - list_del(&var_ref->header.link); - /* the reference is no longer to a local variable */ - var_ref->is_detached = true; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - } - } -} - -#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) -#include "quickjs-opcode.h" - [ 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); - 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); - init_list_head(&sf->var_ref_list); - 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; - 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; - - sf->cur_pc = pc; - val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); - 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[-1]); - if (JS_IsException(val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_check_var): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_CheckGlobalVar(ctx, atom); - if (ret < 0) - goto exception; - *sp++ = js_bool(ret); - } - 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_put_var_strict): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - sf->cur_pc = pc; - - /* sp[-2] is JS_TRUE or JS_FALSE */ - if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - goto exception; - } - ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); - sp -= 2; - 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, 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; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - sf->cur_pc = pc; - val = JS_GetPropertyInternal(ctx, sp[-1], 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; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - sf->cur_pc = pc; - val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], false); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_field): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - sf->cur_pc = pc; - ret = JS_SetPropertyInternal2(ctx, - sp[-2], atom, - sp[-1], sp[-2], - JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-2]); - 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; - 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((int)r != r)) - goto add_slow; - 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 { - add_slow: - 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)) - goto add_loc_slow; - *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]; - add_loc_slow: - /* 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)) - goto binary_arith_slow; - 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): - CASE(OP_post_dec): - 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(!list_empty(&sf->var_ref_list))) { - /* 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; - - sf = &s->frame; - init_list_head(&sf->var_ref_list); - 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; - sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); - 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; - 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; - - /* close the closure variables. */ - close_var_refs(rt, sf); - - if (sf->arg_buf) { - /* 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_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 -#include "quickjs-opcode.h" -#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 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 ` array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + + template + bool get_cbor_negative_integer() + { + NumberType number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number))) + { + return false; + } + const auto max_val = static_cast((std::numeric_limits::max)()); + if (number > max_val) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr)); + } + return sax->number_integer(static_cast(-1) - static_cast(number)); + } + + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + return get_cbor_negative_integer(); + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + return get_cbor_negative_integer(); + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + return get_cbor_negative_integer(); + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + return get_cbor_negative_integer(); + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array( + conditional_static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(detail::unknown_size(), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(conditional_static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(detail::unknown_size(), tag_handler); + + case 0xC6: // tagged item + 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 0xD8: // tagged item (1 byte follows) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in a binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // Code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr)); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr)); + } + } + } + + /*! + @param[in] len the length of the array or detail::unknown_size() for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != detail::unknown_size()) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or detail::unknown_size() for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != detail::unknown_size()) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(conditional_static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(conditional_static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast(len)); + } + + // negative fixint + case 0xE0: + 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: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr)); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr)); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + default: + break; + } + auto last_token = get_token_string(); + std::string message; + + if (input_format != input_format_t::bjdata) + { + message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token; + } + else + { + message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token; + } + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr)); + } + + /*! + @param[out] dim an integer vector storing the ND array dimensions + @return whether reading ND array size vector is successful + */ + bool get_ubjson_ndarray_size(std::vector& dim) + { + std::pair size_and_type; + size_t dimlen = 0; + bool no_ndarray = true; + + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray))) + { + return false; + } + + if (size_and_type.first != npos) + { + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second))) + { + return false; + } + dim.push_back(dimlen); + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray))) + { + return false; + } + dim.push_back(dimlen); + } + } + } + else + { + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current))) + { + return false; + } + dim.push_back(dimlen); + get_ignore_noop(); + } + } + return true; + } + + /*! + @param[out] result determined size + @param[in,out] is_ndarray for input, `true` means already inside an ndarray vector + or ndarray dimension is not allowed; `false` means ndarray + is allowed; for output, `true` means an ndarray is found; + is_ndarray can only return `true` when its initial value + is `false` + @param[in] prefix type marker if already read, otherwise set to 0 + + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0) + { + if (prefix == 0) + { + prefix = get_ignore_noop(); + } + + switch (prefix) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + if (number < 0) + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, + exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); + } + result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + if (number < 0) + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, + exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + if (number < 0) + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, + exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + if (number < 0) + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, + exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); + } + if (!value_in_range_of(number)) + { + return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, + exception_message(input_format, "integer value overflow", "size"), nullptr)); + } + result = static_cast(number); + return true; + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = conditional_static_cast(number); + return true; + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + if (!value_in_range_of(number)) + { + return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, + exception_message(input_format, "integer value overflow", "size"), nullptr)); + } + result = detail::conditional_static_cast(number); + return true; + } + + case '[': + { + if (input_format != input_format_t::bjdata) + { + break; + } + if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr)); + } + std::vector dim; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) + { + return false; + } + if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector + { + result = dim.at(dim.size() - 1); + return true; + } + if (!dim.empty()) // if ndarray, convert to an object in JData annotated array format + { + for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container + { + if ( i == 0 ) + { + result = 0; + return true; + } + } + + string_t key = "_ArraySize_"; + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size()))) + { + return false; + } + result = 1; + for (auto i : dim) + { + // Pre-multiplication overflow check: if i > 0 and result > SIZE_MAX/i, then result*i would overflow. + // This check must happen before multiplication since overflow detection after the fact is unreliable + // as modular arithmetic can produce any value, not just 0 or SIZE_MAX. + if (JSON_HEDLEY_UNLIKELY(i > 0 && result > (std::numeric_limits::max)() / i)) + { + return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); + } + result *= i; + // Additional post-multiplication check to catch any edge cases the pre-check might miss + if (result == 0 || result == npos) + { + return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); + } + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast(i)))) + { + return false; + } + } + is_ndarray = true; + return sax->end_array(); + } + result = 0; + return true; + } + + default: + break; + } + auto last_token = get_token_string(); + std::string message; + + if (input_format != input_format_t::bjdata) + { + message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token; + } + else + { + message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token; + } + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr)); + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + @param[in] inside_ndarray whether the parser is parsing an ND array dimensional vector + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result, bool inside_ndarray = false) + { + result.first = npos; // size + result.second = 0; // type + bool is_ndarray = false; + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (input_format == input_format_t::bjdata + && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second))) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr)); + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); + } + + const bool is_error = get_ubjson_size_value(result.first, is_ndarray); + if (input_format == input_format_t::bjdata && is_ndarray) + { + if (inside_ndarray) + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, + exception_message(input_format, "ndarray can not be recursive", "size"), nullptr)); + } + result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters + } + return is_error; + } + + if (current == '#') + { + const bool is_error = get_ubjson_size_value(result.first, is_ndarray); + if (input_format == input_format_t::bjdata && is_ndarray) + { + return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, + exception_message(input_format, "ndarray requires both type and size", "size"), nullptr)); + } + return is_error; + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case char_traits::eof(): // EOF + return unexpect_eof(input_format, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'B': // byte + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint8_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format, number) && sax->number_integer(number); + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'h': + { + if (input_format != input_format_t::bjdata) + { + break; + } + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // Code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte2 << 8u) + byte1); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 'd': + { + float number{}; + return get_number(input_format, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + break; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr)); + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata): + // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]} + + if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0) + { + size_and_type.second &= ~(static_cast(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker + auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t) + { + return p.first < t; + }); + string_t key = "_ArrayType_"; + if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr)); + } + + string_t type = it->second; // sax->string() takes a reference + if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type))) + { + return false; + } + + if (size_and_type.second == 'C' || size_and_type.second == 'B') + { + size_and_type.second = 'U'; + } + + key = "_ArrayData_"; + if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) )) + { + return false; + } + + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + + return (sax->end_array() && sax->end_object()); + } + + // If BJData type marker is 'B' decode as binary + if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B') + { + binary_t result; + return get_binary(input_format, size_and_type.first, result) && sax->binary(result); + } + + if (size_and_type.first != npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + // do not accept ND-array size in objects in BJData + if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr)); + } + + string_t key; + if (size_and_type.first != npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get the size of the following number string + std::size_t size{}; + bool no_ndarray = true; + auto res = get_ubjson_size_value(size, no_ndarray); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, + exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, + exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @brief get_to read into a primitive type + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns false instead + + @return bool, whether the read was successful + */ + template + bool get_to(T& dest, const input_format_t format, const char* context) + { + auto new_chars_read = ia.get_elements(&dest); + chars_read += new_chars_read; + if (JSON_HEDLEY_UNLIKELY(new_chars_read < sizeof(T))) + { + // in case of failure, advance position by 1 to report the failing location + ++chars_read; + sax->parse_error(chars_read, "", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); + return false; + } + return true; + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + template + static void byte_swap(NumberType& number) + { + constexpr std::size_t sz = sizeof(number); +#ifdef __cpp_lib_byteswap + if constexpr (sz == 1) + { + return; + } + else if constexpr(std::is_integral_v) + { + number = std::byteswap(number); + return; + } + else + { +#endif + auto* ptr = reinterpret_cast(&number); + for (std::size_t i = 0; i < sz / 2; ++i) + { + std::swap(ptr[i], ptr[sz - i - 1]); + } +#ifdef __cpp_lib_byteswap + } +#endif + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianness, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + On the other hand, BSON and BJData use little endian and should reorder + on big endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // read in the original format + + if (JSON_HEDLEY_UNLIKELY(!get_to(result, format, "number"))) + { + return false; + } + if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) + { + byte_swap(result); + } + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::bjdata: + error_msg += "BJData"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return concat(error_msg, ' ', context, ": ", detail); + } + + private: + static JSON_INLINE_VARIABLE constexpr std::size_t npos = detail::unknown_size(); + + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// input format + const input_format_t input_format = input_format_t::json; + + /// the SAX parser + json_sax_t* sax = nullptr; + + // excluded markers in bjdata optimized type +#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \ + make_array('F', 'H', 'N', 'S', 'T', 'Z', '[', '{') + +#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \ + make_array( \ + bjd_type{'B', "byte"}, \ + bjd_type{'C', "char"}, \ + bjd_type{'D', "double"}, \ + bjd_type{'I', "int16"}, \ + bjd_type{'L', "int64"}, \ + bjd_type{'M', "uint64"}, \ + bjd_type{'U', "uint8"}, \ + bjd_type{'d', "single"}, \ + bjd_type{'i', "int8"}, \ + bjd_type{'l', "int32"}, \ + bjd_type{'m', "uint32"}, \ + bjd_type{'u', "uint16"}) + + JSON_PRIVATE_UNLESS_TESTED: + // lookup tables + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers = + JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_; + + using bjd_type = std::pair; + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map = + JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_; + +#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ +#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr std::size_t binary_reader::npos; +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas_ = false) + : callback(std::move(cb)) + , m_lexer(std::move(adapter), ignore_comments) + , allow_exceptions(allow_exceptions_) + , ignore_trailing_commas(ignore_trailing_commas_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions, &m_lexer); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), nullptr)); + } + + // in case of an error, return a discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions, &m_lexer); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); + } + + // in case of an error, return a discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid an "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); + } + case token_type::end_of_input: + { + if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); + } + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + // or end of array (ignore_trailing_commas = true) + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + + // if ignore_trailing_commas and last_token is ], we can continue to "closing ]" + if (!(ignore_trailing_commas && last_token == token_type::end_array)) + { + continue; + } + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr)); + } + + // states.back() is false -> object + + // comma -> next value + // or end of object (ignore_trailing_commas = true) + if (get_token() == token_type::value_separator) + { + get_token(); + + // if ignore_trailing_commas and last_token is }, we can continue to "closing }" + if (!(ignore_trailing_commas && last_token == token_type::end_object)) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); + } + + // parse values + get_token(); + continue; + } + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr)); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += concat("while parsing ", context, ' '); + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += concat(m_lexer.get_error_message(), "; last read: '", + m_lexer.get_token_string(), '\''); + } + else + { + error_msg += concat("unexpected ", lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += concat("; expected ", lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false) + const bool ignore_trailing_commas = false; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // ptrdiff_t +#include // numeric_limits + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). Its only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin and +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// forward declare to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + // superficial check for the LegacyBidirectionalIterator named requirement + static_assert(std::is_base_of::value + && std::is_base_of::iterator_category>::value, + "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement."); + + public: + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_data.m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_data.m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_data.m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_data.m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); + } + + // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 + if (m_object == nullptr) + { + return true; + } + + switch (m_object->m_data.m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); + } + + // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 + if (m_object == nullptr) + { + // the iterators are both value-initialized and are to be considered equal, but this function checks for smaller, so we return false + return false; + } + + switch (m_object->m_data.m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre (1) The iterator is initialized; i.e. `m_object != nullptr`, or (2) both iterators are value-initialized. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_data.m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // conditional, is_same + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief Default base class of the @ref basic_json class. + +So that the correct implementations of the copy / move ctors / assign operators +of @ref basic_json do not require complex case distinctions +(no base class / custom base class used as customization point), +@ref basic_json always has a base class. +By default, this class is used because it is empty and thus has no effect +on the behavior of @ref basic_json. +*/ +struct json_default_base {}; + +template +using json_base_class = typename std::conditional < + std::is_same::value, + json_default_base, + T + >::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // all_of +#include // isdigit +#include // errno, ERANGE +#include // strtoull +#ifndef JSON_NO_IO + #include // ostream +#endif // JSON_NO_IO +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + template + friend class json_pointer; + + template + struct string_t_helper + { + using type = T; + }; + + NLOHMANN_BASIC_JSON_TPL_DECLARATION + struct string_t_helper + { + using type = StringType; + }; + + public: + // for backwards compatibility accept BasicJsonType + using string_t = typename string_t_helper::type; + + /// @brief create JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ + explicit json_pointer(const string_t& s = "") + : reference_tokens(split(s)) + {} + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ + string_t to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + string_t{}, + [](const string_t& a, const string_t& b) + { + return detail::concat(a, '/', detail::escape(b)); + }); + } + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string()) + operator string_t() const + { + return to_string(); + } + +#ifndef JSON_NO_IO + /// @brief write string representation of the JSON pointer to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr) + { + o << ptr.to_string(); + return o; + } +#endif + + /// @brief append another JSON pointer at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /// @brief append an unescaped reference token at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(string_t token) + { + push_back(std::move(token)); + return *this; + } + + /// @brief append an array index at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(lhs) /= std::move(token); + } + + /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) + { + return json_pointer(lhs) /= array_idx; + } + + /// @brief returns the parent of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /// @brief remove last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); + } + + reference_tokens.pop_back(); + } + + /// @brief return last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/back/ + const string_t& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); + } + + return reference_tokens.back(); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(const string_t& token) + { + reference_tokens.push_back(token); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(string_t&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /// @brief return whether pointer points to the root document + /// @sa https://json.nlohmann.me/api/json_pointer/empty/ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + template + static typename BasicJsonType::size_type array_index(const string_t& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); + } + + const char* p = s.c_str(); + char* p_end = nullptr; // NOLINT(misc-const-correctness) + errno = 0; // strtoull doesn't reset errno + const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) + if (p == p_end // invalid input or empty string + || errno == ERANGE // out of range + || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read + { + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + template + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if the reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + a single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + template + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if the reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to an array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + template + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, detail::concat( + "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), + ") is out of range"), ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + template + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + template + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, detail::concat( + "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), + ") is out of range"), ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + template + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // the first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const string_t& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if a nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == string_t::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == string_t::npos) + start = (slash == string_t::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != string_t::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + template + static void flatten(const string_t& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_data.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as a reference string + for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) + { + flatten(detail::concat(reference_string, '/', std::to_string(i)), + value.m_data.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_data.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_data.m_value.object) + { + flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add a primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + template + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_data.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); + } + + // Assign the value to the reference pointed to by JSON pointer. Note + // that if the JSON pointer is "" (i.e., points to the whole value), + // function get_and_create returns a reference to the result itself. + // An assignment will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + // can't use the conversion operator because of ambiguity + json_pointer convert() const& + { + json_pointer result; + result.reference_tokens = reference_tokens; + return result; + } + + json_pointer convert()&& + { + json_pointer result; + result.reference_tokens = std::move(reference_tokens); + return result; + } + + public: +#if JSON_HAS_THREE_WAY_COMPARISON + /// @brief compares two JSON pointers for equality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ + template + bool operator==(const json_pointer& rhs) const noexcept + { + return reference_tokens == rhs.reference_tokens; + } + + /// @brief compares JSON pointer and string for equality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ + JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer)) + bool operator==(const string_t& rhs) const + { + return *this == json_pointer(rhs); + } + + /// @brief 3-way compares two JSON pointers + template + std::strong_ordering operator<=>(const json_pointer& rhs) const noexcept // *NOPAD* + { + return reference_tokens <=> rhs.reference_tokens; // *NOPAD* + } +#else + /// @brief compares two JSON pointers for equality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator==(const json_pointer& lhs, + const json_pointer& rhs) noexcept; + + /// @brief compares JSON pointer and string for equality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator==(const json_pointer& lhs, + const StringType& rhs); + + /// @brief compares string and JSON pointer for equality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator==(const StringType& lhs, + const json_pointer& rhs); + + /// @brief compares two JSON pointers for inequality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator!=(const json_pointer& lhs, + const json_pointer& rhs) noexcept; + + /// @brief compares JSON pointer and string for inequality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator!=(const json_pointer& lhs, + const StringType& rhs); + + /// @brief compares string and JSON pointer for inequality + /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator!=(const StringType& lhs, + const json_pointer& rhs); + + /// @brief compares two JSON pointer for less-than + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator<(const json_pointer& lhs, + const json_pointer& rhs) noexcept; +#endif + + private: + /// the reference tokens + std::vector reference_tokens; +}; + +#if !JSON_HAS_THREE_WAY_COMPARISON +// functions cannot be defined inside the class due to ODR violations +template +inline bool operator==(const json_pointer& lhs, + const json_pointer& rhs) noexcept +{ + return lhs.reference_tokens == rhs.reference_tokens; +} + +template::string_t> +JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) +inline bool operator==(const json_pointer& lhs, + const StringType& rhs) +{ + return lhs == json_pointer(rhs); +} + +template::string_t> +JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) +inline bool operator==(const StringType& lhs, + const json_pointer& rhs) +{ + return json_pointer(lhs) == rhs; +} + +template +inline bool operator!=(const json_pointer& lhs, + const json_pointer& rhs) noexcept +{ + return !(lhs == rhs); +} + +template::string_t> +JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) +inline bool operator!=(const json_pointer& lhs, + const StringType& rhs) +{ + return !(lhs == rhs); +} + +template::string_t> +JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) +inline bool operator!=(const StringType& lhs, + const json_pointer& rhs) +{ + return !(lhs == rhs); +} + +template +inline bool operator<(const json_pointer& lhs, + const json_pointer& rhs) noexcept +{ + return lhs.reference_tokens < rhs.reference_tokens; +} +#endif + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include +#include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // reverse +#include // array +#include // map +#include // isnan, isinf +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // copy +#include // size_t +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_string +#include // vector + +#ifndef JSON_NO_IO + #include // streamsize + #include // basic_ostream +#endif // JSON_NO_IO + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template> +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + v.insert(v.end(), s, s + length); + } + + private: + std::vector& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + template> + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// how to encode BJData +enum class bjdata_version_t +{ + draft2, + draft3, +}; + +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_data.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j)); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_data.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_data.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_data.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_data.m_value.number_integer; + if (j.m_data.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_data.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_data.m_value.number_unsigned)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_data.m_value.number_unsigned)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_data.m_value.number_unsigned)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_data.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_data.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_data.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_data.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_data.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_data.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_data.m_value.string->c_str()), + j.m_data.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_data.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_data.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_data.m_value.binary->has_subtype()) + { + if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd8)); + write_number(static_cast(j.m_data.m_value.binary->subtype())); + } + else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd9)); + write_number(static_cast(j.m_data.m_value.binary->subtype())); + } + else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xda)); + write_number(static_cast(j.m_data.m_value.binary->subtype())); + } + else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xdb)); + write_number(static_cast(j.m_data.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_data.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_data.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_data.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_data.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_data.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_data.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_data.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + } + else + { + if (j.m_data.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_data.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_data.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_data.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_data.m_value.string->c_str()), + j.m_data.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_data.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_data.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether to use the ext or fixext types + const bool use_ext = j.m_data.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_data.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + const std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + const std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_data.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_data.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_data.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_data.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + @param[in] use_bjdata whether write in BJData format, default is false + @param[in] bjdata_version which BJData version to use, default is draft2 + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true, + const bool use_bjdata = false, const bjdata_version_t bjdata_version = bjdata_version_t::draft2) + { + const bool bjdata_draft3 = use_bjdata && bjdata_version == bjdata_version_t::draft3; + + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_data.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_data.m_value.number_integer, add_prefix, use_bjdata); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_data.m_value.number_unsigned, add_prefix, use_bjdata); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_data.m_value.number_float, add_prefix, use_bjdata); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata); + oa->write_characters( + reinterpret_cast(j.m_data.m_value.string->c_str()), + j.m_data.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_data.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix, use_bjdata](const BasicJsonType & v) + { + return ubjson_prefix(v, use_bjdata) == first_prefix; + }); + + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + + if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_data.m_value.array->size(), true, use_bjdata); + } + + for (const auto& el : *j.m_data.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required, use_bjdata, bjdata_version); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty())) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character(bjdata_draft3 ? 'B' : 'U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_data.m_value.binary->size(), true, use_bjdata); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_data.m_value.binary->data()), + j.m_data.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type(bjdata_draft3 ? 'B' : 'U')); + oa->write_character(j.m_data.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end()) + { + if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type, bjdata_version)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + { + break; + } + } + + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_data.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix, use_bjdata](const BasicJsonType & v) + { + return ubjson_prefix(v, use_bjdata) == first_prefix; + }); + + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + + if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_data.m_value.object->size(), true, use_bjdata); + } + + for (const auto& el : *j.m_data.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + case value_t::discarded: + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); + } + + static_cast(j); + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value, true); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul), true); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value), true); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value), true); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(j.m_data.m_value.number_unsigned), true); + } + else if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(j.m_data.m_value.number_unsigned), true); + } + else + { + write_bson_entry_header(name, 0x11 /* uint64 */); + write_number(static_cast(j.m_data.m_value.number_unsigned), true); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value)), true); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size()), true); + write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_data.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_data.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_data.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_data.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_data.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_data.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_data.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_data.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_data.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_data.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_data.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_data.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_data.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value)), true); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix, + const bool use_bjdata) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n, use_bjdata); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix, + const bool use_bjdata) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n), use_bjdata); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n), use_bjdata); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('u')); // uint16 - bjdata only + } + write_number(static_cast(n), use_bjdata); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('m')); // uint32 - bjdata only + } + write_number(static_cast(n), use_bjdata); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('M')); // uint64 - bjdata only + } + write_number(static_cast(n), use_bjdata); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true, use_bjdata); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix, + const bool use_bjdata) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n), use_bjdata); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n), use_bjdata); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)()))) + { + if (add_prefix) + { + oa->write_character(to_char_type('u')); // uint16 - bjdata only + } + write_number(static_cast(n), use_bjdata); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)()))) + { + if (add_prefix) + { + oa->write_character(to_char_type('m')); // uint32 - bjdata only + } + write_number(static_cast(n), use_bjdata); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n), use_bjdata); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true, use_bjdata); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_data.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if (use_bjdata && ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)())) + { + return 'u'; + } + if ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if (use_bjdata && ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)())) + { + return 'm'; + } + if ((std::numeric_limits::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as a high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'u'; + } + if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'm'; + } + if (j.m_data.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + if (use_bjdata && j.m_data.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'M'; + } + // anything else is treated as a high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_data.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + case value_t::discarded: + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /*! + @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid + */ + bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type, const bjdata_version_t bjdata_version) + { + std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, + {"char", 'C'}, {"byte", 'B'} + }; + + string_t key = "_ArrayType_"; + auto it = bjdtype.find(static_cast(value.at(key))); + if (it == bjdtype.end()) + { + return true; + } + CharType dtype = it->second; + + key = "_ArraySize_"; + std::size_t len = (value.at(key).empty() ? 0 : 1); + for (const auto& el : value.at(key)) + { + len *= static_cast(el.m_data.m_value.number_unsigned); + } + + key = "_ArrayData_"; + if (value.at(key).size() != len) + { + return true; + } + + oa->write_character('['); + oa->write_character('$'); + oa->write_character(dtype); + oa->write_character('#'); + + key = "_ArraySize_"; + write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version); + + key = "_ArrayData_"; + if (dtype == 'U' || dtype == 'C' || dtype == 'B') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_unsigned), true); + } + } + else if (dtype == 'i') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_integer), true); + } + } + else if (dtype == 'u') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_unsigned), true); + } + } + else if (dtype == 'I') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_integer), true); + } + } + else if (dtype == 'm') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_unsigned), true); + } + } + else if (dtype == 'l') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_integer), true); + } + } + else if (dtype == 'M') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_unsigned), true); + } + } + else if (dtype == 'L') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_integer), true); + } + } + else if (dtype == 'd') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_float), true); + } + } + else if (dtype == 'D') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_data.m_value.number_float), true); + } + } + return false; + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @param[in] OutputIsLittleEndian Set to true if output data is + required to be little endian + @tparam NumberType the type of the number + + @note This function needs to respect the system's endianness, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + On the other hand, BSON and BJData use little endian and should reorder + on big endian systems. + */ + template + void write_number(const NumberType n, const bool OutputIsLittleEndian = false) + { + // step 1: write the number to an array of length NumberType + std::array vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write the array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (!std::isfinite(n) || ((static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)))) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + // The std::is_trivial trait is deprecated in C++26. The replacement is to use + // std::is_trivially_copyable and std::is_trivially_default_constructible. + // However, some older library implementations support std::is_trivial + // but not all the std::is_trivially_* traits. + // Since detecting full support across all libraries is difficult, + // we use std::is_trivial unless we are using a standard where it has been deprecated. + // For more details, see: https://github.com/nlohmann/json/pull/4775#issuecomment-2884361627 +#ifdef JSON_HAS_CPP_26 + static_assert(std::is_trivially_copyable::value, "CharType must be trivially copyable"); + static_assert(std::is_trivially_default_constructible::value, "CharType must be trivially default constructible"); +#else + static_assert(std::is_trivial::value, "CharType must be trivial"); +#endif + + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the output + output_adapter_t oa = nullptr; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // setfill, setw +#include // is_same +#include // move + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2009 Florian Loitsch +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively, we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const auto bits = static_cast(reinterpret_bits(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor of v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp((2 * v.f) + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp((4 * v.f) - 1, v.e - 2) // (B) + : diyfp((2 * v.f) - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually, this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = ((f * 78913) / (1 << 18)) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + if (n >= 100000) + { + pow10 = 100000; + return 6; + } + if (n >= 10000) + { + pow10 = 10000; + return 5; + } + if (n >= 1000) + { + pow10 = 1000; + return 4; + } + if (n >= 100) + { + pow10 = 100; + return 3; + } + if (n >= 10) + { + pow10 = 10; + return 2; + } + + pow10 = 1; + return 1; +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10{}; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead, it looks as if the buffer + // could be decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if) + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + (k / 10)); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + (k / 100)); + k %= 100; + *buf++ = static_cast('0' + (k / 10)); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e., the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // deleted because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_data.m_type) + { + case value_t::object: + { + if (val.m_data.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_data.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_data.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_data.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_data.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_data.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_data.m_value.array->cbegin(); + i != val.m_data.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_data.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_data.m_value.array->cbegin(); + i != val.m_data.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_data.m_value.array->empty()); + dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_data.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_data.m_value.binary->empty()) + { + for (auto i = val.m_data.m_value.binary->cbegin(); + i != val.m_data.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_data.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_data.m_value.binary->has_subtype()) + { + dump_integer(val.m_data.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_data.m_value.binary->empty()) + { + for (auto i = val.m_data.m_value.binary->cbegin(); + i != val.m_data.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_data.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_data.m_value.binary->has_subtype()) + { + dump_integer(val.m_data.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_data.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_data.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_data.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_data.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint))); + bytes += 6; + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu)))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr)); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multibyte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast(s.back() | 0))), nullptr)); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } + + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + /*! + * @brief convert a byte to a uppercase hex representation + * @param[in] byte byte to represent + * @return representation ("00".."FF") + */ + static std::string hex_bytes(std::uint8_t byte) + { + std::string result = "FF"; + constexpr const char* nibble_to_hex = "0123456789ABCDEF"; + result[0] = nibble_to_hex[byte / 16]; + result[1] = nibble_to_hex[byte % 16]; + return result; + } + + // templates to avoid warnings about useless casts + template ::value, int> = 0> + bool is_negative_number(NumberType x) + { + return x < 0; + } + + template < typename NumberType, enable_if_t ::value, int > = 0 > + bool is_negative_number(NumberType /*unused*/) + { + return false; + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_integral::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative_number(x)) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward, + // so we later avoid reversing the result + buffer_ptr += static_cast(n_chars); + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get the number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if the buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separators + if (thousands_sep != '\0') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if we need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + JSON_ASSERT(static_cast(byte) < utf8d.size()); + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + const std::size_t index = 256u + (static_cast(state) * 16u) + static_cast(type); + JSON_ASSERT(index < utf8d.size()); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as an unsigned integer. The plus/minus shuffling is necessary as we + * cannot directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // equal_to, less +#include // initializer_list +#include // input_iterator_tag, iterator_traits +#include // allocator +#include // for out_of_range +#include // enable_if, is_convertible +#include // pair +#include // vector + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using size_type = typename Container::size_type; + using value_type = typename Container::value_type; +#ifdef JSON_HAS_CPP_14 + using key_compare = std::equal_to<>; +#else + using key_compare = std::equal_to; +#endif + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map() noexcept(noexcept(Container())) : Container{} {} + explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return {it, false}; + } + } + Container::emplace_back(key, std::forward(t)); + return {std::prev(this->end()), true}; + } + + template::value, int> = 0> + std::pair emplace(KeyType && key, T && t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return {it, false}; + } + } + Container::emplace_back(std::forward(key), std::forward(t)); + return {std::prev(this->end()), true}; + } + + T& operator[](const key_type& key) + { + return emplace(key, T{}).first->second; + } + + template::value, int> = 0> + T & operator[](KeyType && key) + { + return emplace(std::forward(key), T{}).first->second; + } + + const T& operator[](const key_type& key) const + { + return at(key); + } + + template::value, int> = 0> + const T & operator[](KeyType && key) const + { + return at(std::forward(key)); + } + + T& at(const key_type& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + template::value, int> = 0> + T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + const T& at(const key_type& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + template::value, int> = 0> + const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const key_type& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + template::value, int> = 0> + size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + return erase(pos, std::next(pos)); + } + + iterator erase(iterator first, iterator last) + { + if (first == last) + { + return first; + } + + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for the first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) + { + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it + } + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; + } + + size_type count(const key_type& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return 1; + } + } + return 0; + } + + template::value, int> = 0> + size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return 1; + } + } + return 0; + } + + iterator find(const key_type& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it; + } + } + return Container::end(); + } + + template::value, int> = 0> + iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const key_type& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, value.first)) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } + +private: + JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare(); +}; + +NLOHMANN_JSON_NAMESPACE_END + + +#if defined(JSON_HAS_CPP_17) + #if JSON_HAS_STATIC_RTTI + #include + #endif + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +NLOHMANN_JSON_NAMESPACE_BEGIN + +/*! +@brief a class to store JSON values + +@internal +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) + : public ::nlohmann::detail::json_base_class +{ + private: + template friend struct detail::external_constructor; + + template + friend class ::nlohmann::json_pointer; + // can be restored when json_pointer backwards compatibility is removed + // friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + using json_base_class_t = ::nlohmann::detail::json_base_class; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// how to encode BJData + using bjdata_version_t = detail::bjdata_version_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + using exception = detail::exception; + using parse_error = detail::parse_error; + using invalid_iterator = detail::invalid_iterator; + using type_error = detail::type_error; + using out_of_range = detail::out_of_range; + using other_error = detail::other_error; + + /// @} + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + /// @brief returns the allocator associated with the container + /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /// @brief returns version information on the library + /// @sa https://json.nlohmann.me/api/basic_json/meta/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2026 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_PATCH)); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( + std::to_string(__GNUC__), '.', + std::to_string(__GNUC_MINOR__), '.', + std::to_string(__GNUC_PATCHLEVEL__)) + } + }; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#if defined(_MSVC_LANG) + result["compiler"]["c++"] = std::to_string(_MSVC_LANG); +#elif defined(__cplusplus) + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /// @brief default object key comparator type + /// The actual object key comparator type (@ref object_comparator_t) may be + /// different. + /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/ +#if defined(JSON_HAS_CPP_14) + // use of transparent comparator avoids unnecessary repeated construction of temporaries + // in functions involving lookup by key with types other than object_t::key_type (aka. StringType) + using default_object_comparator_t = std::less<>; +#else + using default_object_comparator_t = std::less; +#endif + + /// @brief a type for an object + /// @sa https://json.nlohmann.me/api/basic_json/object_t/ + using object_t = ObjectType>>; + + /// @brief a type for an array + /// @sa https://json.nlohmann.me/api/basic_json/array_t/ + using array_t = ArrayType>; + + /// @brief a type for a string + /// @sa https://json.nlohmann.me/api/basic_json/string_t/ + using string_t = StringType; + + /// @brief a type for a boolean + /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/ + using boolean_t = BooleanType; + + /// @brief a type for a number (integer) + /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/ + using number_integer_t = NumberIntegerType; + + /// @brief a type for a number (unsigned) + /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/ + using number_unsigned_t = NumberUnsignedType; + + /// @brief a type for a number (floating-point) + /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/ + using number_float_t = NumberFloatType; + + /// @brief a type for a packed binary type + /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ + using binary_t = nlohmann::byte_container_with_subtype; + + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ + using object_comparator_t = detail::actual_object_comparator_t; + + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = static_cast(false); + break; + } + + case value_t::number_integer: + { + number_integer = static_cast(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = static_cast(0); + break; + } + + case value_t::number_float: + { + number_float = static_cast(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) : string(create(value)) {} + + /// constructor for rvalue strings + json_value(string_t&& value) : string(create(std::move(value))) {} + + /// constructor for objects + json_value(const object_t& value) : object(create(value)) {} + + /// constructor for rvalue objects + json_value(object_t&& value) : object(create(std::move(value))) {} + + /// constructor for arrays + json_value(const array_t& value) : array(create(value)) {} + + /// constructor for rvalue arrays + json_value(array_t&& value) : array(create(std::move(value))) {} + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) : binary(create(std::move(value))) {} + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) : binary(create(std::move(value))) {} + + void destroy(value_t t) + { + if ( + (t == value_t::object && object == nullptr) || + (t == value_t::array && array == nullptr) || + (t == value_t::string && string == nullptr) || + (t == value_t::binary && binary == nullptr) + ) + { + // not initialized (e.g., due to exception in the ctor) + return; + } + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to a local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_data.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_data.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_data.m_value.object->clear(); + } + + // it's now safe that current_item gets destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr); + JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr); + JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr); + JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_data.m_type) + { + case value_t::array: + { + for (auto& element : *m_data.m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_data.m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count_set_parents) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count_set_parents; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count_set_parents); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = detail::unknown_size()) + { +#if JSON_DIAGNOSTICS + if (old_capacity != detail::unknown_size()) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /// @brief parser event types + /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/ + using parse_event_t = detail::parse_event_t; + + /// @brief per-element parser callback type + /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /// @brief create an empty value with a given type + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const value_t v) + : m_data(v) + { + assert_invariant(); + } + + /// @brief create a null object + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape) + : basic_json(value_t::null) + { + assert_invariant(); + } + + /// @brief create a JSON value from compatible types + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /// @brief create a JSON value from an existing one + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) +#if JSON_DIAGNOSTIC_POSITIONS + : start_position(val.start_pos()), + end_position(val.end_pos()) +#endif + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_data.m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + JSON_ASSERT(m_data.m_type == val.type()); + + set_parents(); + assert_invariant(); + } + + /// @brief create a container (array or object) from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int; + // (many string types can be constructed from 0 via its null-pointer guise, so we get a + // broken call to op[key_type], the wrong semantics, and a 4804 warning on Windows) + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast(0)].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if an array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if an object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create an object + m_data.m_type = value_t::object; + m_data.m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_data.m_value.object->emplace( + std::move(*((*element.m_data.m_value.array)[0].m_data.m_value.string)), + std::move((*element.m_data.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create an array + m_data.m_type = value_t::array; + m_data.m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /// @brief explicitly create a binary array (without subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = init; + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = binary_t(init, subtype); + return res; + } + + /// @brief explicitly create a binary array + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = std::move(init); + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = binary_t(std::move(init), subtype); + return res; + } + + /// @brief explicitly create an array from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/array/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /// @brief explicitly create an object from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/object/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /// @brief construct an array with count copies of given value + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(size_type cnt, const basic_json& val): + m_data{cnt, val} + { + set_parents(); + assert_invariant(); + } + + /// @brief construct a JSON container given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) // NOLINT(performance-unnecessary-value-param) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure the iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); + } + + // copy type from the first iterator + m_data.m_type = first.m_object->m_data.m_type; + + // check if the iterator range is complete for primitive values + switch (m_data.m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_data.m_type) + { + case value_t::number_integer: + { + m_data.m_value.number_integer = first.m_object->m_data.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_data.m_value.number_unsigned = first.m_object->m_data.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_data.m_value.number_float = first.m_object->m_data.m_value.number_float; + break; + } + + case value_t::boolean: + { + m_data.m_value.boolean = first.m_object->m_data.m_value.boolean; + break; + } + + case value_t::string: + { + m_data.m_value = *first.m_object->m_data.m_value.string; + break; + } + + case value_t::object: + { + m_data.m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_data.m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_data.m_value = *first.m_object->m_data.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /// @brief copy constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const basic_json& other) + : json_base_class_t(other) +#if JSON_DIAGNOSTIC_POSITIONS + , start_position(other.start_position) + , end_position(other.end_position) +#endif + { + m_data.m_type = other.m_data.m_type; + // check of passed value is valid + other.assert_invariant(); + + switch (m_data.m_type) + { + case value_t::object: + { + m_data.m_value = *other.m_data.m_value.object; + break; + } + + case value_t::array: + { + m_data.m_value = *other.m_data.m_value.array; + break; + } + + case value_t::string: + { + m_data.m_value = *other.m_data.m_value.string; + break; + } + + case value_t::boolean: + { + m_data.m_value = other.m_data.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_data.m_value = other.m_data.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_data.m_value = other.m_data.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_data.m_value = other.m_data.m_value.number_float; + break; + } + + case value_t::binary: + { + m_data.m_value = *other.m_data.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /// @brief move constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(basic_json&& other) noexcept + : json_base_class_t(std::forward(other)), + m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check +#if JSON_DIAGNOSTIC_POSITIONS + , start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check + , end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check +#endif + { + // check that the passed value is valid + other.assert_invariant(false); // cppcheck-suppress[accessForwarded] + + // invalidate payload + other.m_data.m_type = value_t::null; + other.m_data.m_value = {}; + +#if JSON_DIAGNOSTIC_POSITIONS + other.start_position = std::string::npos; + other.end_position = std::string::npos; +#endif + + set_parents(); + assert_invariant(); + } + + /// @brief copy assignment + /// @sa https://json.nlohmann.me/api/basic_json/operator=/ + basic_json& operator=(basic_json other) noexcept ( // NOLINT(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator) + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that the passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_data.m_type, other.m_data.m_type); + swap(m_data.m_value, other.m_data.m_value); + +#if JSON_DIAGNOSTIC_POSITIONS + swap(start_position, other.start_position); + swap(end_position, other.end_position); +#endif + + json_base_class_t::operator=(std::move(other)); + + set_parents(); + assert_invariant(); + return *this; + } + + /// @brief destructor + /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/ + ~basic_json() noexcept + { + assert_invariant(false); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /// @brief serialization + /// @sa https://json.nlohmann.me/api/basic_json/dump/ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /// @brief return the type of the JSON value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/type/ + constexpr value_t type() const noexcept + { + return m_data.m_type; + } + + /// @brief return whether type is primitive + /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /// @brief return whether type is structured + /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /// @brief return whether value is null + /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + constexpr bool is_null() const noexcept + { + return m_data.m_type == value_t::null; + } + + /// @brief return whether value is a boolean + /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + constexpr bool is_boolean() const noexcept + { + return m_data.m_type == value_t::boolean; + } + + /// @brief return whether value is a number + /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /// @brief return whether value is an integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + constexpr bool is_number_integer() const noexcept + { + return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; + } + + /// @brief return whether value is an unsigned integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + constexpr bool is_number_unsigned() const noexcept + { + return m_data.m_type == value_t::number_unsigned; + } + + /// @brief return whether value is a floating-point number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + constexpr bool is_number_float() const noexcept + { + return m_data.m_type == value_t::number_float; + } + + /// @brief return whether value is an object + /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + constexpr bool is_object() const noexcept + { + return m_data.m_type == value_t::object; + } + + /// @brief return whether value is an array + /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + constexpr bool is_array() const noexcept + { + return m_data.m_type == value_t::array; + } + + /// @brief return whether value is a string + /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + constexpr bool is_string() const noexcept + { + return m_data.m_type == value_t::string; + } + + /// @brief return whether value is a binary array + /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + constexpr bool is_binary() const noexcept + { + return m_data.m_type == value_t::binary; + } + + /// @brief return whether value is discarded + /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + constexpr bool is_discarded() const noexcept + { + return m_data.m_type == value_t::discarded; + } + + /// @brief return the type of the JSON value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ + constexpr operator value_t() const noexcept + { + return m_data.m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_data.m_value.boolean; + } + + JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_data.m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_data.m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_data.m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_data.m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_data.m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_data.m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_data.m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_data.m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_data.m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_data.m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_data.m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_data.m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + auto ret = ValueType(); + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /// @brief get a value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_to/ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow calling get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif +#if defined(JSON_HAS_CPP_17) && JSON_HAS_STATIC_RTTI + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); + } + + return *get_ptr(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); + } + + return *get_ptr(); + } + + /// @} + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_data.m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create a better exception explanation + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); + } // cppcheck-suppress[missingReturn] + } + else + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + } + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_data.m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create a better exception explanation + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); + } // cppcheck-suppress[missingReturn] + } + else + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(key); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); + } + return set_parent(it->second); + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template::value, int> = 0> + reference at(KeyType && key) + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(std::forward(key)); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward(key)), "' not found"), this)); + } + return set_parent(it->second); + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(key); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); + } + return it->second; + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template::value, int> = 0> + const_reference at(KeyType && key) const + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(std::forward(key)); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward(key)), "' not found"), this)); + } + return it->second; + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](size_type idx) + { + // implicitly convert a null value to an empty array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up the array with null values if given idx is outside the range + if (idx >= m_data.m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size & capacity before resizing + const auto old_size = m_data.m_value.array->size(); + const auto old_capacity = m_data.m_value.array->capacity(); +#endif + m_data.m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + } + else + { + // set parent for values added above + set_parents(begin() + static_cast(old_size), static_cast(idx + 1 - old_size)); + } +#endif + assert_invariant(); + } + + return m_data.m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_data.m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](typename object_t::key_type key) // NOLINT(performance-unnecessary-value-param) + { + // implicitly convert a null value to an empty object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto result = m_data.m_value.object->emplace(std::move(key), nullptr); + return set_parent(result.first->second); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto it = m_data.m_value.object->find(key); + JSON_ASSERT(it != m_data.m_value.object->end()); + return it->second; + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC + // (they seemingly cannot be constrained to resolve the ambiguity) + template + reference operator[](T* key) + { + return operator[](typename object_t::key_type(key)); + } + + template + const_reference operator[](T* key) const + { + return operator[](typename object_t::key_type(key)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template::value, int > = 0 > + reference operator[](KeyType && key) + { + // implicitly convert a null value to an empty object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto result = m_data.m_value.object->emplace(std::forward(key), nullptr); + return set_parent(result.first->second); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template::value, int > = 0 > + const_reference operator[](KeyType && key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto it = m_data.m_value.object->find(std::forward(key)); + JSON_ASSERT(it != m_data.m_value.object->end()); + return it->second; + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + private: + template + using is_comparable_with_object_key = detail::is_comparable < + object_comparator_t, const typename object_t::key_type&, KeyType >; + + template + using value_return_type = std::conditional < + detail::is_c_string_uncvref::value, + string_t, typename std::decay::type >; + + public: + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, detail::enable_if_t < + !detail::is_transparent::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // If 'key' is found, return its value. Otherwise, return `default_value'. + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + !detail::is_transparent::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // If 'key' is found, return its value. Otherwise, return `default_value'. + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class KeyType, detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(KeyType && key, const ValueType& default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // If 'key' is found, return its value. Otherwise, return `default_value'. + const auto it = find(std::forward(key)); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class KeyType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(KeyType && key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // If 'key' is found, return its value. Otherwise, return `default_value'. + const auto it = find(std::forward(key)); + if (it != end()) + { + return it->template get(); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, detail::enable_if_t < + detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // If the pointer resolves to a value, return it. Otherwise, return + // 'default_value'. + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const json_pointer& ptr, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // If the pointer resolves to a value, return it. Otherwise, return + // 'default_value'. + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return std::forward(default_value); + } + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + template < class ValueType, class BasicJsonType, detail::enable_if_t < + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const + { + return value(ptr.convert(), default_value); + } + + template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + ReturnType value(const ::nlohmann::json_pointer& ptr, ValueType && default_value) const + { + return value(ptr.convert(), std::forward(default_value)); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + reference front() + { + return *begin(); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + const_reference front() const + { + return *cbegin(); + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /// @brief remove element given an iterator + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, detail::enable_if_t < + std::is_same::value || + std::is_same::value, int > = 0 > + IteratorType erase(IteratorType pos) // NOLINT(performance-unnecessary-value-param) + { + // make sure the iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + IteratorType result = end(); + + switch (m_data.m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.string); + std::allocator_traits::deallocate(alloc, m_data.m_value.string, 1); + m_data.m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.binary); + std::allocator_traits::deallocate(alloc, m_data.m_value.binary, 1); + m_data.m_value.binary = nullptr; + } + + m_data.m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_data.m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_data.m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return result; + } + + /// @brief remove elements given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, detail::enable_if_t < + std::is_same::value || + std::is_same::value, int > = 0 > + IteratorType erase(IteratorType first, IteratorType last) // NOLINT(performance-unnecessary-value-param) + { + // make sure the iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); + } + + IteratorType result = end(); + + switch (m_data.m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.string); + std::allocator_traits::deallocate(alloc, m_data.m_value.string, 1); + m_data.m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.binary); + std::allocator_traits::deallocate(alloc, m_data.m_value.binary, 1); + m_data.m_value.binary = nullptr; + } + + m_data.m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_data.m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return result; + } + + private: + template < typename KeyType, detail::enable_if_t < + detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) + { + // this erase only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return m_data.m_value.object->erase(std::forward(key)); + } + + template < typename KeyType, detail::enable_if_t < + !detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) + { + // this erase only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + const auto it = m_data.m_value.object->find(std::forward(key)); + if (it != m_data.m_value.object->end()) + { + m_data.m_value.object->erase(it); + return 1; + } + return 0; + } + + public: + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // the indirection via erase_internal() is added to avoid making this + // function a template and thus de-rank it during overload resolution + return erase_internal(key); + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template::value, int> = 0> + size_type erase(KeyType && key) + { + return erase_internal(std::forward(key)); + } + + /// @brief remove element from a JSON array given an index + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); + } + + m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + } + + /// @} + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + iterator find(const typename object_t::key_type& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(key); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + const_iterator find(const typename object_t::key_type& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(key); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template::value, int> = 0> + iterator find(KeyType && key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template::value, int> = 0> + const_iterator find(KeyType && key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + size_type count(const typename object_t::key_type& key) const + { + // return 0 for all nonobject types + return is_object() ? m_data.m_value.object->count(key) : 0; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template::value, int> = 0> + size_type count(KeyType && key) const + { + // return 0 for all nonobject types + return is_object() ? m_data.m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const typename object_t::key_type& key) const + { + return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + template::value, int> = 0> + bool contains(KeyType && key) const + { + return is_object() && m_data.m_value.object->find(std::forward(key)) != m_data.m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object given a JSON pointer + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + bool contains(const typename ::nlohmann::json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /// @brief returns a const iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/cbegin/ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + const_iterator end() const noexcept + { + return cend(); + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/cend/ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /// @brief returns a const reverse iterator to the last element + /// @sa https://json.nlohmann.me/api/basic_json/crbegin/ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /// @brief returns a const reverse iterator to one before the first + /// @sa https://json.nlohmann.me/api/basic_json/crend/ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /// @brief checks whether the container is empty. + /// @sa https://json.nlohmann.me/api/basic_json/empty/ + bool empty() const noexcept + { + switch (m_data.m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_data.m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_data.m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /// @brief returns the number of elements + /// @sa https://json.nlohmann.me/api/basic_json/size/ + size_type size() const noexcept + { + switch (m_data.m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_data.m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_data.m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /// @brief returns the maximum possible number of elements + /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + size_type max_size() const noexcept + { + switch (m_data.m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_data.m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_data.m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /// @brief clears the contents + /// @sa https://json.nlohmann.me/api/basic_json/clear/ + void clear() noexcept + { + switch (m_data.m_type) + { + case value_t::number_integer: + { + m_data.m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_data.m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_data.m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_data.m_value.boolean = false; + break; + } + + case value_t::string: + { + m_data.m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_data.m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_data.m_value.array->clear(); + break; + } + + case value_t::object: + { + m_data.m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); + } + + // transform a null object into an array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value = value_t::array; + assert_invariant(); + } + + // add the element to the array (move semantics) + const auto old_capacity = m_data.m_value.array->capacity(); + m_data.m_value.array->push_back(std::move(val)); + set_parent(m_data.m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); + } + + // transform a null object into an array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value = value_t::array; + assert_invariant(); + } + + // add the element to the array + const auto old_capacity = m_data.m_value.array->capacity(); + m_data.m_value.array->push_back(val); + set_parent(m_data.m_value.array->back(), old_capacity); + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); + } + + // transform a null object into an object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value = value_t::object; + assert_invariant(); + } + + // add the element to the object + auto res = m_data.m_value.object->insert(val); + set_parent(res.first->second); + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); + } + + // transform a null object into an array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value = value_t::array; + assert_invariant(); + } + + // add the element to the array (perfect forwarding) + const auto old_capacity = m_data.m_value.array->capacity(); + m_data.m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_data.m_value.array->back(), old_capacity); + } + + /// @brief add an object to an object if key does not exist + /// @sa https://json.nlohmann.me/api/basic_json/emplace/ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); + } + + // transform a null object into an object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value = value_t::object; + assert_invariant(); + } + + // add the element to the array (perfect forwarding) + auto res = m_data.m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create a result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) // NOLINT(performance-unnecessary-value-param) + { + iterator result(this); + JSON_ASSERT(m_data.m_value.array != nullptr); + + auto insert_pos = std::distance(m_data.m_value.array->begin(), pos.m_it.array_iterator); + m_data.m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_data.m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_data.m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param) + { + return insert(pos, val); + } + + /// @brief inserts copies of element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + /// @brief inserts range of elements into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /// @brief inserts elements from initializer list into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, initializer_list_t ilist) // NOLINT(performance-unnecessary-value-param) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /// @brief inserts range of elements into object + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + void insert(const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this)); + } + + m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + set_parents(); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_reference j, bool merge_objects = false) + { + update(j.begin(), j.end(), merge_objects); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param) + { + // implicitly convert a null value to an empty object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object)); + } + + for (auto it = first; it != last; ++it) + { + if (merge_objects && it.value().is_object()) + { + auto it2 = m_data.m_value.object->find(it.key()); + if (it2 != m_data.m_value.object->end()) + { + it2->second.update(it.value(), true); + continue; + } + } + m_data.m_value.object->operator[](it.key()) = it.value(); +#if JSON_DIAGNOSTICS + m_data.m_value.object->operator[](it.key()).m_parent = this; +#endif + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_data.m_type, other.m_data.m_type); + std::swap(m_data.m_value, other.m_data.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + using std::swap; + swap(*(m_data.m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + using std::swap; + swap(*(m_data.m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + using std::swap; + swap(*(m_data.m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + using std::swap; + swap(*(m_data.m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + using std::swap; + swap(*(m_data.m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this)); + } + } + + /// @} + + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + // note parentheses around operands are necessary; see + // https://github.com/nlohmann/json/issues/1530 +#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \ + const auto lhs_type = lhs.type(); \ + const auto rhs_type = rhs.type(); \ + \ + if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \ + { \ + switch (lhs_type) \ + { \ + case value_t::array: \ + return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array); \ + \ + case value_t::object: \ + return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \ + \ + case value_t::null: \ + return (null_result); \ + \ + case value_t::string: \ + return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \ + \ + case value_t::boolean: \ + return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \ + \ + case value_t::number_integer: \ + return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \ + \ + case value_t::number_unsigned: \ + return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \ + \ + case value_t::number_float: \ + return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float); \ + \ + case value_t::binary: \ + return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary); \ + \ + case value_t::discarded: \ + default: \ + return (unordered_result); \ + } \ + } \ + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \ + { \ + return static_cast(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_float; \ + } \ + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \ + { \ + return lhs.m_data.m_value.number_float op static_cast(rhs.m_data.m_value.number_integer); \ + } \ + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \ + { \ + return static_cast(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_float; \ + } \ + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \ + { \ + return lhs.m_data.m_value.number_float op static_cast(rhs.m_data.m_value.number_unsigned); \ + } \ + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \ + { \ + return static_cast(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_integer; \ + } \ + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \ + { \ + return lhs.m_data.m_value.number_integer op static_cast(rhs.m_data.m_value.number_unsigned); \ + } \ + else if(compares_unordered(lhs, rhs))\ + {\ + return (unordered_result);\ + }\ + \ + return (default_result); + + JSON_PRIVATE_UNLESS_TESTED: + // returns true if: + // - any operand is NaN and the other operand is of number type + // - any operand is discarded + // in legacy mode, discarded values are considered ordered if + // an operation is computed as an odd number of inverses of others + static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept + { + if ((lhs.is_number_float() && std::isnan(lhs.m_data.m_value.number_float) && rhs.is_number()) + || (rhs.is_number_float() && std::isnan(rhs.m_data.m_value.number_float) && lhs.is_number())) + { + return true; + } +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + return (lhs.is_discarded() || rhs.is_discarded()) && !inverse; +#else + static_cast(inverse); + return lhs.is_discarded() || rhs.is_discarded(); +#endif + } + + private: + bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept + { + return compares_unordered(*this, rhs, inverse); + } + + public: +#if JSON_HAS_THREE_WAY_COMPARISON + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + bool operator==(const_reference rhs) const noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const_reference lhs = *this; + JSON_IMPLEMENT_OPERATOR( ==, true, false, false) +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template + requires std::is_scalar_v + bool operator==(ScalarType rhs) const noexcept + { + return *this == basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + bool operator!=(const_reference rhs) const noexcept + { + if (compares_unordered(rhs, true)) + { + return false; + } + return !operator==(rhs); + } + + /// @brief comparison: 3-way + /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/ + std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD* + { + const_reference lhs = *this; + // default_result is used if we cannot compare values. In that case, + // we compare types. + JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD* + std::partial_ordering::equivalent, + std::partial_ordering::unordered, + lhs_type <=> rhs_type) // *NOPAD* + } + + /// @brief comparison: 3-way + /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/ + template + requires std::is_scalar_v + std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD* + { + return *this <=> basic_json(rhs); // *NOPAD* + } + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + // all operators that are computed as an odd number of inverses of others + // need to be overloaded to emulate the legacy comparison behavior + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON) + bool operator<=(const_reference rhs) const noexcept + { + if (compares_unordered(rhs, true)) + { + return false; + } + return !(rhs < *this); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template + requires std::is_scalar_v + bool operator<=(ScalarType rhs) const noexcept + { + return *this <= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON) + bool operator>=(const_reference rhs) const noexcept + { + if (compares_unordered(rhs, true)) + { + return false; + } + return !(*this < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template + requires std::is_scalar_v + bool operator>=(ScalarType rhs) const noexcept + { + return *this >= basic_json(rhs); + } +#endif +#else + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + JSON_IMPLEMENT_OPERATOR( ==, true, false, false) +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + if (compares_unordered(lhs, rhs, true)) + { + return false; + } + return !(lhs == rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + // default_result is used if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type)) + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + if (compares_unordered(lhs, rhs, true)) + { + return false; + } + return !(rhs < lhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + // double inverse + if (compares_unordered(lhs, rhs)) + { + return false; + } + return !(lhs <= rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + if (compares_unordered(lhs, rhs, true)) + { + return false; + } + return !(lhs < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } +#endif + +#undef JSON_IMPLEMENT_OPERATOR + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as the indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + /// @deprecated This function is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator<<(std::ostream&, const basic_json&) instead; that is, + /// replace calls like `j >> o;` with `o << j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /// @brief deserialize from a compatible input + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded] + return result; + } + + /// @brief deserialize from a pair of character iterators + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved] + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { + basic_json result; + parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved] + return result; + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(InputType&& i, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { + return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + /// @deprecated This function is deprecated since 3.8.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// sax_parse(ptr, ptr + len) instead. + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false, + const bool ignore_trailing_commas = false) + { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator>>(std::istream&, basic_json&) instead; that is, + /// replace calls like `j << i;` with `i >> j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /// @brief return the type as string + /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + switch (m_data.m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + return "number"; + default: + return "invalid"; + } + } + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + struct data + { + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + data(const value_t v) + : m_type(v), m_value(v) + { + } + + data(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + } + + data() noexcept = default; + data(data&&) noexcept = default; + data(const data&) noexcept = delete; + data& operator=(data&&) noexcept = delete; + data& operator=(const data&) noexcept = delete; + + ~data() noexcept + { + m_value.destroy(m_type); + } + }; + + data m_data = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + +#if JSON_DIAGNOSTIC_POSITIONS + /// the start position of the value + std::size_t start_position = std::string::npos; + /// the end position of the value + std::size_t end_position = std::string::npos; + public: + constexpr std::size_t start_pos() const noexcept + { + return start_position; + } + + constexpr std::size_t end_pos() const noexcept + { + return end_position; + } +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static std::vector to_bjdata(const basic_json& j, + const bool use_size = false, + const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) + { + std::vector result; + to_bjdata(j, result, use_size, use_type, version); + return result; + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true, version); + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true, version); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + auto ia = detail::input_adapter(std::forward(i)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + auto ia = detail::input_adapter(std::move(first), std::move(last)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::forward(i)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::move(first), std::move(last)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::forward(i)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::move(first), std::move(last)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::forward(i)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::move(first), std::move(last)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::forward(i)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = detail::input_adapter(std::move(first), std::move(last)); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference operator[](const ::nlohmann::json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference operator[](const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference at(const ::nlohmann::json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference at(const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /// @brief return flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/flatten/ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /// @brief unflatten a previously flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /// @brief applies a JSON patch in-place without copying the object + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + void patch_inplace(const basic_json& json_patch) + { + basic_json& result = *this; + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const string_t& op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, const basic_json & val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer const top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to the parent of the JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + // parent must exist when performing patch add per RFC6902 specs + basic_json& parent = result.at(ptr); + + switch (parent.m_data.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::template array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent, it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, & result](json_pointer & ptr) + { + // get reference to the parent of the JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::template array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const string_t& op, + const string_t& member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_data.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // NOLINT(bugprone-unused-local-non-trivial-variable) + + // check if the desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); + } + + // check if the result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json const v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json const v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if the test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val)); + } + } + } + } + + /// @brief applies a JSON patch to a copy of the current object + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + basic_json result = *this; + result.patch_inplace(json_patch); + return result; + } + + /// @brief creates a diff as a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/diff/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const string_t& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return an empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', detail::to_string(i))); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", detail::concat(path, '/', detail::to_string(i))} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", detail::concat(path, "/-")}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /// @brief applies a JSON Merge Patch + /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/// @brief user-defined to_string function for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/to_string/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} + +inline namespace literals +{ +inline namespace json_literals +{ + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ +JSON_HEDLEY_NON_NULL(1) +#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + inline nlohmann::json operator""_json(const char* s, std::size_t n) +#else + // GCC 4.8 requires a space between "" and suffix + inline nlohmann::json operator"" _json(const char* s, std::size_t n) +#endif +{ + return nlohmann::json::parse(s, s + n); +} + +#if defined(__cpp_char8_t) +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) +{ + return nlohmann::json::parse(reinterpret_cast(s), + reinterpret_cast(s) + n); +} +#endif + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ +JSON_HEDLEY_NON_NULL(1) +#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n) +#else + // GCC 4.8 requires a space between "" and suffix + inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n) +#endif +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); +} +#endif + +} // namespace json_literals +} // namespace literals +NLOHMANN_JSON_NAMESPACE_END + +/////////////////////// +// nonmember support // +/////////////////////// + +namespace std // NOLINT(cert-dcl58-cpp) +{ + +/// @brief hash value for JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_hash/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct hash // NOLINT(cert-dcl58-cpp) +{ + std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const + { + return nlohmann::detail::hash(j); + } +}; + +// specialization for std::less +template<> +struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679 +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(::nlohmann::detail::value_t lhs, + ::nlohmann::detail::value_t rhs) const noexcept + { +#if JSON_HAS_THREE_WAY_COMPARISON + return std::is_lt(lhs <=> rhs); // *NOPAD* +#else + return ::nlohmann::detail::operator<(lhs, rhs); +#endif + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/// @brief exchanges the values of two JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_swap/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + is_nothrow_move_assignable::value) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +#if JSON_USE_GLOBAL_UDLS + #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #else + // GCC 4.8 requires a space between "" and suffix + using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #endif +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// restore clang diagnostic settings +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_THROW +#undef JSON_PRIVATE_UNLESS_TESTED +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL +#undef JSON_INLINE_VARIABLE +#undef JSON_NO_UNIQUE_ADDRESS +#undef JSON_DISABLE_ENUM_SERIALIZATION +#undef JSON_USE_GLOBAL_UDLS + +#ifndef JSON_TEST_KEEP_MACROS + #undef JSON_CATCH + #undef JSON_TRY + #undef JSON_HAS_CPP_11 + #undef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_20 + #undef JSON_HAS_CPP_23 + #undef JSON_HAS_CPP_26 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #undef JSON_HAS_THREE_WAY_COMPARISON + #undef JSON_HAS_RANGES + #undef JSON_HAS_STATIC_RTTI + #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/test-app/runtime/src/main/cpp/runtime/jni/JEnv.h b/test-app/runtime/src/main/cpp/runtime/jni/JEnv.h index d1de99d5..b1a544e4 100644 --- a/test-app/runtime/src/main/cpp/runtime/jni/JEnv.h +++ b/test-app/runtime/src/main/cpp/runtime/jni/JEnv.h @@ -12,6 +12,12 @@ namespace tns { JEnv(JNIEnv *jniEnv); + // Wrap an already-obtained JNIEnv* WITHOUT re-querying the JavaVM + // (no GetEnv). Use only when the pointer is known to belong to the + // current attached thread (e.g. threaded down from a callback prologue). + enum class Adopt { Trusted }; + JEnv(JNIEnv *jniEnv, Adopt) : m_env(jniEnv) {} + ~JEnv(); operator JNIEnv *() const; @@ -20,6 +26,10 @@ namespace tns { jsize GetArrayLength(jarray array); + inline bool isSameObject(jobject obj1, jobject obj2) { + return m_env->IsSameObject(obj1, obj2) == JNI_TRUE; + } + jmethodID GetMethodID(jclass clazz, const std::string &name, const std::string &sig); jmethodID GetStaticMethodID(jclass clazz, const std::string &name, const std::string &sig); diff --git a/test-app/runtime/src/main/cpp/runtime/jni/LRUCache.h b/test-app/runtime/src/main/cpp/runtime/jni/LRUCache.h index c3318ff6..7b829c3d 100644 --- a/test-app/runtime/src/main/cpp/runtime/jni/LRUCache.h +++ b/test-app/runtime/src/main/cpp/runtime/jni/LRUCache.h @@ -45,8 +45,8 @@ class LRUCache { // Constuctor specifies the cached function and // the maximum number of records to be stored - LRUCache(value_type (*loadCallback)(const key_type&, void*), void (*evictCallback)(const value_type&, void*), size_t capacity, void* state) - : m_loadCallback(loadCallback), m_capacity(capacity), m_evictCallback(evictCallback), m_state(state) { + LRUCache(value_type (*loadCallback)(const key_type&, void*), void (*evictCallback)(const value_type&, void*), bool (*cacheValidCallback)(const key_type&, const value_type&, void*), size_t capacity, void* state) + : m_loadCallback(loadCallback), m_capacity(capacity), m_evictCallback(evictCallback), m_cacheValidCallback(cacheValidCallback), m_state(state) { assert(m_loadCallback != nullptr); assert((0 < m_capacity) && (m_capacity < 10000)); } @@ -57,6 +57,15 @@ class LRUCache { // Attempt to find existing record auto it = m_key_to_value.find(k); + if (m_cacheValidCallback != nullptr && it != m_key_to_value.end()) { + // Check if the cached value is still valid (e.g. a jweak that no + // longer points to a live object); if not, evict and treat as miss. + if (!m_cacheValidCallback(k, (*it).second.first, m_state)) { + evictKey(k); + it = m_key_to_value.end(); + } + } + if (it == m_key_to_value.end()) { // We don't have it: @@ -98,6 +107,18 @@ class LRUCache { private: + // Evict a specific key (used when a cached value is no longer valid). + void evictKey(const key_type& key) { + auto it = m_key_to_value.find(key); + if (it != m_key_to_value.end()) { + if (m_evictCallback != nullptr) { + m_evictCallback((*it).second.first, m_state); + } + m_key_tracker.erase((*it).second.second); + m_key_to_value.erase(it); + } + } + // Record a fresh key-value pair in the cache void insert(const key_type& k, const value_type& v) { // Method is only called on cache misses @@ -141,6 +162,8 @@ class LRUCache { void (*m_evictCallback)(const value_type&, void*); + bool (*m_cacheValidCallback)(const key_type&, const value_type&, void*); + // Maximum number of key-value pairs to be retained const size_t m_capacity; diff --git a/test-app/runtime/src/main/cpp/runtime/jsonhelper/JSONObjectHelper.cpp b/test-app/runtime/src/main/cpp/runtime/jsonhelper/JSONObjectHelper.cpp index 4b2f356d..717cca71 100644 --- a/test-app/runtime/src/main/cpp/runtime/jsonhelper/JSONObjectHelper.cpp +++ b/test-app/runtime/src/main/cpp/runtime/jsonhelper/JSONObjectHelper.cpp @@ -8,20 +8,25 @@ using namespace tns; void JSONObjectHelper::RegisterFromFunction(napi_env env, napi_value value) { + napi_status status; napi_valuetype type; - napi_typeof(env, value, &type); + NAPI_GUARD(napi_typeof(env, value, &type)) { + return; + } if (type != napi_function && type != napi_object) { return; } bool hasProperty; - napi_has_named_property(env, value, "from", &hasProperty); + NAPI_GUARD(napi_has_named_property(env, value, "from", &hasProperty)) { + return; + } if (hasProperty) { return; } napi_value from = CreateFromFunction(env); - napi_set_named_property(env, value, "from", from); + NAPI_GUARD(napi_set_named_property(env, value, "from", from)) {} } @@ -59,11 +64,16 @@ napi_value JSONObjectHelper::CreateFromFunction(napi_env env) { } })();)"; + napi_status status; napi_value script; - napi_create_string_utf8(env, source, NAPI_AUTO_LENGTH, &script); + NAPI_GUARD(napi_create_string_utf8(env, source, NAPI_AUTO_LENGTH, &script)) { + return nullptr; + } napi_value result; - js_execute_script(env, script, "", &result); + NAPI_GUARD(js_execute_script(env, script, "", &result)) { + return nullptr; + } return result; } \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/runtime/messageloop/MessageLoopTimer.cpp b/test-app/runtime/src/main/cpp/runtime/messageloop/MessageLoopTimer.cpp index 4193445b..6fbc8bdd 100644 --- a/test-app/runtime/src/main/cpp/runtime/messageloop/MessageLoopTimer.cpp +++ b/test-app/runtime/src/main/cpp/runtime/messageloop/MessageLoopTimer.cpp @@ -6,6 +6,7 @@ #include #include "NativeScriptAssert.h" #include "Runtime.h" +#include "native_api_util.h" using namespace tns; @@ -17,27 +18,39 @@ void MessageLoopTimer::Init(napi_env env) { void MessageLoopTimer::RegisterStartStopFunctions(napi_env env) { + napi_status status; napi_value timer_start; napi_value timer_stop; const char * timer_start_name = "__messageLoopTimerStart"; const char * timer_stop_name = "__messageLoopTimerStop"; - napi_create_function(env, timer_start_name, strlen(timer_start_name), MessageLoopTimer::StartCallback, - this, &timer_start); - napi_create_function(env, timer_stop_name, strlen(timer_stop_name), MessageLoopTimer::StopCallback, - this, &timer_stop); + NAPI_GUARD(napi_create_function(env, timer_start_name, strlen(timer_start_name), MessageLoopTimer::StartCallback, + this, &timer_start)) { + return; + } + NAPI_GUARD(napi_create_function(env, timer_stop_name, strlen(timer_stop_name), MessageLoopTimer::StopCallback, + this, &timer_stop)) { + return; + } napi_value global; - napi_get_global(env, &global); - napi_set_named_property(env, global, timer_start_name, timer_start); - napi_set_named_property(env, global, timer_stop_name, timer_stop); + NAPI_GUARD(napi_get_global(env, &global)) { + return; + } + NAPI_GUARD(napi_set_named_property(env, global, timer_start_name, timer_start)) { + return; + } + NAPI_GUARD(napi_set_named_property(env, global, timer_stop_name, timer_stop)) {} } napi_value MessageLoopTimer::StartCallback(napi_env env, napi_callback_info info) { - void * data; - napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data); + napi_status status; + void * data = nullptr; + NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data)) { + return nullptr; + } auto self = static_cast(data); @@ -53,8 +66,8 @@ napi_value MessageLoopTimer::StartCallback(napi_env env, napi_callback_info info return nullptr; } - int status = pipe(self->m_fd); - if (status != 0) { + int pipeStatus = pipe(self->m_fd); + if (pipeStatus != 0) { __android_log_print(ANDROID_LOG_ERROR, "NAPI", "Unable to create a pipe: %s", strerror(errno)); return nullptr; } @@ -69,8 +82,11 @@ napi_value MessageLoopTimer::StartCallback(napi_env env, napi_callback_info info } napi_value MessageLoopTimer::StopCallback(napi_env env, napi_callback_info info) { - void * data; - napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data); + napi_status status; + void * data = nullptr; + NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data)) { + return nullptr; + } auto self = static_cast(data); diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.cpp b/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.cpp index 7572a931..433f328e 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.cpp +++ b/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.cpp @@ -8,16 +8,17 @@ using namespace std; using namespace tns; napi_value -FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData *fieldData) { +FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData *fieldData, + ObjectManager *objectManager, JniLocalRef targetJavaObject) { JEnv jEnv; - auto runtime = Runtime::GetRuntime(env); - auto objectManager = runtime->GetObjectManager(); + if (objectManager == nullptr) { + objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + } + napi_status status; napi_value fieldResult; - JniLocalRef targetJavaObject; - auto &fieldMetadata = fieldData->metadata; const auto &fieldTypeName = fieldMetadata.getSig(); @@ -44,8 +45,11 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * } if (!isStatic) { - // Using fast, target is always the original *this* - targetJavaObject = objectManager->GetJavaObjectByJsObjectFast(target); + // The caller usually pre-resolves this (single probe); only fall back to + // resolving here when it wasn't supplied. + if (targetJavaObject.IsNull()) { + targetJavaObject = objectManager->GetJavaObjectByJsObjectFast(target); + } if (targetJavaObject.IsNull()) { stringstream ss; @@ -80,7 +84,9 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * } else { result = jEnv.GetByteField(targetJavaObject, fieldId); } - napi_create_int32(env, result, &fieldResult); + NAPI_GUARD(napi_create_int32(env, result, &fieldResult)) { + return nullptr; + } break; } case 'C': { // char @@ -105,7 +111,9 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * } else { result = jEnv.GetShortField(targetJavaObject, fieldId); } - napi_create_int32(env, result, &fieldResult); + NAPI_GUARD(napi_create_int32(env, result, &fieldResult)) { + return nullptr; + } break; } case 'I': { // int @@ -116,7 +124,9 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * result = jEnv.GetIntField(targetJavaObject, fieldId); } - napi_create_int32(env, result, &fieldResult); + NAPI_GUARD(napi_create_int32(env, result, &fieldResult)) { + return nullptr; + } break; } case 'J': { // long @@ -137,7 +147,9 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * } else { result = jEnv.GetFloatField(targetJavaObject, fieldId); } - napi_create_double(env, (double) result, &fieldResult); + NAPI_GUARD(napi_create_double(env, (double) result, &fieldResult)) { + return nullptr; + } break; } case 'D': { // double @@ -147,7 +159,9 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * } else { result = jEnv.GetDoubleField(targetJavaObject, fieldId); } - napi_create_double(env, (double) result, &fieldResult); + NAPI_GUARD(napi_create_double(env, (double) result, &fieldResult)) { + return nullptr; + } break; } default: { @@ -184,20 +198,22 @@ FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData * } jEnv.DeleteLocalRef(result); } else { - napi_get_null(env, &fieldResult); + NAPI_GUARD(napi_get_null(env, &fieldResult)) { + return nullptr; + } } } return fieldResult; } void FieldAccessor::SetJavaField(napi_env env, napi_value target, napi_value value, - FieldCallbackData *fieldData) { + FieldCallbackData *fieldData, ObjectManager *objectManager, + JniLocalRef targetJavaObject) { JEnv jEnv; - auto runtime = Runtime::GetRuntime(env); - auto objectManager = runtime->GetObjectManager(); - - JniLocalRef targetJavaObject; + if (objectManager == nullptr) { + objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + } auto &fieldMetadata = fieldData->metadata; @@ -230,8 +246,11 @@ void FieldAccessor::SetJavaField(napi_env env, napi_value target, napi_value val } if (!isStatic) { - // Using fast, target is always the original *this* - targetJavaObject = objectManager->GetJavaObjectByJsObjectFast(target); + // The caller usually pre-resolves this (single probe); only fall back to + // resolving here when it wasn't supplied. + if (targetJavaObject.IsNull()) { + targetJavaObject = objectManager->GetJavaObjectByJsObjectFast(target); + } if (targetJavaObject.IsNull()) { stringstream ss; diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.h b/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.h index cdc98675..758847a2 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.h +++ b/test-app/runtime/src/main/cpp/runtime/metadata/FieldAccessor.h @@ -9,9 +9,17 @@ namespace tns { class FieldAccessor { public: - napi_value GetJavaField(napi_env env, napi_value target, FieldCallbackData* fieldData); + // `objectManager` and `targetJavaObject` may be supplied pre-resolved by + // the caller (the accessor callback) so this avoids a locked env->runtime + // lookup and a second host-object probe. Both fall back to resolving + // internally when omitted. + napi_value GetJavaField(napi_env env, napi_value target, FieldCallbackData* fieldData, + ObjectManager* objectManager = nullptr, + JniLocalRef targetJavaObject = JniLocalRef()); - void SetJavaField(napi_env env, napi_value target, napi_value value, FieldCallbackData* fieldData); + void SetJavaField(napi_env env, napi_value target, napi_value value, FieldCallbackData* fieldData, + ObjectManager* objectManager = nullptr, + JniLocalRef targetJavaObject = JniLocalRef()); }; } diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/FieldCallbackData.h b/test-app/runtime/src/main/cpp/runtime/metadata/FieldCallbackData.h index 95476c78..26c2e817 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/FieldCallbackData.h +++ b/test-app/runtime/src/main/cpp/runtime/metadata/FieldCallbackData.h @@ -2,9 +2,12 @@ #define FIELDCALLBACKDATA_H_ #include "jni.h" +#include "js_native_api.h" #include "MetadataEntry.h" namespace tns { + class ObjectManager; + struct FieldCallbackData { FieldCallbackData(MetadataEntry metadata) : @@ -15,6 +18,12 @@ namespace tns { MetadataEntry metadata; jfieldID fid; jclass clazz; + // Cached prototype the accessor lives on; used to detect + // Class.prototype. access when host objects are disabled. + napi_ref prototype = nullptr; + // Cached per-env ObjectManager (this data is created per env, so the + // pointer's lifetime matches it) — avoids a locked env->runtime lookup. + tns::ObjectManager *objectManager = nullptr; }; } diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataBuilder.cpp b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataBuilder.cpp index a2fee850..02422aa7 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataBuilder.cpp +++ b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataBuilder.cpp @@ -52,7 +52,7 @@ MetadataReader MetadataBuilder::BuildMetadata(const std::string &filesPath) { // startup because the receiver is triggered. So even though we are exiting, the receiver will have // done its job - exit(0); + _Exit(0); } else { throw NativeScriptException(ss.str()); diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp index a6425c90..3a9a7ea5 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp +++ b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp @@ -36,13 +36,16 @@ napi_value MetadataNode::CreateArrayObjectConstructor(napi_env env) { auto node = GetOrCreate("java/lang/Object"); auto objectConstructor = node->GetConstructorFunction(env); + napi_status status; napi_value arrayConstructor; const char *name = "ArrayObjectWrapper"; - napi_define_class(env, name, strlen(name), + NAPI_GUARD(napi_define_class(env, name, strlen(name), [](napi_env env, napi_callback_info info) -> napi_value { NAPI_CALLBACK_BEGIN(0) return jsThis; - }, nullptr, 0, nullptr, &arrayConstructor); + }, nullptr, 0, nullptr, &arrayConstructor)) { + return nullptr; + } napi_value proto = napi_util::get_prototype(env, arrayConstructor); ObjectManager::MarkObject(env, proto); @@ -51,6 +54,20 @@ napi_value MetadataNode::CreateArrayObjectConstructor(napi_env env) { napi_util::napi_set_function(env, proto, "getAllValues", ArrayGetAllValuesCallback, nullptr); napi_util::define_property(env, proto, "length", nullptr, ArrayLengthCallback); + // Native helpers (previously synthesized by the JS getNativeArrayProp). + napi_util::napi_set_function(env, proto, "map", ArrayMapCallback, nullptr); + napi_util::napi_set_function(env, proto, "forEach", ArrayForEachCallback, nullptr); + napi_util::napi_set_function(env, proto, "toString", ArrayToStringCallback, nullptr); + { + napi_value globalObj, symbolCtor, symbolIterator, iteratorFn; + NAPI_GUARD(napi_get_global(env, &globalObj)) {} + NAPI_GUARD(napi_get_named_property(env, globalObj, "Symbol", &symbolCtor)) {} + NAPI_GUARD(napi_get_named_property(env, symbolCtor, "iterator", &symbolIterator)) {} + NAPI_GUARD(napi_create_function(env, "[Symbol.iterator]", NAPI_AUTO_LENGTH, + ArraySymbolIteratorCallback, nullptr, &iteratorFn)) {} + NAPI_GUARD(napi_set_property(env, proto, symbolIterator, iteratorFn)) {} + } + napi_util::napi_inherits(env, arrayConstructor, objectConstructor); s_arrayObjects.emplace(env, napi_util::make_ref(env, arrayConstructor)); @@ -60,12 +77,13 @@ napi_value MetadataNode::CreateArrayObjectConstructor(napi_env env) { napi_value MetadataNode::CreateExtendedJSWrapper(napi_env env, ObjectManager *objectManager, const std::string &proxyClassName, - int javaObjectID) { + int javaObjectID, MetadataNode **outNode) { napi_value extInstance = nullptr; auto cacheData = GetCachedExtendedClassData(env, proxyClassName); if (cacheData.node != nullptr) { + extInstance = objectManager->GetEmptyObject(); ObjectManager::MarkSuperCall(env, extInstance); napi_value extendedCtorFunc = napi_util::get_ref_value(env, @@ -73,17 +91,22 @@ napi_value MetadataNode::CreateExtendedJSWrapper(napi_env env, ObjectManager *ob napi_util::setPrototypeOf(env, extInstance, napi_util::get_prototype(env, extendedCtorFunc)); - napi_set_named_property(env, extInstance, CONSTRUCTOR, extendedCtorFunc); + napi_status status; + NAPI_GUARD(napi_set_named_property(env, extInstance, CONSTRUCTOR, extendedCtorFunc)) {} SetInstanceMetadata(env, extInstance, cacheData.node); + *outNode = cacheData.node; } return extInstance; } string MetadataNode::GetTypeMetadataName(napi_env env, napi_value value) { + napi_status status; napi_value typeMetadataName; - napi_get_named_property(env, value, PRIVATE_TYPE_NAME, &typeMetadataName); + NAPI_GUARD(napi_get_named_property(env, value, PRIVATE_TYPE_NAME, &typeMetadataName)) { + return ""; + } return napi_util::get_string_value(env, typeMetadataName); } @@ -94,6 +117,7 @@ bool MetadataNode::isArray() { } napi_value MetadataNode::CreateJSWrapper(napi_env env, ObjectManager *objectManager) { + napi_status status; napi_value obj; if (m_isArray) { @@ -101,7 +125,7 @@ napi_value MetadataNode::CreateJSWrapper(napi_env env, ObjectManager *objectMana } else { obj = objectManager->GetEmptyObject(); napi_value ctorFunc = GetConstructorFunction(env); - napi_set_named_property(env, obj, CONSTRUCTOR, ctorFunc); + NAPI_GUARD(napi_set_named_property(env, obj, CONSTRUCTOR, ctorFunc)) {} napi_util::setPrototypeOf(env, obj, napi_util::get_prototype(env, ctorFunc)); SetInstanceMetadata(env, obj, this); } @@ -116,7 +140,9 @@ napi_value MetadataNode::ArrayGetterCallback(napi_env env, napi_callback_info in napi_value index = argv[0]; int32_t indexValue; - napi_get_value_int32(env, index, &indexValue); + NAPI_GUARD(napi_get_value_int32(env, index, &indexValue)) { + return nullptr; + } auto node = GetInstanceMetadata(env, jsThis); return CallbackHandlers::GetArrayElement(env, jsThis, indexValue, node->m_name); @@ -142,11 +168,19 @@ napi_value MetadataNode::ArrayGetAllValuesCallback(napi_env env, napi_callback_i auto node = GetInstanceMetadata(env, jsThis); auto length = CallbackHandlers::GetArrayLength(env, jsThis); napi_value arr; - napi_create_array(env, &arr); + NAPI_GUARD(napi_create_array(env, &arr)) { + return nullptr; + } + + // Resolve the manager + backing array once for the whole loop. + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + JniLocalRef javaArr = objectManager->GetJavaObjectByJsObjectFast(jsThis); + jobject javaArrObj = javaArr; for (int i = 0; i < length; i++) { - napi_value element = CallbackHandlers::GetArrayElement(env, jsThis, i, node->m_name); - napi_set_element(env, arr, i, element); + napi_value element = CallbackHandlers::GetArrayElement(env, jsThis, i, node->m_name, + objectManager, javaArrObj); + NAPI_GUARD(napi_set_element(env, arr, i, element)) {} } return arr; @@ -175,7 +209,9 @@ napi_value MetadataNode::ArraySetterCallback(napi_env env, napi_callback_info in napi_value value = argv[1]; int32_t indexValue; - napi_get_value_int32(env, index, &indexValue); + NAPI_GUARD(napi_get_value_int32(env, index, &indexValue)) { + return nullptr; + } auto node = GetInstanceMetadata(env, jsThis); CallbackHandlers::SetArrayElement(env, jsThis, indexValue, node->m_name, value); @@ -202,7 +238,9 @@ napi_value MetadataNode::ArrayLengthCallback(napi_env env, napi_callback_info in int length = CallbackHandlers::GetArrayLength(env, jsThis); napi_value len; - napi_create_int32(env, length, &len); + NAPI_GUARD(napi_create_int32(env, length, &len)) { + return nullptr; + } return len; } catch (NativeScriptException &e) { e.ReThrowToNapi(env); @@ -219,22 +257,231 @@ napi_value MetadataNode::ArrayLengthCallback(napi_env env, napi_callback_info in return nullptr; } +napi_value MetadataNode::ArrayMapCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + try { + napi_value callback = argv[0]; + auto node = GetInstanceMetadata(env, jsThis); + int length = CallbackHandlers::GetArrayLength(env, jsThis); + + napi_value result; + NAPI_GUARD(napi_create_array_with_length(env, length, &result)) { + return nullptr; + } + + napi_value undefined; + NAPI_GUARD(napi_get_undefined(env, &undefined)) { + return nullptr; + } + + // Resolve the manager + backing array once for the whole loop. + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + JniLocalRef javaArr = objectManager->GetJavaObjectByJsObjectFast(jsThis); + jobject javaArrObj = javaArr; + + for (int i = 0; i < length; i++) { + napi_value element = + CallbackHandlers::GetArrayElement(env, jsThis, i, node->m_name, + objectManager, javaArrObj); + napi_value index; + NAPI_GUARD(napi_create_int32(env, i, &index)) { + return nullptr; + } + napi_value cbArgs[3] = {element, index, jsThis}; + napi_value mapped; + NAPI_GUARD(napi_call_function(env, undefined, callback, 3, cbArgs, &mapped)) { + return nullptr; + } + NAPI_GUARD(napi_set_element(env, result, i, mapped)) {} + } + + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::ArrayForEachCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + try { + napi_value callback = argv[0]; + auto node = GetInstanceMetadata(env, jsThis); + int length = CallbackHandlers::GetArrayLength(env, jsThis); + + napi_value undefined; + NAPI_GUARD(napi_get_undefined(env, &undefined)) { + return nullptr; + } + + // Resolve the manager + backing array once for the whole loop. + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + JniLocalRef javaArr = objectManager->GetJavaObjectByJsObjectFast(jsThis); + jobject javaArrObj = javaArr; + + for (int i = 0; i < length; i++) { + napi_value element = + CallbackHandlers::GetArrayElement(env, jsThis, i, node->m_name, + objectManager, javaArrObj); + napi_value index; + NAPI_GUARD(napi_create_int32(env, i, &index)) { + return nullptr; + } + napi_value cbArgs[3] = {element, index, jsThis}; + napi_value ignored; + NAPI_GUARD(napi_call_function(env, undefined, callback, 3, cbArgs, &ignored)) { + return nullptr; + } + } + + return undefined; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +// Builds a real JS array snapshot of all elements (native get loop). +static napi_value BuildArraySnapshot(napi_env env, napi_value jsThis, + const std::string &signature) { + napi_status status; + int length = CallbackHandlers::GetArrayLength(env, jsThis); + napi_value values; + NAPI_GUARD(napi_create_array_with_length(env, length, &values)) { + return nullptr; + } + + // Resolve the manager + backing array once for the whole loop. + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + JniLocalRef javaArr = objectManager->GetJavaObjectByJsObjectFast(jsThis); + jobject javaArrObj = javaArr; + + for (int i = 0; i < length; i++) { + napi_value element = + CallbackHandlers::GetArrayElement(env, jsThis, i, signature, + objectManager, javaArrObj); + NAPI_GUARD(napi_set_element(env, values, i, element)) {} + } + return values; +} + +napi_value MetadataNode::ArrayToStringCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + + try { + auto node = GetInstanceMetadata(env, jsThis); + napi_value values = BuildArraySnapshot(env, jsThis, node->m_name); + + // values.join(",") + napi_value joinFn; + NAPI_GUARD(napi_get_named_property(env, values, "join", &joinFn)) { + return nullptr; + } + napi_value comma; + NAPI_GUARD(napi_create_string_utf8(env, ",", 1, &comma)) { + return nullptr; + } + napi_value result; + NAPI_GUARD(napi_call_function(env, values, joinFn, 1, &comma, &result)) { + return nullptr; + } + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value +MetadataNode::ArraySymbolIteratorCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + + try { + auto node = GetInstanceMetadata(env, jsThis); + napi_value values = BuildArraySnapshot(env, jsThis, node->m_name); + + // return values[Symbol.iterator]() -> delegate to the real array iterator + napi_value globalObj, symbolCtor, symbolIterator, iterMethod, iterator; + NAPI_GUARD(napi_get_global(env, &globalObj)) { + return nullptr; + } + NAPI_GUARD(napi_get_named_property(env, globalObj, "Symbol", &symbolCtor)) { + return nullptr; + } + NAPI_GUARD(napi_get_named_property(env, symbolCtor, "iterator", &symbolIterator)) { + return nullptr; + } + NAPI_GUARD(napi_get_property(env, values, symbolIterator, &iterMethod)) { + return nullptr; + } + NAPI_GUARD(napi_call_function(env, values, iterMethod, 0, nullptr, &iterator)) { + return nullptr; + } + return iterator; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + napi_value MetadataNode::CreateArrayWrapper(napi_env env) { + napi_status status; napi_value constructor = CreateArrayObjectConstructor(env); napi_value instance; - napi_new_instance(env, constructor, 0, nullptr, &instance); + NAPI_GUARD(napi_new_instance(env, constructor, 0, nullptr, &instance)) { + return nullptr; + } SetInstanceMetadata(env, instance, this); return instance; } napi_value MetadataNode::GetImplementationObject(napi_env env, napi_value object) { + napi_status status; auto target = object; napi_value currentPrototype = target; napi_value implementationObject; - napi_get_named_property(env, currentPrototype, CLASS_IMPLEMENTATION_OBJECT, - &implementationObject); + NAPI_GUARD(napi_get_named_property(env, currentPrototype, CLASS_IMPLEMENTATION_OBJECT, + &implementationObject)) {} if (implementationObject != nullptr && !napi_util::is_undefined(env, implementationObject)) { return implementationObject; @@ -243,15 +490,15 @@ napi_value MetadataNode::GetImplementationObject(napi_env env, napi_value object bool hasProperty; napi_value prototypeImplObjectKey; - napi_create_string_utf8(env, PROP_KEY_IS_PROTOTYPE_IMPLEMENTATION_OBJECT, NAPI_AUTO_LENGTH, - &prototypeImplObjectKey); - napi_has_own_property(env, object, prototypeImplObjectKey, &hasProperty); + NAPI_GUARD(napi_create_string_utf8(env, PROP_KEY_IS_PROTOTYPE_IMPLEMENTATION_OBJECT, NAPI_AUTO_LENGTH, + &prototypeImplObjectKey)) {} + NAPI_GUARD(napi_has_own_property(env, object, prototypeImplObjectKey, &hasProperty)) {} if (hasProperty) { bool maybeHasOwnProperty; napi_value prototypeKey; - napi_create_string_utf8(env, PROTOTYPE, NAPI_AUTO_LENGTH, &prototypeKey); - napi_has_own_property(env, object, prototypeKey, &maybeHasOwnProperty); + NAPI_GUARD(napi_create_string_utf8(env, PROTOTYPE, NAPI_AUTO_LENGTH, &prototypeKey)) {} + NAPI_GUARD(napi_has_own_property(env, object, prototypeKey, &maybeHasOwnProperty)) {} if (!maybeHasOwnProperty) { return nullptr; @@ -261,8 +508,8 @@ napi_value MetadataNode::GetImplementationObject(napi_env env, napi_value object } napi_value activityImplementationObject; - napi_get_named_property(env, object, "t::ActivityImplementationObject", - &activityImplementationObject); + NAPI_GUARD(napi_get_named_property(env, object, "t::ActivityImplementationObject", + &activityImplementationObject)) {} if (activityImplementationObject != nullptr && !napi_util::is_undefined(env, activityImplementationObject)) { @@ -293,8 +540,8 @@ napi_value MetadataNode::GetImplementationObject(napi_env env, napi_value object return nullptr; } else { napi_value implObject; - napi_get_named_property(env, currentPrototype, CLASS_IMPLEMENTATION_OBJECT, - &implObject); + NAPI_GUARD(napi_get_named_property(env, currentPrototype, CLASS_IMPLEMENTATION_OBJECT, + &implObject)) {} if (implObject != nullptr && !napi_util::is_undefined(env, implObject)) { foundImplementationObject = true; @@ -308,20 +555,36 @@ napi_value MetadataNode::GetImplementationObject(napi_env env, napi_value object } void MetadataNode::SetInstanceMetadata(napi_env env, napi_value object, MetadataNode *node) { - auto cache = GetMetadataNodeCache(env); +#ifdef USE_HOST_OBJECT + // node now lives on the per-instance JSInstanceInfo (set in + // ObjectManager::Link / GetOrCreateProxy); the "#instance_metadata" property + // is no longer used on the host path. + (void) env; + (void) object; + (void) node; +#else + napi_status status; napi_value external; - napi_create_external(env, node, [](napi_env env, void *d1, void *d2) {}, node, &external); - napi_set_named_property(env, object, "#instance_metadata", external); + NAPI_GUARD(napi_create_external(env, node, [](napi_env env, void *d1, void *d2) {}, node, &external)) { + return; + } + NAPI_GUARD(napi_set_named_property(env, object, "#instance_metadata", external)) {} +#endif // napi_wrap(env, object, node, nullptr, nullptr, nullptr); } napi_value MetadataNode::ExtendedClassConstructorCallback(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(8) try { napi_value newTarget; - napi_get_new_target(env, info, &newTarget); + // Throw (not return nullptr) so a JS exception is left pending; otherwise + // the constructor trampoline maps a null result to `undefined` and + // `new X()` silently yields undefined. + NAPI_GUARD(napi_get_new_target(env, info, &newTarget)) { + throw NativeScriptException("Failed to read new.target in constructor call."); + } if (napi_util::is_null_or_undefined(env, newTarget)) return nullptr; auto extData = reinterpret_cast(data); @@ -333,11 +596,12 @@ napi_value MetadataNode::ExtendedClassConstructorCallback(napi_env env, napi_cal string fullClassName = extData->fullClassName; - ArgsWrapper argWrapper(argv.data(), argc, ArgType::Class); + ArgsWrapper argWrapper(argv, argc, ArgType::Class); napi_value jsThisProxy; bool success = CallbackHandlers::RegisterInstance(env, jsThis, fullClassName, argWrapper, implementationObject, false, - &jsThisProxy, extData->node->m_name); + &jsThisProxy, extData->node->m_name, + extData->node); return jsThisProxy; @@ -357,17 +621,23 @@ napi_value MetadataNode::ExtendedClassConstructorCallback(napi_env env, napi_cal } napi_value MetadataNode::InterfaceConstructorCallback(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(8) try { napi_valuetype arg1Type; napi_valuetype arg2Type; - napi_typeof(env, argv[0], &arg1Type); + // Throw so an exception is left pending (a null result would otherwise + // surface as `undefined` from `new`). + NAPI_GUARD(napi_typeof(env, argv[0], &arg1Type)) { + throw NativeScriptException("Failed to read constructor argument type."); + } if (argc == 2) { - napi_typeof(env, argv[1], &arg2Type); + NAPI_GUARD(napi_typeof(env, argv[1], &arg2Type)) { + throw NativeScriptException("Failed to read constructor argument type."); + } } napi_value implementationObject; @@ -414,13 +684,14 @@ napi_value MetadataNode::InterfaceConstructorCallback(napi_env env, napi_callbac napi_util::setPrototypeOf(env, jsThis, implementationObject); - napi_set_named_property(env, jsThis, CLASS_IMPLEMENTATION_OBJECT, implementationObject); + NAPI_GUARD(napi_set_named_property(env, jsThis, CLASS_IMPLEMENTATION_OBJECT, implementationObject)) {} - ArgsWrapper argsWrapper(argv.data(), argc, ArgType::Interface); + ArgsWrapper argsWrapper(argv, argc, ArgType::Interface); napi_value jsThisProxy; auto success = CallbackHandlers::RegisterInstance(env, jsThis, className, argsWrapper, - implementationObject, true, &jsThisProxy); + implementationObject, true, &jsThisProxy, + std::string(), node); return jsThisProxy; } catch (NativeScriptException &e) { @@ -439,7 +710,7 @@ napi_value MetadataNode::InterfaceConstructorCallback(napi_env env, napi_callbac } napi_value MetadataNode::ClassConstructorCallback(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(8) try { @@ -447,15 +718,16 @@ napi_value MetadataNode::ClassConstructorCallback(napi_env env, napi_callback_in SetInstanceMetadata(env, jsThis, node); - string extendName; - auto className = node->m_name; + // Plain construction has no extend name, so the full class name equals the + // base class name; skip CreateFullClassName (a string copy) and use the + // node's name directly for both. + const string &className = node->m_name; - string fullClassName = CreateFullClassName(className, extendName); - - ArgsWrapper argsWrapper(argv.data(), argc, ArgType::Class); + ArgsWrapper argsWrapper(argv, argc, ArgType::Class); napi_value jsThisProxy; - bool success = CallbackHandlers::RegisterInstance(env, jsThis, fullClassName, argsWrapper, - nullptr, false, &jsThisProxy, className); + bool success = CallbackHandlers::RegisterInstance(env, jsThis, className, argsWrapper, + nullptr, false, &jsThisProxy, className, + node); return jsThisProxy; } catch (NativeScriptException &e) { @@ -684,9 +956,12 @@ MetadataNode::MetadataNode(MetadataTreeNode *treeNode) : m_treeNode(treeNode) { } void MetadataNode::CreateTopLevelNamespaces(napi_env env) { + napi_status status; napi_value global; - napi_get_global(env, &global); + NAPI_GUARD(napi_get_global(env, &global)) { + return; + } auto root = s_metadataReader.GetRoot(); @@ -705,7 +980,7 @@ void MetadataNode::CreateTopLevelNamespaces(napi_env env) { if (IsJavascriptKeyword(nameSpace)) { nameSpace = "$" + nameSpace; } - napi_set_named_property(env, global, nameSpace.c_str(), packageObj); + NAPI_GUARD(napi_set_named_property(env, global, nameSpace.c_str(), packageObj)) {} } } } @@ -853,23 +1128,13 @@ napi_value MetadataNode::PackageGetterCallback(napi_env env, napi_callback_info NAPI_CALLBACK_BEGIN(0) try { auto childTreeNode = static_cast(data); - auto node = GetOrCreateInternal(childTreeNode->parent); DEBUG_WRITE("Get package item: %s", childTreeNode->name.c_str()); - auto hiddenPropName = "__" + childTreeNode->name; - napi_value hiddenValue; - napi_get_named_property(env, jsThis, hiddenPropName.c_str(), &hiddenValue); - - if (!napi_util::is_null_or_undefined(env, hiddenValue)) { - DEBUG_WRITE("Get cached package item: %s", hiddenPropName.c_str()); - return hiddenValue; - } auto childNode = MetadataNode::GetOrCreateInternal(childTreeNode); - hiddenValue = childNode->CreateWrapper(env); + napi_value value = childNode->CreateWrapper(env); uint8_t childNodeType = s_metadataReader.GetNodeType(childTreeNode); - bool isInterface = s_metadataReader.IsNodeTypeInterface(childNodeType); - if (isInterface) { + if (s_metadataReader.IsNodeTypeInterface(childNodeType)) { // For all java interfaces we register the special Symbol.hasInstance property // which is invoked by the instanceof operator (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance). // For example: @@ -879,16 +1144,27 @@ napi_value MetadataNode::PackageGetterCallback(napi_env env, napi_callback_info // return true; // } // }); - RegisterSymbolHasInstanceCallback(env, childTreeNode, hiddenValue); + RegisterSymbolHasInstanceCallback(env, childTreeNode, value); } - auto parentNode = GetOrCreateInternal(childTreeNode->parent); - if (parentNode->m_name == "org/json" && childTreeNode->name == "JSONObject") { - JSONObjectHelper::RegisterFromFunction(env, hiddenValue); + + // org.json.JSONObject special-case. Cheap name check first so the parent + // lookup only happens for the one class that needs it. + if (childTreeNode->name == "JSONObject") { + auto parentNode = GetOrCreateInternal(childTreeNode->parent); + if (parentNode->m_name == "org/json") { + JSONObjectHelper::RegisterFromFunction(env, value); + } } - napi_set_named_property(env, jsThis, hiddenPropName.c_str(), hiddenValue); + // Replace this accessor on the receiver with the resolved value as a plain + // (configurable) data property, so every subsequent `pkg.Child` access is a + // direct, inline-cacheable property load instead of re-invoking this getter. + napi_property_descriptor dataProp = { + childTreeNode->name.c_str(), nullptr, nullptr, nullptr, nullptr, + value, napi_default_jsproperty, nullptr}; + NAPI_GUARD(napi_define_properties(env, jsThis, 1, &dataProp)) {} - return hiddenValue; + return value; } catch (NativeScriptException &e) { e.ReThrowToNapi(env); @@ -901,6 +1177,7 @@ napi_value MetadataNode::PackageGetterCallback(napi_env env, napi_callback_info NativeScriptException nsEx(std::string("Error: c++ exception!")); nsEx.ReThrowToNapi(env); } + return nullptr; } void MetadataNode::RegisterSymbolHasInstanceCallback(napi_env env, const MetadataTreeNode *treeNode, @@ -917,16 +1194,34 @@ void MetadataNode::RegisterSymbolHasInstanceCallback(napi_env env, const Metadat return; } + napi_status status; napi_value hasInstance; napi_value symbol; napi_value global; - napi_get_global(env, &global); - napi_get_named_property(env, global, "Symbol", &symbol); - napi_get_named_property(env, symbol, "hasInstance", &hasInstance); + NAPI_GUARD(napi_get_global(env, &global)) { + return; + } + NAPI_GUARD(napi_get_named_property(env, global, "Symbol", &symbol)) { + return; + } + NAPI_GUARD(napi_get_named_property(env, symbol, "hasInstance", &hasInstance)) { + return; + } + // NOTE: the napi `data` pointer must be a heap-allocated pointer, NOT a raw + // JNI reference. PrimJS's napi boxes the callback data into 48 bits and + // reconstructs it with a fixed top-16-bit heap tag (0xb400...) on retrieval; + // that is lossless for real heap pointers but corrupts a JNI global ref + // (top bits 0x0000), yielding a bogus jclass and a CheckJNI "invalid jobject" + // abort. Wrap the class ref in a heap holder so `data` is always a heap + // pointer (engine-neutral; matches the MethodCallbackData pattern). + auto *holder = new SymbolHasInstanceData{clazz}; napi_value method; - napi_create_function(env, "hasInstance", NAPI_AUTO_LENGTH, SymbolHasInstanceCallback, clazz, - &method); - + NAPI_GUARD(napi_create_function(env, "hasInstance", NAPI_AUTO_LENGTH, SymbolHasInstanceCallback, holder, + &method)) { + delete holder; + return; + } + napi_property_descriptor desc = { nullptr, // utf8name hasInstance, // name @@ -937,11 +1232,11 @@ void MetadataNode::RegisterSymbolHasInstanceCallback(napi_env env, const Metadat napi_default, // attributes nullptr // data }; - napi_define_properties(env, interface, 1, &desc); + NAPI_GUARD(napi_define_properties(env, interface, 1, &desc)) {} } napi_value MetadataNode::SymbolHasInstanceCallback(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS(); + NAPI_CALLBACK_BEGIN_VARGS_FAST(2); if (argc != 1) { throw NativeScriptException(string("Symbol.hasInstance must take exactly 1 argument")); return nullptr; @@ -953,7 +1248,7 @@ napi_value MetadataNode::SymbolHasInstanceCallback(napi_env env, napi_callback_i return napi_util::get_false(env); } - auto clazz = reinterpret_cast(data); + auto clazz = reinterpret_cast(data)->clazz; auto runtime = Runtime::GetRuntime(env); auto objectManager = runtime->GetObjectManager(); @@ -969,7 +1264,9 @@ napi_value MetadataNode::SymbolHasInstanceCallback(napi_env env, napi_callback_i auto isInstanceOf = jEnv.IsInstanceOf(obj, clazz); napi_value result; - napi_get_boolean(env, isInstanceOf, &result); + NAPI_GUARD(napi_get_boolean(env, isInstanceOf, &result)) { + return nullptr; + } return result; @@ -995,8 +1292,11 @@ std::string MetadataNode::GetJniClassName(const MetadataTreeNode *node) { } napi_value MetadataNode::CreatePackageObject(napi_env env) { + napi_status status; napi_value packageObj; - napi_create_object(env, &packageObj); + NAPI_GUARD(napi_create_object(env, &packageObj)) { + return nullptr; + } auto ptrChildren = this->m_treeNode->children; @@ -1017,7 +1317,7 @@ napi_value MetadataNode::CreatePackageObject(napi_env env) { nullptr, napi_default_jsproperty, childNode}; - napi_define_properties(env, packageObj, 1, &descriptor); + NAPI_GUARD(napi_define_properties(env, packageObj, 1, &descriptor)) {} } } @@ -1047,6 +1347,7 @@ std::vector MetadataNode::SetClassMembersFro const std::vector &baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode) { + napi_status status; std::vector instanceMethodData; uint8_t *curPtr = s_metadataReader.GetValueData() + treeNode->offsetValue + 1; @@ -1066,6 +1367,14 @@ std::vector MetadataNode::SetClassMembersFro napi_value prototype = napi_util::get_prototype(env, constructor); + // Strong reference to the prototype shared by every instance field/property + // accessor below. When host objects are disabled the accessors use it to + // identity-compare the receiver and short-circuit Class.prototype. + // access. The prototype lives for the class' lifetime, so this never frees. + napi_ref prototypeRef = nullptr; + NAPI_GUARD(napi_create_reference(env, prototype, 1, &prototypeRef)) {} + + auto objectManager = Runtime::GetObjectManager(env); auto extensionFunctionsCount = *reinterpret_cast(curPtr); curPtr += sizeof(uint16_t); collectedExtensionMethods.reserve(extensionFunctionsCount); @@ -1082,8 +1391,8 @@ std::vector MetadataNode::SetClassMembersFro callbackData = new MethodCallbackData(this); napi_value method; - napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, - callbackData, &method); + NAPI_GUARD(napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, + callbackData, &method)) {} napi_util::define_property_value(env, prototype, methodName.c_str(), method, napi_default_method); lastMethodName = methodName; @@ -1093,6 +1402,7 @@ std::vector MetadataNode::SetClassMembersFro } callbackData->candidates.push_back(std::move(entry)); + callbackData->objectManager = objectManager; } auto instanceMethodCount = *reinterpret_cast(curPtr); @@ -1110,8 +1420,8 @@ std::vector MetadataNode::SetClassMembersFro if (callbackData == nullptr) { callbackData = new MethodCallbackData(this); napi_value method; - napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, - callbackData, &method); + NAPI_GUARD(napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, + callbackData, &method)) {} napi_util::define_property_value(env, prototype, methodName.c_str(), method, napi_default_method); collectedExtensionMethods.emplace(methodName, callbackData); } @@ -1132,8 +1442,8 @@ std::vector MetadataNode::SetClassMembersFro } callbackData->candidates.push_back(std::move(entry)); + callbackData->objectManager = objectManager; } - auto instanceFieldCount = *reinterpret_cast(curPtr); curPtr += sizeof(uint16_t); for (auto i = 0; i < instanceFieldCount; i++) { @@ -1141,6 +1451,8 @@ std::vector MetadataNode::SetClassMembersFro auto &fieldName = entry.getName(); auto fieldInfo = new FieldCallbackData(entry); fieldInfo->metadata.declaringType = curType; + fieldInfo->prototype = prototypeRef; + fieldInfo->objectManager = objectManager; napi_util::define_property(env, prototype, fieldName.c_str(), nullptr, FieldAccessorGetterCallback, FieldAccessorSetterCallback, fieldInfo); @@ -1159,23 +1471,32 @@ std::vector MetadataNode::SetClassMembersFro auto hasGetter = *reinterpret_cast(curPtr); curPtr += sizeof(uint16_t); + // Keep the full method entry (not just its name) so the accessor can call + // CallJavaMethod directly instead of looking up + invoking the JS method. + MetadataEntry *getterEntry = nullptr; std::string getterMethodName; if (hasGetter >= 1) { - auto entry = MetadataReader::ReadInstanceMethodEntry(&curPtr); - getterMethodName = entry.getName(); + getterEntry = new MetadataEntry(MetadataReader::ReadInstanceMethodEntry(&curPtr)); + getterMethodName = getterEntry->getName(); } auto hasSetter = *reinterpret_cast(curPtr); curPtr += sizeof(uint16_t); + MetadataEntry *setterEntry = nullptr; std::string setterMethodName; if (hasSetter >= 1) { - auto entry = MetadataReader::ReadInstanceMethodEntry(&curPtr); - setterMethodName = entry.getName(); + setterEntry = new MetadataEntry(MetadataReader::ReadInstanceMethodEntry(&curPtr)); + setterMethodName = setterEntry->getName(); } auto propertyInfo = new PropertyCallbackData(propertyName, getterMethodName, setterMethodName); + propertyInfo->prototype = prototypeRef; + propertyInfo->getterEntry = getterEntry; + propertyInfo->setterEntry = setterEntry; + propertyInfo->node = this; + propertyInfo->objectManager = objectManager; napi_util::define_property(env, prototype, propertyName.c_str(), nullptr, PropertyAccessorGetterCallback, PropertyAccessorSetterCallback, propertyInfo); @@ -1197,19 +1518,30 @@ std::vector MetadataNode::SetClassMembersFro if (methodName != lastMethodName) { callbackData = new MethodCallbackData(this); napi_value method; - napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, - callbackData, &method); + NAPI_GUARD(napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, + callbackData, &method)) {} napi_util::define_property_value(env, constructor, methodName.c_str(), method, napi_default_method); lastMethodName = methodName; } callbackData->candidates.push_back(std::move(entry)); + callbackData->objectManager = objectManager; } napi_value extendMethod; - napi_create_function(env, PROP_KEY_EXTEND, sizeof(PROP_KEY_EXTEND), ExtendMethodCallback, this, - &extendMethod); - napi_set_named_property(env, constructor, PROP_KEY_EXTEND, extendMethod); + NAPI_GUARD(napi_create_function(env, PROP_KEY_EXTEND, sizeof(PROP_KEY_EXTEND), ExtendMethodCallback, this, + &extendMethod)) {} + NAPI_GUARD(napi_set_named_property(env, constructor, PROP_KEY_EXTEND, extendMethod)) {} + + // Brand the runtime's native extend() so ts_helpers can reliably tell a native class's + // extend from a user/JS extend. It must NOT rely on Function.prototype.toString() sniffing + // "[native code]": in release builds JS is compiled to bytecode and every function + // (native or JS) stringifies to "[native code]", so a plain JS class with a static method + // named "extend" would be misdetected as native. This brand is a real, non-enumerable + // property set by the runtime, so it works identically for source and bytecode on all engines. + napi_value nativeExtendBrand; + NAPI_GUARD(napi_get_boolean(env, true, &nativeExtendBrand)) {} + NAPI_GUARD(napi_util::define_property_value(env, extendMethod, "__isNativeExtend__", nativeExtendBrand, napi_default)) {} // get candidates from static fields metadata auto staticFieldCout = *reinterpret_cast(curPtr); @@ -1223,6 +1555,7 @@ std::vector MetadataNode::SetClassMembersFro FieldAccessorGetterCallback, FieldAccessorSetterCallback, fieldInfo); MetadataNode::GetMetadataNodeCache(env)->fieldCallbackData.push_back(fieldInfo); + fieldInfo->objectManager = objectManager; } @@ -1231,8 +1564,8 @@ std::vector MetadataNode::SetClassMembersFro std::string tname = s_metadataReader.ReadTypeName(treeNode); - napi_set_named_property(env, constructor, PRIVATE_TYPE_NAME, - ArgConverter::convertToJsString(env, tname)); + NAPI_GUARD(napi_set_named_property(env, constructor, PRIVATE_TYPE_NAME, + ArgConverter::convertToJsString(env, tname))) {} SetClassAccessor(env, constructor); @@ -1267,6 +1600,7 @@ std::vector MetadataNode::SetInstanceMembers MetadataTreeNode *treeNode) { assert(treeNode->metadata != nullptr); + napi_status status; std::vector instanceMethodData; std::string line; @@ -1316,9 +1650,9 @@ std::vector MetadataNode::SetInstanceMembers } napi_value method; - napi_create_function(env, entry.name.c_str(), NAPI_AUTO_LENGTH, MethodCallback, - callbackData, &method); - napi_set_named_property(env, proto, entry.name.c_str(), method); + NAPI_GUARD(napi_create_function(env, entry.name.c_str(), NAPI_AUTO_LENGTH, MethodCallback, + callbackData, &method)) {} + NAPI_GUARD(napi_set_named_property(env, proto, entry.name.c_str(), method)) {} lastMethodName = entry.name; } @@ -1346,7 +1680,9 @@ napi_value MetadataNode::ClassAccessorGetterCallback(napi_env env, napi_callback NAPI_CALLBACK_BEGIN(0); try { napi_value name; - napi_get_named_property(env, jsThis, PRIVATE_TYPE_NAME, &name); + NAPI_GUARD(napi_get_named_property(env, jsThis, PRIVATE_TYPE_NAME, &name)) { + return nullptr; + } const char *nameValue = napi_util::get_string_value(env, name); return CallbackHandlers::FindClass(env, nameValue); } catch (NativeScriptException &e) { @@ -1372,6 +1708,7 @@ napi_value MetadataNode::GetConstructorFunction(napi_env env) { napi_value MetadataNode::GetConstructorFunctionInternal(napi_env env, MetadataTreeNode *treeNode, std::vector instanceMethodsCallbackData) { + napi_status status; auto cache = GetMetadataNodeCache(env); auto itFound = cache->CtorFuncCache.find(treeNode); if (itFound != cache->CtorFuncCache.end()) { @@ -1392,7 +1729,7 @@ napi_value MetadataNode::GetConstructorFunctionInternal(napi_env env, MetadataTr #endif itFound->second.instanceMethodCallbacks.clear(); if (itFound->second.constructorFunction != nullptr) { - napi_delete_reference(env, itFound->second.constructorFunction); + NAPI_GUARD(napi_delete_reference(env, itFound->second.constructorFunction)) {} } cache->CtorFuncCache.erase(itFound); } @@ -1427,9 +1764,11 @@ napi_value MetadataNode::GetConstructorFunctionInternal(napi_env env, MetadataTr napi_value constructor; auto isInterface = s_metadataReader.IsNodeTypeInterface(treeNode->type); - napi_define_class(env, finalName.c_str(), NAPI_AUTO_LENGTH, + NAPI_GUARD(napi_define_class(env, finalName.c_str(), NAPI_AUTO_LENGTH, isInterface ? InterfaceConstructorCallback : ClassConstructorCallback, - node, 0, nullptr, &constructor); + node, 0, nullptr, &constructor)) { + return nullptr; + } // Mark this constructor's prototype as a runtime object. ObjectManager::MarkObject(env, napi_util::get_prototype(env, constructor)); @@ -1493,11 +1832,12 @@ void MetadataNode::SetInnerTypes(napi_env env, napi_value constructor, MetadataT const auto &children = *treeNode->children; std::vector childNames(children.size()); + napi_status status; for (auto curChild: children) { bool hasOwnProperty = false; napi_value childName; - napi_create_string_utf8(env, curChild->name.c_str(), curChild->name.size(), &childName); - napi_has_own_property(env, constructor, childName, &hasOwnProperty); + NAPI_GUARD(napi_create_string_utf8(env, curChild->name.c_str(), curChild->name.size(), &childName)) {} + NAPI_GUARD(napi_has_own_property(env, constructor, childName, &hasOwnProperty)) {} if (!hasOwnProperty) { napi_util::define_property(env, constructor, curChild->name.c_str(), nullptr, InnerTypeGetterCallback, nullptr, curChild); @@ -1511,13 +1851,26 @@ napi_value MetadataNode::InnerTypeGetterCallback(napi_env env, napi_callback_inf try { auto curChild = reinterpret_cast(data); auto childNode = GetOrCreateInternal(curChild); - auto cache = GetMetadataNodeCache(env); - auto itFound = cache->CtorFuncCache.find(childNode->m_treeNode); - if (itFound != cache->CtorFuncCache.end()) { - auto value = napi_util::get_ref_value(env, itFound->second.constructorFunction); - if (!napi_util::is_null_or_undefined(env, value)) return value; - } + // GetConstructorFunction caches per node (CtorFuncCache); inner types are + // always class/interface, both resolved here. napi_value constructor = childNode->GetConstructorFunction(env); + + // Java interfaces need Symbol.hasInstance for `instanceof` support, just + // like package-level interfaces in PackageGetterCallback. + uint8_t childNodeType = s_metadataReader.GetNodeType(curChild); + if (s_metadataReader.IsNodeTypeInterface(childNodeType)) { + RegisterSymbolHasInstanceCallback(env, curChild, constructor); + } + + // Replace this accessor on the receiver (the outer type) with the resolved + // inner class/interface as a plain (configurable) data property, so every + // subsequent Outer.Inner access is a direct, inline-cacheable property load + // instead of re-invoking this getter. + napi_property_descriptor dataProp = { + curChild->name.c_str(), nullptr, nullptr, nullptr, nullptr, + constructor, napi_default_jsproperty, nullptr}; + NAPI_GUARD(napi_define_properties(env, jsThis, 1, &dataProp)) {} + return constructor; } catch (NativeScriptException &e) { @@ -1545,15 +1898,21 @@ napi_value MetadataNode::NullObjectAccessorGetterCallback(napi_env env, napi_cal bool value; napi_value nullNodeKey; - napi_create_string_utf8(env, PROP_KEY_NULL_NODE_NAME, NAPI_AUTO_LENGTH, &nullNodeKey); - napi_has_own_property(env, jsThis, nullNodeKey, &value); + NAPI_GUARD(napi_create_string_utf8(env, PROP_KEY_NULL_NODE_NAME, NAPI_AUTO_LENGTH, &nullNodeKey)) { + return nullptr; + } + NAPI_GUARD(napi_has_own_property(env, jsThis, nullNodeKey, &value)) { + return nullptr; + } if (!value) { auto node = reinterpret_cast(data); napi_value external; - napi_create_external(env, node, [](napi_env env, void *d1, void *d2) {}, node, - &external); - napi_set_named_property(env, jsThis, PROP_KEY_NULL_NODE_NAME, external); + NAPI_GUARD(napi_create_external(env, node, [](napi_env env, void *d1, void *d2) {}, node, + &external)) { + return nullptr; + } + NAPI_GUARD(napi_set_named_property(env, jsThis, PROP_KEY_NULL_NODE_NAME, external)) {} napi_util::napi_set_function(env, jsThis, @@ -1578,11 +1937,34 @@ napi_value MetadataNode::NullObjectAccessorGetterCallback(napi_env env, napi_cal } napi_value MetadataNode::NullValueOfCallback(napi_env env, napi_callback_info info) { + napi_status status; napi_value nullValue; - napi_get_null(env, &nullValue); + NAPI_GUARD(napi_get_null(env, &nullValue)) { + return nullptr; + } return nullValue; } +bool MetadataNode::IsInstanceReceiver(napi_env env, napi_value jsThis, napi_ref prototypeRef) { +#ifdef USE_HOST_OBJECT + // Real instances are host-object proxies; the class prototype is not. A + // non-host receiver means someone touched Class.prototype.. + (void) prototypeRef; + napi_status status; + bool isHostObject = false; + NAPI_GUARD(napi_is_host_object(env, jsThis, &isHostObject)) {} + return isHostObject; +#else + // Fallback: identity-compare the receiver against the cached prototype. + if (prototypeRef == nullptr) return true; + napi_value prototype = napi_util::get_ref_value(env, prototypeRef); + napi_status status; + bool isHolder = false; + NAPI_GUARD(napi_strict_equals(env, jsThis, prototype, &isHolder)) {} + return !isHolder; +#endif +} + napi_value MetadataNode::FieldAccessorGetterCallback(napi_env env, napi_callback_info info) { NAPI_CALLBACK_BEGIN(0); try { @@ -1593,28 +1975,23 @@ napi_value MetadataNode::FieldAccessorGetterCallback(napi_env env, napi_callback return UNDEFINED; } - if (!fieldMetadata.isStatic) { - bool is__this__ = false; - napi_has_named_property(env, jsThis, "__napi::this", &is__this__); - if (!is__this__) { - napi_value constructor; - napi_value prototype; - napi_get_named_property(env, jsThis, "constructor", &constructor); - if (!napi_util::is_null_or_undefined(env, constructor)) { - napi_get_named_property(env, constructor, "prototype", &prototype); - bool isHolder; - napi_strict_equals(env, prototype, jsThis, &isHolder); - if (isHolder) { - return UNDEFINED; - } else { - napi_set_named_property(env, jsThis, "__napi::this", - napi_util::get_true(env)); - } - } - } + if (fieldData->objectManager == nullptr) { + fieldData->objectManager = Runtime::GetRuntime(env)->GetObjectManager(); } - return CallbackHandlers::GetJavaField(env, jsThis, fieldData); + if (fieldMetadata.isStatic) { + return CallbackHandlers::GetJavaField(env, jsThis, fieldData, + fieldData->objectManager); + } + + // A single probe both validates the receiver and resolves the java + // object; null + non-host means Class.prototype. access. + JniLocalRef target = fieldData->objectManager->GetJavaObjectByJsObjectFast(jsThis); + if (target.IsNull() && !IsInstanceReceiver(env, jsThis, fieldData->prototype)) { + return UNDEFINED; + } + return CallbackHandlers::GetJavaField(env, jsThis, fieldData, + fieldData->objectManager, std::move(target)); } catch (NativeScriptException &e) { e.ReThrowToNapi(env); @@ -1638,24 +2015,17 @@ napi_value MetadataNode::FieldAccessorSetterCallback(napi_env env, napi_callback auto fieldData = reinterpret_cast(data); auto &fieldMetadata = fieldData->metadata; + if (fieldData->objectManager == nullptr) { + fieldData->objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + } + + // A single probe both validates the receiver and resolves the java + // object; null + non-host means Class.prototype. access. + JniLocalRef target; if (!fieldMetadata.isStatic) { - bool is__this__ = false; - napi_has_named_property(env, jsThis, "__napi::this", &is__this__); - if (!is__this__) { - napi_value constructor; - napi_value prototype; - napi_get_named_property(env, jsThis, "constructor", &constructor); - if (!napi_util::is_null_or_undefined(env, constructor)) { - napi_get_named_property(env, constructor, "prototype", &prototype); - bool isHolder; - napi_strict_equals(env, prototype, jsThis, &isHolder); - if (isHolder) { - return UNDEFINED; - } else { - napi_set_named_property(env, jsThis, "__napi::this", - napi_util::get_true(env)); - } - } + target = fieldData->objectManager->GetJavaObjectByJsObjectFast(jsThis); + if (target.IsNull() && !IsInstanceReceiver(env, jsThis, fieldData->prototype)) { + return UNDEFINED; } } @@ -1667,7 +2037,8 @@ napi_value MetadataNode::FieldAccessorSetterCallback(napi_env env, napi_callback throw NativeScriptException(exceptionMessage); } else { - CallbackHandlers::SetJavaField(env, jsThis, argv[0], fieldData); + CallbackHandlers::SetJavaField(env, jsThis, argv[0], fieldData, + fieldData->objectManager, std::move(target)); return argv[0]; } @@ -1692,17 +2063,30 @@ napi_value MetadataNode::PropertyAccessorGetterCallback(napi_env env, napi_callb try { auto propertyCallbackData = reinterpret_cast(data); - if (propertyCallbackData->getterMethodName.empty()) { + if (propertyCallbackData->getterEntry == nullptr) { return nullptr; } - napi_value getter; - napi_get_named_property(env, jsThis, propertyCallbackData->getterMethodName.c_str(), - &getter); + if (!IsInstanceReceiver(env, jsThis, propertyCallbackData->prototype)) { + return nullptr; + } - napi_value result; - napi_call_function(env, jsThis, getter, 0, nullptr, &result); - return result; + // Call the Java getter directly — no JS method lookup, no nested + // MethodCallback. Invariants are resolved once and cached. + if (propertyCallbackData->cachedIsFromInterface < 0) { + propertyCallbackData->cachedIsFromInterface = + propertyCallbackData->node->IsNodeTypeInterface() ? 1 : 0; + } + if (propertyCallbackData->objectManager == nullptr) { + propertyCallbackData->objectManager = + Runtime::GetRuntime(env)->GetObjectManager(); + } + return CallbackHandlers::CallJavaMethod( + env, jsThis, propertyCallbackData->node->m_name, + propertyCallbackData->getterMethodName, propertyCallbackData->getterEntry, + propertyCallbackData->cachedIsFromInterface == 1, + propertyCallbackData->getterEntry->isStatic, info, 0, nullptr, + propertyCallbackData->objectManager); } catch (NativeScriptException &e) { e.ReThrowToNapi(env); @@ -1725,18 +2109,30 @@ napi_value MetadataNode::PropertyAccessorSetterCallback(napi_env env, napi_callb try { auto propertyCallbackData = reinterpret_cast(data); - if (propertyCallbackData->setterMethodName.empty()) { + if (propertyCallbackData->setterEntry == nullptr) { return nullptr; } - napi_value setter; - napi_get_named_property(env, jsThis, propertyCallbackData->setterMethodName.c_str(), - &setter); - - napi_value result; - napi_call_function(env, jsThis, setter, 1, &argv[0], &result); + if (!IsInstanceReceiver(env, jsThis, propertyCallbackData->prototype)) { + return nullptr; + } - return result; + // Call the Java setter directly — no JS method lookup, no nested + // MethodCallback. Invariants are resolved once and cached. + if (propertyCallbackData->cachedIsFromInterface < 0) { + propertyCallbackData->cachedIsFromInterface = + propertyCallbackData->node->IsNodeTypeInterface() ? 1 : 0; + } + if (propertyCallbackData->objectManager == nullptr) { + propertyCallbackData->objectManager = + Runtime::GetRuntime(env)->GetObjectManager(); + } + return CallbackHandlers::CallJavaMethod( + env, jsThis, propertyCallbackData->node->m_name, + propertyCallbackData->setterMethodName, propertyCallbackData->setterEntry, + propertyCallbackData->cachedIsFromInterface == 1, + propertyCallbackData->setterEntry->isStatic, info, 1, &argv[0], + propertyCallbackData->objectManager); } catch (NativeScriptException &e) { e.ReThrowToNapi(env); } catch (std::exception e) { @@ -1753,7 +2149,7 @@ napi_value MetadataNode::PropertyAccessorSetterCallback(napi_env env, napi_callb } napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(8) try { napi_value extendName; @@ -1786,7 +2182,7 @@ napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info i hasDot = strName.find('.') != string::npos; } else if (argc == 3) { if (napi_util::is_of_type(env, argv[2], napi_boolean)) { - napi_get_value_bool(env, argv[2], &isTypeScriptExtend); + NAPI_GUARD(napi_get_value_bool(env, argv[2], &isTypeScriptExtend)) {} }; } @@ -1797,8 +2193,10 @@ napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info i implementationObject = argv[1]; } else { bool validExtend = GetExtendLocation(env, extendLocation, isTypeScriptExtend); - napi_create_string_utf8(env, "", 0, &extendName); - auto validArgs = ValidateExtendArguments(env, argc, argv.data(), validExtend, + NAPI_GUARD(napi_create_string_utf8(env, "", 0, &extendName)) { + return nullptr; + } + auto validArgs = ValidateExtendArguments(env, argc, argv, validExtend, extendLocation, &extendName, &implementationObject, isTypeScriptExtend); @@ -1831,12 +2229,14 @@ napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info i } napi_value implementationObjectName; - napi_get_named_property(env, implementationObject, CLASS_IMPLEMENTATION_OBJECT, - &implementationObjectName); + NAPI_GUARD(napi_get_named_property(env, implementationObject, CLASS_IMPLEMENTATION_OBJECT, + &implementationObjectName)) { + return nullptr; + } if (napi_util::is_null_or_undefined(env, implementationObjectName)) { - napi_set_named_property(env, implementationObject, CLASS_IMPLEMENTATION_OBJECT, - ArgConverter::convertToJsString(env, fullExtendedName)); + NAPI_GUARD(napi_set_named_property(env, implementationObject, CLASS_IMPLEMENTATION_OBJECT, + ArgConverter::convertToJsString(env, fullExtendedName))) {} } else { string usedClassName = ArgConverter::ConvertToString(env, implementationObjectName); stringstream s; @@ -1847,13 +2247,15 @@ napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info i auto baseClassCtorFunction = node->GetConstructorFunction(env); napi_value extendFuncCtor; - napi_define_class(env, fullExtendedName.c_str(), NAPI_AUTO_LENGTH, + NAPI_GUARD(napi_define_class(env, fullExtendedName.c_str(), NAPI_AUTO_LENGTH, MetadataNode::ExtendedClassConstructorCallback, new ExtendedClassCallbackData(node, extendNameAndLocation, napi_util::make_ref(env, implementationObject), fullClassName), 0, nullptr, - &extendFuncCtor); + &extendFuncCtor)) { + return nullptr; + } napi_value extendFuncPrototype = napi_util::get_prototype(env, extendFuncCtor); ObjectManager::MarkObject(env, extendFuncPrototype); @@ -1870,8 +2272,8 @@ napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info i SetClassAccessor(env, extendFuncCtor); - napi_set_named_property(env, extendFuncCtor, PRIVATE_TYPE_NAME, - ArgConverter::convertToJsString(env, fullExtendedName)); + NAPI_GUARD(napi_set_named_property(env, extendFuncCtor, PRIVATE_TYPE_NAME, + ArgConverter::convertToJsString(env, fullExtendedName))) {} s_name2NodeCache.emplace(fullExtendedName, node); @@ -1904,16 +2306,18 @@ napi_value MetadataNode::SuperAccessorGetterCallback(napi_env env, napi_callback try { napi_value superValue; - napi_get_named_property(env, jsThis, PROP_KEY_SUPERVALUE, &superValue); + NAPI_GUARD(napi_get_named_property(env, jsThis, PROP_KEY_SUPERVALUE, &superValue)) { + return nullptr; + } if (napi_util::is_null_or_undefined(env, superValue)) { auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); superValue = objectManager->GetEmptyObject(); - napi_delete_property(env, superValue, - ArgConverter::convertToJsString(env, PROP_KEY_TOSTRING), nullptr); - napi_delete_property(env, superValue, - ArgConverter::convertToJsString(env, PROP_KEY_VALUEOF), nullptr); + NAPI_GUARD(napi_delete_property(env, superValue, + ArgConverter::convertToJsString(env, PROP_KEY_TOSTRING), nullptr)) {} + NAPI_GUARD(napi_delete_property(env, superValue, + ArgConverter::convertToJsString(env, PROP_KEY_VALUEOF), nullptr)) {} ObjectManager::MarkSuperCall(env, superValue); napi_value superProto = napi_util::getPrototypeOf(env, napi_util::getPrototypeOf(env, @@ -1931,7 +2335,7 @@ napi_value MetadataNode::SuperAccessorGetterCallback(napi_env env, napi_callback if (javaObjectID != -1) { superValue = objectManager->GetOrCreateProxyWeak(javaObjectID, superValue); } - napi_set_named_property(env, jsThis, PROP_KEY_SUPERVALUE, superValue); + NAPI_GUARD(napi_set_named_property(env, jsThis, PROP_KEY_SUPERVALUE, superValue)) {} } return superValue; @@ -1952,7 +2356,7 @@ napi_value MetadataNode::SuperAccessorGetterCallback(napi_env env, napi_callback } napi_value MetadataNode::MethodCallback(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(8) try { MetadataEntry *entry = nullptr; @@ -1964,6 +2368,16 @@ napi_value MetadataNode::MethodCallback(napi_env env, napi_callback_info info) { auto &first = callbackData->candidates.front(); auto &methodName = first.getName(); + // Fast path for the overwhelmingly common single-overload, non-extension + // method with no parent chain: skip the candidate-search loop entirely. + if (callbackData->parent == nullptr && + callbackData->candidates.size() == 1 && + !first.isExtensionFunction && + first.getParamCount() == argc) { + className = &callbackData->node->m_name; + entry = &first; + } + while ((callbackData != nullptr) && (entry == nullptr)) { auto &candidates = callbackData->candidates; @@ -1993,14 +2407,26 @@ napi_value MetadataNode::MethodCallback(napi_env env, napi_callback_info info) { } - if (argc == 0 && methodName == PROP_KEY_VALUEOF) { - return jsThis; + if (initialCallbackData->cachedIsValueOf < 0) { + initialCallbackData->cachedIsValueOf = + (methodName == PROP_KEY_VALUEOF) ? 1 : 0; + } + if (argc == 0 && initialCallbackData->cachedIsValueOf == 1) { + return jsThis; } else { // Runtime::GetRuntime(env)->clearPendingError(); - bool isFromInterface = initialCallbackData->node->IsNodeTypeInterface(); + if (initialCallbackData->cachedIsFromInterface < 0) { + initialCallbackData->cachedIsFromInterface = + initialCallbackData->node->IsNodeTypeInterface() ? 1 : 0; + } + bool isFromInterface = initialCallbackData->cachedIsFromInterface == 1; + if (initialCallbackData->objectManager == nullptr) { + initialCallbackData->objectManager = + Runtime::GetRuntime(env)->GetObjectManager(); + } napi_value result = CallbackHandlers::CallJavaMethod(env, jsThis, *className, methodName, entry, isFromInterface, first.isStatic, info, - argc, argv.data()); + argc, argv, initialCallbackData->objectManager); // napi_value error; // error = Runtime::GetRuntime(env)->getPendingError(); // if (error) { @@ -2053,6 +2479,7 @@ void MetadataNode::SetMissingBaseMethods( napi_env env, const std::vector &skippedBaseTypes, const std::vector &instanceMethodData, napi_value constructor) { + napi_status status; for (auto treeNode: skippedBaseTypes) { uint8_t *curPtr = s_metadataReader.GetValueData() + treeNode->offsetValue + 1; @@ -2088,9 +2515,9 @@ void MetadataNode::SetMissingBaseMethods( callbackData = new MethodCallbackData(this); napi_value proto = napi_util::get_prototype(env, constructor); napi_value method; - napi_create_function(env, methodName.c_str(), NAPI_AUTO_LENGTH, MethodCallback, - callbackData, &method); - napi_set_named_property(env, proto, methodName.c_str(), method); + NAPI_GUARD(napi_create_function(env, methodName.c_str(), NAPI_AUTO_LENGTH, MethodCallback, + callbackData, &method)) {} + NAPI_GUARD(napi_set_named_property(env, proto, methodName.c_str(), method)) {} } bool foundSameSig = false; @@ -2113,12 +2540,13 @@ void MetadataNode::BuildMetadata(const std::string &filesPath) { } void MetadataNode::onDisposeEnv(napi_env env) { + napi_status status; { auto it = s_metadata_node_cache.Get(env); if (it != nullptr) { for (const auto &entry: it->CtorFuncCache) { if (entry.second.constructorFunction == nullptr) { - napi_delete_reference(env, entry.second.constructorFunction); + NAPI_GUARD(napi_delete_reference(env, entry.second.constructorFunction)) {} } for (const auto data: entry.second.instanceMethodCallbacks) { delete data; @@ -2128,7 +2556,7 @@ void MetadataNode::onDisposeEnv(napi_env env) { for (const auto &entry: it->ExtendedCtorFuncCache) { if (entry.second.extendedCtorFunction == nullptr) { - napi_delete_reference(env, entry.second.extendedCtorFunction); + NAPI_GUARD(napi_delete_reference(env, entry.second.extendedCtorFunction)) {} } } it->ExtendedCtorFuncCache.clear(); @@ -2144,7 +2572,7 @@ void MetadataNode::onDisposeEnv(napi_env env) { auto it = s_arrayObjects.find(env); if (it != s_arrayObjects.end()) { if (it->second != nullptr) { - napi_delete_reference(env, it->second); + NAPI_GUARD(napi_delete_reference(env, it->second)) {} } s_arrayObjects.erase(it); } diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.h b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.h index bdb6537c..03e59890 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.h +++ b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.h @@ -7,6 +7,7 @@ #include "robin_hood.h" #include "MetadataReader.h" #include "Runtime.h" +#include "ObjectManager.h" #include "FieldCallbackData.h" using namespace tns; @@ -32,7 +33,13 @@ class MetadataNode { static napi_value GetImplementationObject(napi_env env, napi_value object); inline static MetadataNode* GetInstanceMetadata(napi_env env, napi_value object) { - void *node; +#ifdef USE_HOST_OBJECT + // Host build: metadata lives on the per-instance JSInstanceInfo (reached + // via host data on the proxy, or the wrap on a raw instance) — no + // interceptor-forwarded "#instance_metadata" property get. + return tns::Runtime::GetRuntime(env)->GetObjectManager()->GetInstanceNode(object); +#else + void *node = nullptr; napi_value external; napi_get_named_property(env, object, "#instance_metadata", &external); @@ -42,6 +49,7 @@ class MetadataNode { if (node == nullptr) return nullptr; return reinterpret_cast(node); +#endif } inline static MetadataNode* GetNodeFromHandle(napi_env env, napi_value value) { @@ -52,7 +60,8 @@ class MetadataNode { static string GetTypeMetadataName(napi_env env, napi_value value); static napi_value CreateExtendedJSWrapper(napi_env env, ObjectManager *objectManager, - const std::string &proxyClassName, int javaObjectID); + const std::string &proxyClassName, int javaObjectID, + MetadataNode **outNode = nullptr); std::string GetName(); @@ -142,6 +151,12 @@ class MetadataNode { static napi_value NullObjectAccessorGetterCallback(napi_env env, napi_callback_info info); + // Returns true if `jsThis` is a real backed instance rather than the class + // prototype (i.e. someone did Class.prototype.). With host objects + // enabled, every real instance is a host-object proxy and the prototype is + // not; otherwise we identity-compare against the cached prototype. + static bool IsInstanceReceiver(napi_env env, napi_value jsThis, napi_ref prototypeRef); + static napi_value FieldAccessorGetterCallback(napi_env env, napi_callback_info info); static napi_value FieldAccessorSetterCallback(napi_env env, napi_callback_info info); @@ -154,6 +169,16 @@ class MetadataNode { static napi_value ArrayLengthCallback(napi_env env, napi_callback_info info); + // Native equivalents of the helpers that used to live in getNativeArrayProp. + static napi_value ArrayMapCallback(napi_env env, napi_callback_info info); + + static napi_value ArrayForEachCallback(napi_env env, napi_callback_info info); + + static napi_value ArrayToStringCallback(napi_env env, napi_callback_info info); + + static napi_value + ArraySymbolIteratorCallback(napi_env env, napi_callback_info info); + static napi_value PropertyAccessorGetterCallback(napi_env env, napi_callback_info info); static napi_value PropertyAccessorSetterCallback(napi_env env, napi_callback_info info); @@ -179,6 +204,15 @@ class MetadataNode { static napi_value NullValueOfCallback(napi_env env, napi_callback_info info); + // Heap holder for the Symbol.hasInstance callback data. The napi `data` + // pointer must be a real heap pointer: PrimJS boxes callback data into 48 + // bits and rebuilds it with a fixed top-16-bit heap tag on retrieval, which + // would corrupt a raw JNI global ref passed directly. See + // RegisterSymbolHasInstanceCallback. + struct SymbolHasInstanceData { + jclass clazz; + }; + static void RegisterSymbolHasInstanceCallback(napi_env env, const MetadataTreeNode *treeNode, napi_value interface); static napi_value SymbolHasInstanceCallback(napi_env env, napi_callback_info info); @@ -231,6 +265,13 @@ class MetadataNode { MetadataNode *node; MethodCallbackData *parent; bool isSuper; + // Lazily-cached, per-class invariants resolved on first dispatch + // (-1 = not yet computed, 0 = false, 1 = true). + int8_t cachedIsFromInterface = -1; + int8_t cachedIsValueOf = -1; + // Cached per-env ObjectManager (this data is created per env, so the + // pointer's lifetime matches it — no staleness across envs). + tns::ObjectManager *objectManager = nullptr; }; struct PackageGetterMethodData { @@ -274,6 +315,17 @@ class MetadataNode { std::string propertyName; std::string getterMethodName; std::string setterMethodName; + // Cached prototype the accessor lives on; used to detect + // Class.prototype. access when host objects are disabled. + napi_ref prototype = nullptr; + // Direct-dispatch support: the resolved getter/setter method entries plus + // cached invariants let the accessor call CallJavaMethod directly, with no + // JS method lookup or nested MethodCallback. nullptr => no getter/setter. + MetadataEntry *getterEntry = nullptr; + MetadataEntry *setterEntry = nullptr; + MetadataNode *node = nullptr; + int8_t cachedIsFromInterface = -1; + tns::ObjectManager *objectManager = nullptr; }; struct ExtendedClassCallbackData { diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/MethodCache.h b/test-app/runtime/src/main/cpp/runtime/metadata/MethodCache.h index bcf8e17d..58bbbdd8 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/MethodCache.h +++ b/test-app/runtime/src/main/cpp/runtime/metadata/MethodCache.h @@ -155,7 +155,7 @@ class MethodCache { if (!napi_util::is_null_or_undefined(env, nullNode)) { - void *data; + void *data = nullptr; napi_get_value_external(env, nullNode, &data); auto treeNode = reinterpret_cast(data); diff --git a/test-app/runtime/src/main/cpp/runtime/module/ModuleInternal.cpp b/test-app/runtime/src/main/cpp/runtime/module/ModuleInternal.cpp index 9efe25a7..77015595 100644 --- a/test-app/runtime/src/main/cpp/runtime/module/ModuleInternal.cpp +++ b/test-app/runtime/src/main/cpp/runtime/module/ModuleInternal.cpp @@ -27,14 +27,15 @@ ModuleInternal::ModuleInternal() } void ModuleInternal::DeInit() { + napi_status status; if (m_env != nullptr) { - napi_delete_reference(m_env, this->m_requireFunction); - napi_delete_reference(m_env, this->m_requireFactoryFunction); + NAPI_GUARD(napi_delete_reference(m_env, this->m_requireFunction)) {} + NAPI_GUARD(napi_delete_reference(m_env, this->m_requireFactoryFunction)) {} } for (const auto& pair: this->m_requireCache) { if (m_env != nullptr) { - napi_delete_reference(m_env, pair.second); + NAPI_GUARD(napi_delete_reference(m_env, pair.second)) {} } } this->m_requireCache.clear(); @@ -71,10 +72,14 @@ void ModuleInternal::Init(napi_env env, const std::string& baseDir) { )"; napi_value source; - napi_create_string_utf8(env, requireFactoryScript, NAPI_AUTO_LENGTH, &source); + NAPI_GUARD(napi_create_string_utf8(env, requireFactoryScript, NAPI_AUTO_LENGTH, &source)) { + return; + } napi_value global; - napi_get_global(env, &global); + NAPI_GUARD(napi_get_global(env, &global)) { + return; + } napi_value result; status = js_execute_script(env, source, "", &result); @@ -98,19 +103,24 @@ napi_value ModuleInternal::GetRequireFunction(napi_env env, const std::string& d if (itFound != m_requireCache.end()) { requireFunc = napi_util::get_ref_value(env, itFound->second); } else { + napi_status status; napi_value requireFuncFactory = napi_util::get_ref_value(env, m_requireFactoryFunction); napi_value requireInternalFunc = napi_util::get_ref_value(env, m_requireFunction); napi_value args[2]; args[0] = requireInternalFunc; - napi_create_string_utf8(env, dirName.c_str(), NAPI_AUTO_LENGTH, &args[1]); - + NAPI_GUARD(napi_create_string_utf8(env, dirName.c_str(), NAPI_AUTO_LENGTH, &args[1])) { + return nullptr; + } + napi_value thiz; - napi_create_object(env, &thiz); + NAPI_GUARD(napi_create_object(env, &thiz)) { + return nullptr; + } napi_value result; - napi_status status = napi_call_function(env, thiz, requireFuncFactory, 2, args, &result); + status = napi_call_function(env, thiz, requireFuncFactory, 2, args, &result); assert(status == napi_ok && result != nullptr); bool isFunction = napi_util::is_of_type(env, result, napi_function); @@ -146,7 +156,7 @@ napi_value ModuleInternal::RequireCallback(napi_env env, napi_callback_info info } napi_value ModuleInternal::RequireCallbackImpl(napi_env env, napi_callback_info info) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(4) if (argc != 2) { throw NativeScriptException(string("require should be called with two parameters")); @@ -170,44 +180,65 @@ napi_value ModuleInternal::RequireCallbackImpl(napi_env env, napi_callback_info return moduleObj; } else { napi_value exports; - napi_get_named_property(env, moduleObj, "exports", &exports); + // Throw rather than return nullptr so a failed require surfaces as a JS + // exception instead of silently evaluating to undefined. + NAPI_GUARD(napi_get_named_property(env, moduleObj, "exports", &exports)) { + throw NativeScriptException("Failed to read exports for module: " + moduleName); + } assert(!napi_util::is_null_or_undefined(env, exports)); return exports; } } napi_value ModuleInternal::RequireNativeCallback(napi_env env, napi_callback_info info) { - void* data; - napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data); + napi_status status; + void* data = nullptr; + NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data)) { + return nullptr; + } auto cb = reinterpret_cast(data); napi_value exports; - napi_create_object(env, &exports); + NAPI_GUARD(napi_create_object(env, &exports)) { + return nullptr; + } return cb(env, exports); } napi_status ModuleInternal::Load(napi_env env, const std::string& path) { + napi_status status; napi_value global; - napi_get_global(env, &global); + NAPI_GUARD(napi_get_global(env, &global)) { + return status; + } napi_value require; - napi_get_named_property(env, global, "require", &require); + NAPI_GUARD(napi_get_named_property(env, global, "require", &require)) { + return status; + } napi_value args[1]; - napi_create_string_utf8(env, path.c_str(), path.size(), &args[0]); + NAPI_GUARD(napi_create_string_utf8(env, path.c_str(), path.size(), &args[0])) { + return status; + } napi_value result; - napi_status status = napi_call_function(env, global, require, 1, args, &result); + status = napi_call_function(env, global, require, 1, args, &result); return status; } void ModuleInternal::LoadWorker(napi_env env, const string& path) { + napi_status status; Load(env, path); bool hasPendingException; - napi_is_exception_pending(env, &hasPendingException); + NAPI_GUARD(napi_is_exception_pending(env, &hasPendingException)) { + return; + } if (hasPendingException) { napi_value error; - napi_get_and_clear_last_exception(env, &error); + NAPI_GUARD(napi_get_and_clear_last_exception(env, &error)) { + return; + } CallbackHandlers::CallWorkerScopeOnErrorHandle(env, error); } } @@ -221,14 +252,25 @@ void ModuleInternal::CheckFileExists(napi_env env, const std::string& path, cons napi_value ModuleInternal::LoadInternalModule(napi_env env, const std::string& moduleName) { if (moduleName == "url") { + napi_status status; napi_value moduleObj; - napi_create_object(env, &moduleObj); + NAPI_GUARD(napi_create_object(env, &moduleObj)) { + return nullptr; + } napi_value url; napi_value exports; - napi_create_object(env, &exports); - napi_get_named_property(env, napi_util::global(env), "URL", &url); - napi_set_named_property(env, exports, "URL", url); - napi_set_named_property(env, moduleObj, "exports", exports); + NAPI_GUARD(napi_create_object(env, &exports)) { + return nullptr; + } + NAPI_GUARD(napi_get_named_property(env, napi_util::global(env), "URL", &url)) { + return nullptr; + } + NAPI_GUARD(napi_set_named_property(env, exports, "URL", url)) { + return nullptr; + } + NAPI_GUARD(napi_set_named_property(env, moduleObj, "exports", exports)) { + return nullptr; + } napi_util::napi_set_function(env, exports, "pathToFileURL", [](napi_env env, napi_callback_info info) -> napi_value { return ArgConverter::convertToJsString(env, "file://"); }); @@ -316,22 +358,38 @@ std::string ModuleInternal::EnsureFileProtocol(const std::string& path) { } napi_value ModuleInternal::LoadModule(napi_env env, const std::string& modulePath, const std::string& moduleCacheKey) { + napi_status status; napi_value result; + // A failure setting up the module scaffold must propagate as an exception: + // require() returning nullptr would surface to JS as `undefined` with no + // error, unlike every other failure path in this function which throws. napi_value context; - napi_get_global(env, &context); + NAPI_GUARD(napi_get_global(env, &context)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value moduleObj; - napi_create_object(env, &moduleObj); + NAPI_GUARD(napi_create_object(env, &moduleObj)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value exportsObj; - napi_create_object(env, &exportsObj); + NAPI_GUARD(napi_create_object(env, &exportsObj)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } - napi_set_named_property(env, moduleObj, "exports", exportsObj); + NAPI_GUARD(napi_set_named_property(env, moduleObj, "exports", exportsObj)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value fullRequiredModulePath; - napi_create_string_utf8(env, modulePath.c_str(), modulePath.size(), &fullRequiredModulePath); - napi_set_named_property(env, moduleObj, "filename", fullRequiredModulePath); + NAPI_GUARD(napi_create_string_utf8(env, modulePath.c_str(), modulePath.size(), &fullRequiredModulePath)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } + NAPI_GUARD(napi_set_named_property(env, moduleObj, "filename", fullRequiredModulePath)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_ref poModuleObj = napi_util::make_ref(env, moduleObj); TempModule tempModule(this, modulePath, moduleCacheKey, poModuleObj); @@ -339,16 +397,24 @@ napi_value ModuleInternal::LoadModule(napi_env env, const std::string& modulePat napi_value moduleFunc; if (Util::EndsWith(modulePath, ".js")) { - napi_value script = LoadScript(env, modulePath, fullRequiredModulePath); DEBUG_WRITE("%s", modulePath.c_str()); - napi_status status = js_execute_script(env, script, EnsureFileProtocol(modulePath).c_str(), &moduleFunc); + // Fast path: if the build compiled this module to engine bytecode, run it + // directly. This peeks the file header only — the source is never read or + // wrapped for a bytecode module. Bytecode is the compiled form of the + // *wrapped* module content, so it yields the same wrapper function. + status = js_run_bytecode_file(env, EnsureFileProtocol(modulePath).c_str(), &moduleFunc); + if (status == napi_cannot_run_js) { + // Not bytecode — compile and run the wrapped source as usual. + napi_value script = LoadScript(env, modulePath, fullRequiredModulePath); + status = js_execute_script(env, script, EnsureFileProtocol(modulePath).c_str(), &moduleFunc); + } if (status != napi_ok) { bool pendingException; - napi_is_exception_pending(env, &pendingException); + NAPI_GUARD(napi_is_exception_pending(env, &pendingException)) {} napi_value error = nullptr; if (pendingException) { - napi_get_and_clear_last_exception(env, &error); + NAPI_GUARD(napi_get_and_clear_last_exception(env, &error)) {} } if (error) { throw NativeScriptException(env, error, "Error running script " + modulePath); @@ -372,9 +438,13 @@ napi_value ModuleInternal::LoadModule(napi_env env, const std::string& modulePat auto cb = reinterpret_cast(func); napi_value exports; - napi_create_object(env, &exports); + NAPI_GUARD(napi_create_object(env, &exports)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value result = cb(env, exports); - napi_set_named_property(env, moduleObj, "exports", result); + NAPI_GUARD(napi_set_named_property(env, moduleObj, "exports", result)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } tempModule.SaveToCache(); return moduleObj; } else { @@ -383,36 +453,50 @@ napi_value ModuleInternal::LoadModule(napi_env env, const std::string& modulePat } napi_value fileName; - napi_create_string_utf8(env, modulePath.c_str(), modulePath.size(), &fileName); + NAPI_GUARD(napi_create_string_utf8(env, modulePath.c_str(), modulePath.size(), &fileName)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } char pathcopy[1024]; strcpy(pathcopy, modulePath.c_str()); std::string strDirName(dirname(pathcopy)); napi_value dirName; - napi_create_string_utf8(env, strDirName.c_str(), strDirName.size(), &dirName); + NAPI_GUARD(napi_create_string_utf8(env, strDirName.c_str(), strDirName.size(), &dirName)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value require = GetRequireFunction(env, strDirName); napi_value requireArgs[5] = { moduleObj, exportsObj, require, fileName, dirName }; - napi_set_named_property(env, moduleObj, "require", require); + NAPI_GUARD(napi_set_named_property(env, moduleObj, "require", require)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_util::define_property(env, moduleObj, "id", fileName); napi_value thiz; - napi_create_object(env, &thiz); + NAPI_GUARD(napi_create_object(env, &thiz)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value globalExtends; - napi_get_named_property(env, context, "__extends", &globalExtends); - napi_set_named_property(env, thiz, "__extends", globalExtends); + NAPI_GUARD(napi_get_named_property(env, context, "__extends", &globalExtends)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } + NAPI_GUARD(napi_set_named_property(env, thiz, "__extends", globalExtends)) { + throw NativeScriptException("Failed to load module: " + modulePath); + } napi_value callResult; - napi_status status = napi_call_function(env, thiz, moduleFunc, 5, requireArgs, &callResult); - bool pendingException; - napi_is_exception_pending(env, &pendingException); - if (status != napi_ok || pendingException) { + // Keep the module-call status in its own variable: NAPI_GUARD below reassigns + // the shared `status`, which would otherwise mask a failed module invocation. + napi_status callStatus = napi_call_function(env, thiz, moduleFunc, 5, requireArgs, &callResult); + bool pendingException = false; + NAPI_GUARD(napi_is_exception_pending(env, &pendingException)) {} + if (callStatus != napi_ok || pendingException) { napi_value exception; - napi_get_and_clear_last_exception(env, &exception); + NAPI_GUARD(napi_get_and_clear_last_exception(env, &exception)) {} if (exception) { throw NativeScriptException(env, exception, "Error calling module function: "); } else { @@ -432,15 +516,16 @@ napi_value ModuleInternal::LoadScript(napi_env env, const std::string& path, nap } napi_value ModuleInternal::LoadData(napi_env env, const std::string& path) { + napi_status status; std::string jsonData = Runtime::GetRuntime(m_env)->ReadFileText(path); napi_value json = JsonParseString(env, jsonData); if (!napi_util::is_of_type(env, json, napi_object)) { bool pendingException; - napi_is_exception_pending(env, &pendingException); + NAPI_GUARD(napi_is_exception_pending(env, &pendingException)) {} if (pendingException) { napi_value error; - napi_get_and_clear_last_exception(env, &error); + NAPI_GUARD(napi_get_and_clear_last_exception(env, &error)) {} throw NativeScriptException(env, error, "JSON is not valid, file=" + path); } else { throw NativeScriptException("JSON is not valid, file=" + path); @@ -462,8 +547,11 @@ napi_value ModuleInternal::WrapModuleContent(napi_env env, const std::string& pa result += content; result += MODULE_EPILOGUE; + napi_status status; napi_value wrappedContent; - napi_create_string_utf8(env, result.c_str(), result.size(), &wrappedContent); + NAPI_GUARD(napi_create_string_utf8(env, result.c_str(), result.size(), &wrappedContent)) { + return nullptr; + } return wrappedContent; } diff --git a/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.cpp b/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.cpp index b24db2ce..2feb1482 100644 --- a/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.cpp +++ b/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.cpp @@ -5,15 +5,22 @@ #include "Util.h" #include "NativeScriptException.h" #include "Runtime.h" +#include "CallbackHandlers.h" #include #include using namespace std; using namespace tns; +// GetClassName is static so exception handling can resolve a Java class name +// without retrieving the runtime/ObjectManager (which may be unavailable +// mid-exception). These JNI ids are process-global once looked up. +jclass ObjectManager::JAVA_LANG_CLASS = nullptr; +jmethodID ObjectManager::GET_NAME_METHOD_ID = nullptr; + ObjectManager::ObjectManager(jobject javaRuntimeObject) : m_javaRuntimeObject(javaRuntimeObject), - m_cache(NewWeakGlobalRefCallback, DeleteWeakGlobalRefCallback, 1000, this), + m_cache(NewWeakGlobalRefCallback, DeleteWeakGlobalRefCallback, ValidateWeakGlobalRefCallback, 1000, this), m_currentObjectId(0), m_jsObjectProxyCreator(nullptr), m_jsObjectCtor(nullptr), @@ -53,32 +60,36 @@ ObjectManager::ObjectManager(jobject javaRuntimeObject) : void ObjectManager::Init(napi_env env) { + napi_status status; m_env = env; napi_value jsObjectCtor; - napi_define_class(env, "JSObject", NAPI_AUTO_LENGTH, JSObjectConstructorCallback, nullptr, + NAPI_GUARD(napi_define_class(env, "JSObject", NAPI_AUTO_LENGTH, JSObjectConstructorCallback, nullptr, 0, - nullptr, &jsObjectCtor); + nullptr, &jsObjectCtor)) { + return; + } - napi_set_named_property(env, napi_util::get_prototype(env, jsObjectCtor), PRIVATE_IS_NAPI, - napi_util::get_true(env)); + NAPI_GUARD(napi_set_named_property(env, napi_util::get_prototype(env, jsObjectCtor), PRIVATE_IS_NAPI, + napi_util::get_true(env))) {} m_jsObjectCtor = napi_util::make_ref(env, jsObjectCtor, 1); } void ObjectManager::OnDisposeEnv() { + napi_status status; JEnv jEnv; - if (this->m_jsObjectCtor) napi_delete_reference(m_env, this->m_jsObjectCtor); - if (this->m_jsObjectProxyCreator) napi_delete_reference(m_env, this->m_jsObjectProxyCreator); + if (this->m_jsObjectCtor) { NAPI_GUARD(napi_delete_reference(m_env, this->m_jsObjectCtor)) {} } + if (this->m_jsObjectProxyCreator) { NAPI_GUARD(napi_delete_reference(m_env, this->m_jsObjectProxyCreator)) {} } for (auto &entry: m_idToProxy) { if (!entry.second) continue; - napi_delete_reference(m_env, entry.second); + NAPI_GUARD(napi_delete_reference(m_env, entry.second)) {} } m_idToProxy.clear(); for (auto &entry: m_idToObject) { if (!entry.second) continue; - napi_delete_reference(m_env, entry.second); + NAPI_GUARD(napi_delete_reference(m_env, entry.second)) {} } m_idToObject.clear(); } @@ -86,43 +97,41 @@ void ObjectManager::OnDisposeEnv() { napi_value ObjectManager::GetOrCreateProxyWeak(jint javaObjectID, napi_value instance) { napi_value proxy = nullptr; #ifdef USE_HOST_OBJECT - bool is_array = false; - napi_value getter = nullptr; - napi_value setter = nullptr; - - napi_has_named_property(m_env, instance, "__is__javaArray", &is_array); - void* data; + // An unwrap miss is expected here (the instance may carry no wrap), so the + // status is deliberately ignored — data stays null and CreateHostObjectProxy + // handles that. + void* data = nullptr; napi_unwrap(m_env, instance, &data); - - if (is_array) { - napi_value global; - napi_get_global(m_env, &global); - napi_get_named_property(m_env, global, "getNativeArrayProp", &getter); - napi_get_named_property(m_env, global, "setNativeArrayProp", &setter); - } - - napi_create_host_object(m_env, instance, nullptr, data, is_array, getter, setter, &proxy); + // Transient (weak) proxy: borrows the instance's existing JSInstanceInfo. + proxy = CreateHostObjectProxy(instance, reinterpret_cast(data), + /*isPrimary=*/false); #else + napi_status status; napi_value argv[2]; argv[0] = instance; - napi_create_int32(m_env, javaObjectID, &argv[1]); + NAPI_GUARD(napi_create_int32(m_env, javaObjectID, &argv[1])) { + return nullptr; + } if (!this->m_jsObjectProxyCreator) { napi_value jsObjectProxyCreator; - napi_get_named_property(m_env, napi_util::global(m_env), "__createNativeProxy", - &jsObjectProxyCreator); + NAPI_GUARD(napi_get_named_property(m_env, napi_util::global(m_env), "__createNativeProxy", + &jsObjectProxyCreator)) { + return nullptr; + } this->m_jsObjectProxyCreator = napi_util::make_ref(m_env, jsObjectProxyCreator); } - napi_call_function(m_env, napi_util::global(m_env), + NAPI_GUARD(napi_call_function(m_env, napi_util::global(m_env), napi_util::get_ref_value(m_env, this->m_jsObjectProxyCreator), - 2, argv, &proxy); + 2, argv, &proxy)) {} #endif return proxy; } napi_value ObjectManager::GetOrCreateProxy(jint javaObjectID, napi_value instance) { + napi_status status; napi_value proxy = nullptr; auto it = m_idToProxy.find(javaObjectID); if (it != m_idToProxy.end() && it->second != nullptr) { @@ -130,7 +139,7 @@ napi_value ObjectManager::GetOrCreateProxy(jint javaObjectID, napi_value instanc if (!napi_util::is_null_or_undefined(m_env, proxy)) { return proxy; } else { - napi_delete_reference(m_env, it->second); + NAPI_GUARD(napi_delete_reference(m_env, it->second)) {} m_idToProxy.erase(javaObjectID); } } @@ -138,38 +147,38 @@ napi_value ObjectManager::GetOrCreateProxy(jint javaObjectID, napi_value instanc DEBUG_WRITE("%s %d", "Creating a new proxy for java object with id:", javaObjectID); #ifdef USE_HOST_OBJECT - bool is_array = false; - napi_value getter = nullptr; - napi_value setter = nullptr; - - napi_has_named_property(m_env, instance, "__is__javaArray", &is_array); - - auto data = new JSInstanceInfo(javaObjectID, nullptr); - - if (is_array) { - napi_value global; - napi_get_global(m_env, &global); - napi_get_named_property(m_env, global, "getNativeArrayProp", &getter); - napi_get_named_property(m_env, global, "setNativeArrayProp", &setter); + // Primary (cached) proxy: owns a fresh JSInstanceInfo and marks the java + // instance weak when collected. + auto info = new JSInstanceInfo(javaObjectID, nullptr); + // Carry the class metadata from the raw instance's JSInstanceInfo (set in + // Link) so GetInstanceMetadata resolves it from the proxy. + // Unwrap miss is expected (weak/non-wrapped instances); ignore the status. + void *rawInfo = nullptr; + napi_unwrap(m_env, instance, &rawInfo); + if (rawInfo != nullptr) { + info->node = reinterpret_cast(rawInfo)->node; } - - napi_create_host_object(m_env, instance, JSObjectProxyFinalizerCallback, data, is_array, getter, setter, &proxy); + proxy = CreateHostObjectProxy(instance, info, /*isPrimary=*/true); #else napi_value argv[2]; argv[0] = instance; - napi_create_int32(m_env, javaObjectID, &argv[1]); + NAPI_GUARD(napi_create_int32(m_env, javaObjectID, &argv[1])) { + return nullptr; + } if (!this->m_jsObjectProxyCreator) { napi_value jsObjectProxyCreator; - napi_get_named_property(m_env, napi_util::global(m_env), "__createNativeProxy", - &jsObjectProxyCreator); + NAPI_GUARD(napi_get_named_property(m_env, napi_util::global(m_env), "__createNativeProxy", + &jsObjectProxyCreator)) { + return nullptr; + } this->m_jsObjectProxyCreator = napi_util::make_ref(m_env, jsObjectProxyCreator); } - napi_call_function(m_env, napi_util::global(m_env), + NAPI_GUARD(napi_call_function(m_env, napi_util::global(m_env), napi_util::get_ref_value(m_env, this->m_jsObjectProxyCreator), - 2, argv, &proxy); + 2, argv, &proxy)) {} if (!proxy) { DEBUG_WRITE("Failed to create proxy for javaObjectId %d", javaObjectID); @@ -180,8 +189,8 @@ napi_value ObjectManager::GetOrCreateProxy(jint javaObjectID, napi_value instanc auto data = new JSInstanceInfo(javaObjectID, nullptr); napi_value external; - napi_create_external(m_env, data, JSObjectProxyFinalizerCallback, data, &external); - napi_set_named_property(m_env, proxy, "[[external]]", external); + NAPI_GUARD(napi_create_external(m_env, data, JSObjectProxyFinalizerCallback, data, &external)) {} + NAPI_GUARD(napi_set_named_property(m_env, proxy, "[[external]]", external)) {} #endif @@ -201,46 +210,87 @@ napi_value ObjectManager::GetOrCreateProxy(jint javaObjectID, napi_value instanc return proxy; } -JniLocalRef ObjectManager::GetJavaObjectByJsObject(napi_value object, int *objectId) { - int32_t javaObjectId = -1; - if (objectId) { - javaObjectId = *objectId; - } +JniLocalRef ObjectManager::GetJavaObjectByJsObject(napi_value object, int *objectId, bool *isSuper) { + napi_status status; + int32_t javaObjectId = (objectId) ? *objectId : -1; + // Cache slot for the super-call flag on whichever per-object info we resolve; + // resolved once from PRIVATE_CALLSUPER, then read from the cached field. + int8_t *superSlot = nullptr; #ifdef USE_HOST_OBJECT + // Non-host object → miss (an error status on some engines); expected, ignore. void* data = nullptr; napi_get_host_object_data(m_env, object, &data); if (data) { - auto info = (JSInstanceInfo *) data; - javaObjectId = info->JavaObjectID; + auto proxy = (HostObjectProxy *) data; + if (proxy->instanceInfo) javaObjectId = proxy->instanceInfo->JavaObjectID; + superSlot = &proxy->isSuper; } else { JSInstanceInfo *jsInstanceInfo = GetJSInstanceInfo(object); - if (jsInstanceInfo != nullptr) javaObjectId = jsInstanceInfo->JavaObjectID; + if (jsInstanceInfo != nullptr) { + javaObjectId = jsInstanceInfo->JavaObjectID; + superSlot = &jsInstanceInfo->isSuper; + } } #else if (javaObjectId == -1) { JSInstanceInfo *jsInstanceInfo = GetJSInstanceInfo(object); - if (jsInstanceInfo != nullptr) javaObjectId = jsInstanceInfo->JavaObjectID; + if (jsInstanceInfo != nullptr) { + javaObjectId = jsInstanceInfo->JavaObjectID; + superSlot = &jsInstanceInfo->isSuper; + } } #endif + if (isSuper) { + if (superSlot != nullptr) { + if (*superSlot < 0) { + napi_value superValue; + NAPI_GUARD(napi_get_named_property(m_env, object, PRIVATE_CALLSUPER, &superValue)) {} + *superSlot = napi_util::get_bool(m_env, superValue) ? 1 : 0; + } + *isSuper = (*superSlot == 1); + } else { + *isSuper = false; + } + } + if (objectId) { *objectId = javaObjectId; } - if (javaObjectId != -1) return {GetJavaObjectByID(javaObjectId), true}; + if (javaObjectId != -1) { + try { + return {GetJavaObjectByID(javaObjectId), true}; + } catch (NativeScriptException &e) { + // Surface which object failed instead of a bare error — this usually + // means the id belongs to a different runtime/thread. + throw NativeScriptException("Failed to get Java object by ID. id=" + + std::to_string(javaObjectId) + ". " + e.what()); + } + } return {}; } JniLocalRef ObjectManager::GetJavaObjectByJsObjectFast(napi_value object) { - void *data = nullptr; - #ifdef USE_HOST_OBJECT - napi_get_host_object_data(m_env, object, &data); + // A non-host object yields a miss here (an error status on some engines); + // that is expected, so ignore the status and fall through. + void *hostData = nullptr; + napi_get_host_object_data(m_env, object, &hostData); + if (hostData) { + auto proxy = reinterpret_cast(hostData); + if (proxy->instanceInfo) { + return {GetJavaObjectByID(proxy->instanceInfo->JavaObjectID), true}; + } + } #endif - if (!data) napi_unwrap(m_env, object, &data); + // Unwrap miss is the common, expected case (the object may not be wrapped); + // ignore the status and fall back to the slow lookup below. + void *data = nullptr; + napi_unwrap(m_env, object, &data); if (data) { auto info = reinterpret_cast(data); @@ -251,38 +301,437 @@ JniLocalRef ObjectManager::GetJavaObjectByJsObjectFast(napi_value object) { } ObjectManager::JSInstanceInfo *ObjectManager::GetJSInstanceInfo(napi_value object) { + #ifdef USE_HOST_OBJECT + // Non-host object → miss (an error status on some engines); expected, ignore. + void *hostData = nullptr; + napi_get_host_object_data(m_env, object, &hostData); + if (hostData) { + auto proxy = reinterpret_cast(hostData); + if (proxy->instanceInfo) { + return proxy->instanceInfo; + } + } + #endif + if (!IsRuntimeJsObject(object)) return nullptr; - return GetJSInstanceInfoFromRuntimeObject(object); } +MetadataNode *ObjectManager::GetInstanceNode(napi_value object) { + JSInstanceInfo *info = GetJSInstanceInfo(object); + return info != nullptr ? info->node : nullptr; +} + bool ObjectManager::IsHostObject(napi_value object) { #ifdef USE_HOST_OBJECT + napi_status status; bool isHostObject; - napi_is_host_object(m_env, object, &isHostObject); + NAPI_GUARD(napi_is_host_object(m_env, object, &isHostObject)) { + return false; + } return isHostObject; #endif return false; } +#ifdef USE_HOST_OBJECT +// ---------------------------------------------------------------------------- +// Host object proxy: callbacks + lifecycle +// +// The new napi_create_host_object takes no target/getter/setter, so the proxy +// forwards to the wrapped `instance` (kept in HostObjectProxy::target) via +// these callbacks. Array-like instances route get/set through the JS helpers +// getNativeArrayProp/setNativeArrayProp, mirroring the old implementation. +// ---------------------------------------------------------------------------- + +// Recognise a canonical array index in a host-object trap key. V8 routes numeric +// indices through a dedicated indexed interceptor (the key is a napi_number), but +// QuickJS(-NG) delivers every key to get()/set() as a string, so an index arrives +// as its canonical decimal string ("0", "1", ...). Accept both forms: a +// non-negative integer number, or the canonical decimal string of a uint32 in +// [0, 2^32-2] (the valid array-index range, no leading zeros). +static bool TryGetArrayIndex(napi_env env, napi_value property, uint32_t &outIndex) { + napi_status status; + napi_valuetype type; + NAPI_GUARD(napi_typeof(env, property, &type)) { return false; } + + if (type == napi_number) { + double d = 0; + NAPI_GUARD(napi_get_value_double(env, property, &d)) { return false; } + if (d < 0 || d > 4294967294.0 || d != (double) (uint32_t) d) return false; + outIndex = (uint32_t) d; + return true; + } + if (type == napi_string) { + size_t len = 0; + NAPI_GUARD(napi_get_value_string_utf8(env, property, nullptr, 0, &len)) { return false; } + if (len == 0 || len > 10) return false; // a uint32 has at most 10 digits + char buf[11]; + NAPI_GUARD(napi_get_value_string_utf8(env, property, buf, sizeof(buf), nullptr)) { return false; } + if (buf[0] == '0') { // canonical form has no leading zeros; only "0" itself + if (len != 1) return false; + outIndex = 0; + return true; + } + uint64_t v = 0; + for (size_t i = 0; i < len; i++) { + if (buf[i] < '0' || buf[i] > '9') return false; + v = v * 10 + (uint64_t) (buf[i] - '0'); + } + if (v > 4294967294ULL) return false; // max array index is 2^32 - 2 + outIndex = (uint32_t) v; + return true; + } + return false; +} + +napi_value ObjectManager::HostObjectGet(napi_env env, napi_value host, + napi_value property, void *data) { + napi_status status; + auto *proxy = reinterpret_cast(data); + try { + // Numeric keys on arrays: straight into the native element accessor. On V8 + // these arrive via the indexed interceptor (HostObjectIndexedGet); engines + // that route everything through get() (e.g. QuickJS, which passes the index + // as its decimal string) hit it here. + uint32_t index = 0; + if (proxy->isArray && !proxy->arraySignature.empty() && + TryGetArrayIndex(env, property, index)) { + return HostObjectIndexedGet(env, host, index, data); + } + + // Everything else (incl. map/forEach/toString/Symbol.iterator/length, which + // are now native methods on the array prototype) forwards to the instance. + napi_value target = napi_util::get_ref_value(env, proxy->target); + napi_value result = nullptr; + NAPI_GUARD(napi_get_property(env, target, property, &result)) {} + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectGet").ReThrowToNapi(env); + } + return nullptr; +} + +void ObjectManager::HostObjectSet(napi_env env, napi_value host, + napi_value property, napi_value value, + void *data) { + napi_status status; + auto *proxy = reinterpret_cast(data); + try { + uint32_t index = 0; + if (proxy->isArray && !proxy->arraySignature.empty() && + TryGetArrayIndex(env, property, index)) { + HostObjectIndexedSet(env, host, index, value, data); + return; + } + napi_value target = napi_util::get_ref_value(env, proxy->target); + NAPI_GUARD(napi_set_property(env, target, property, value)) {} + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectSet").ReThrowToNapi(env); + } +} + +int ObjectManager::HostObjectHas(napi_env env, napi_value host, + napi_value property, void *data) { + napi_status status; + auto *proxy = reinterpret_cast(data); + try { + // Numeric keys on java arrays: an index is "present" when it is within the + // array's bounds, matching JS array semantics. This is load-bearing on JSC: + // its JSCallbackObject consults the hasProperty callback BEFORE getProperty + // and only fetches the value when hasProperty returns true, so returning + // false here makes arr[i] read back as undefined. (V8 routes indexed reads + // through a dedicated interceptor and QuickJS calls get() directly, so this + // only affects `in`/hasOwnProperty there — which is also more correct.) + uint32_t index = 0; + if (proxy->isArray && !proxy->arraySignature.empty() && + TryGetArrayIndex(env, property, index)) { + // A Java array has a fixed length, so resolve "length" once and cache it + // on the proxy. This is the JSC hot path: JSCallbackObject calls + // hasProperty before every getProperty, so an uncached length fetch + // (JSString alloc + property get + double read) was paid per element read. + if (proxy->arrayLength < 0) { + napi_value target = napi_util::get_ref_value(env, proxy->target); + napi_value lengthVal = nullptr; + NAPI_GUARD(napi_get_named_property(env, target, "length", &lengthVal)) { return false; } + double length = 0; + NAPI_GUARD(napi_get_value_double(env, lengthVal, &length)) { return false; } + proxy->arrayLength = (int64_t) length; + } + return (int64_t) index < proxy->arrayLength; + } + + napi_value target = napi_util::get_ref_value(env, proxy->target); + bool result = false; + NAPI_GUARD(napi_has_property(env, target, property, &result)) {} + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectHas").ReThrowToNapi(env); + } + return false; +} + +int ObjectManager::HostObjectDelete(napi_env env, napi_value host, + napi_value property, void *data) { + napi_status status; + auto *proxy = reinterpret_cast(data); + try { + napi_value target = napi_util::get_ref_value(env, proxy->target); + bool result = false; + NAPI_GUARD(napi_delete_property(env, target, property, &result)) {} + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectDelete").ReThrowToNapi(env); + } + return false; +} + +napi_value ObjectManager::HostObjectOwnKeys(napi_env env, napi_value host, + void *data) { + napi_status status; + auto *proxy = reinterpret_cast(data); + try { + napi_value target = napi_util::get_ref_value(env, proxy->target); + napi_value names = nullptr; + NAPI_GUARD(napi_get_property_names(env, target, &names)) {} + return names; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectOwnKeys").ReThrowToNapi(env); + } + return nullptr; +} + +// Fast path for numeric indices on java arrays: call straight into the native +// array element accessor (the same code getValueAtIndex/setValueAtIndex run), +// skipping the JS method dispatch entirely. The host object is passed as the +// `array` receiver so CallbackHandlers can resolve the backing java array. +napi_value ObjectManager::HostObjectIndexedGet(napi_env env, napi_value host, + uint32_t index, void *data) { + auto *proxy = reinterpret_cast(data); + try { + // The proxy already knows the java object id + ObjectManager, so resolve + // the backing array directly (no locked env->runtime lookup, no host probe). + jobject arr = proxy->instanceInfo + ? (jobject) proxy->objectManager->GetJavaObjectByID( + proxy->instanceInfo->JavaObjectID) + : nullptr; + return CallbackHandlers::GetArrayElement(env, host, index, proxy->arraySignature, + proxy->objectManager, arr); + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectIndexedGet").ReThrowToNapi(env); + } + return nullptr; +} + +void ObjectManager::HostObjectIndexedSet(napi_env env, napi_value host, + uint32_t index, napi_value value, + void *data) { + auto *proxy = reinterpret_cast(data); + try { + jobject arr = proxy->instanceInfo + ? (jobject) proxy->objectManager->GetJavaObjectByID( + proxy->instanceInfo->JavaObjectID) + : nullptr; + CallbackHandlers::SetArrayElement(env, host, index, proxy->arraySignature, + value, proxy->objectManager, arr); + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectIndexedSet").ReThrowToNapi(env); + } +} + +// Mirrors the old "super" accessor: `proxy.super` resolves to `target.super`. +napi_value ObjectManager::HostObjectSuperGetter(napi_env env, + napi_callback_info info) { + napi_status status; + void *data = nullptr; + NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data)) { + return nullptr; + } + auto *proxy = reinterpret_cast(data); + try { + napi_value target = napi_util::get_ref_value(env, proxy->target); + napi_value superValue = nullptr; + NAPI_GUARD(napi_get_named_property(env, target, "super", &superValue)) {} + return superValue; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what(); + NativeScriptException(ss.str()).ReThrowToNapi(env); + } catch (...) { + NativeScriptException("Error: unknown c++ exception in HostObjectSuperGetter").ReThrowToNapi(env); + } + return nullptr; +} + +void ObjectManager::HostObjectProxyFinalizer(napi_env env, void *data, + void *hint) { + auto *proxy = reinterpret_cast(data); + if (proxy == nullptr) return; + + // The cleanup deletes a napi_ref, which is illegal from inside the GC + // finalizer pass on every engine (V8's InvokeFinalizerFromGC; a JS_FreeValue + // during a QuickJS sweep corrupts the collector). Defer it to the runtime's + // engine-agnostic post-GC drain (message-loop tick). + Runtime::PostFinalizer(env, HostObjectProxyPostFinalizer, proxy, hint); +} + +void ObjectManager::HostObjectProxyPostFinalizer(napi_env env, void *data, + void *hint) { + napi_status status; + auto *proxy = reinterpret_cast(data); + if (proxy == nullptr) return; + + auto rt = Runtime::GetRuntimeUnchecked(env); + // Once the runtime is tearing down (or its env is already off the cache), the + // env-dispose path owns every outstanding napi_ref: OnDisposeEnv clears the + // id maps and js_free_napi_env frees whatever remains in env->referencesList. + // Deleting proxy->target here in that window double-frees it (a use-after-free + // in the reference-free loop). So only release the reference during normal, + // per-object finalization; the C++ cleanup below still runs in both cases. + bool destroying = (rt == nullptr) || rt->is_destroying; + + if (proxy->target != nullptr && !destroying) { + NAPI_GUARD(napi_delete_reference(env, proxy->target)) {} + } + + // Primary (cached) proxies own their JSInstanceInfo and mark the java + // instance weak on collection (the old JSObjectProxyFinalizerCallback role). + if (proxy->isPrimary && proxy->instanceInfo) { + if (!destroying) { + auto objManager = rt->GetObjectManager(); + auto javaObjectID = proxy->instanceInfo->JavaObjectID; + if (objManager->m_weakObjectIds.find(javaObjectID) == + objManager->m_weakObjectIds.end()) { + objManager->m_weakObjectIds.emplace(javaObjectID); + JEnv jEnv; + jEnv.CallVoidMethod(objManager->m_javaRuntimeObject, + objManager->MAKE_INSTANCE_WEAK_METHOD_ID, + javaObjectID); + } + } + delete proxy->instanceInfo; + } + + delete proxy; +} + +napi_value ObjectManager::CreateHostObjectProxy(napi_value instance, + JSInstanceInfo *instanceInfo, + bool isPrimary) { + napi_status status; + auto *proxy = new HostObjectProxy(); + proxy->objectManager = this; + proxy->instanceInfo = instanceInfo; + proxy->isPrimary = isPrimary; + proxy->env = m_env; + proxy->target = napi_util::make_ref(m_env, instance, 1); + proxy->isArray = false; + + napi_host_object_methods methods = { + HostObjectGet, + HostObjectSet, + HostObjectHas, + HostObjectDelete, + HostObjectOwnKeys, + nullptr, // indexed_get (set below for arrays) + nullptr, // indexed_set + }; + + NAPI_GUARD(napi_has_named_property(m_env, instance, "__is__javaArray", &proxy->isArray)) {} + if (proxy->isArray) { + // Cache the jni array signature so numeric index access goes straight + // into the native element accessor (no JS getValueAtIndex dispatch). + // node is already on the raw instance's JSInstanceInfo (set in Link). + MetadataNode *node = GetInstanceNode(instance); + if (node != nullptr) { + proxy->arraySignature = node->GetName(); + methods.indexed_get = HostObjectIndexedGet; + methods.indexed_set = HostObjectIndexedSet; + } + } + + napi_value proxyObject = nullptr; + NAPI_GUARD(napi_create_host_object(m_env, HostObjectProxyFinalizer, proxy, &methods, + &proxyObject)) {} + + // The napi layer no longer touches the prototype chain or installs the + // "super" accessor for host objects, so do both here to preserve behaviour + // (instanceof checks, super dispatch). + napi_util::setPrototypeOf(m_env, proxyObject, + napi_util::getPrototypeOf(m_env, instance)); + + napi_property_descriptor superDesc = { + "super", nullptr, nullptr, HostObjectSuperGetter, nullptr, nullptr, + napi_default, proxy}; + NAPI_GUARD(napi_define_properties(m_env, proxyObject, 1, &superDesc)) {} + + return proxyObject; +} +#endif // USE_HOST_OBJECT + ObjectManager::JSInstanceInfo * ObjectManager::GetJSInstanceInfoFromRuntimeObject(napi_value object) { + napi_status status; napi_value jsInfo; - napi_get_named_property(m_env, object, PRIVATE_JSINFO, &jsInfo); + NAPI_GUARD(napi_get_named_property(m_env, object, PRIVATE_JSINFO, &jsInfo)) {} if (napi_util::is_null_or_undefined(m_env, jsInfo)) { napi_value proto = napi_util::get__proto__(m_env, object); //Typescript object layout has an object instance as child of the actual registered instance. checking for that if (!napi_util::is_null_or_undefined(m_env, proto)) { if (IsRuntimeJsObject(proto)) { - napi_get_named_property(m_env, proto, PRIVATE_JSINFO, &jsInfo); + NAPI_GUARD(napi_get_named_property(m_env, proto, PRIVATE_JSINFO, &jsInfo)) {} } } } if (!napi_util::is_null_or_undefined(m_env, jsInfo)) { - void *data; - napi_get_value_external(m_env, jsInfo, &data); + void *data = nullptr; + NAPI_GUARD(napi_get_value_external(m_env, jsInfo, &data)) {} auto info = reinterpret_cast(data); return info; } @@ -290,8 +739,11 @@ ObjectManager::GetJSInstanceInfoFromRuntimeObject(napi_value object) { } bool ObjectManager::IsRuntimeJsObject(napi_value object) { + napi_status status; bool result; - napi_has_named_property(m_env, object, PRIVATE_IS_NAPI, &result); + NAPI_GUARD(napi_has_named_property(m_env, object, PRIVATE_IS_NAPI, &result)) { + return false; + } return result; } @@ -356,18 +808,27 @@ ObjectManager::CreateJSWrapper(jint javaObjectID, const std::string &typeName, j napi_value ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const std::string &typeName, jclass clazz) { + napi_status status; auto className = (clazz != nullptr) ? GetClassName(clazz) : typeName; auto node = MetadataNode::GetOrCreate(className); napi_value proxy = nullptr; napi_value jsWrapper = node->CreateJSWrapper(m_env, this); if (jsWrapper != nullptr) { - JEnv jenv; - auto claz = jenv.FindClass(className); - Link(jsWrapper, javaObjectID, claz); + // Reuse the class we already resolved via GetObjectClass on the instance + // path instead of re-resolving it with a JNI FindClass. The class is only + // stored on JSInstanceInfo::ObjectClazz, which nothing on this path reads, + // so a fresh FindClass is pure overhead; only fall back to it for the + // typeName-only overload where no instance class was available. + jclass linkClazz = clazz; + if (linkClazz == nullptr) { + JEnv jenv; + linkClazz = jenv.FindClass(className); + } + Link(jsWrapper, javaObjectID, linkClazz, node); if (node->isArray()) { - napi_set_named_property(m_env, jsWrapper, "__is__javaArray", - napi_util::get_true(m_env)); + NAPI_GUARD(napi_set_named_property(m_env, jsWrapper, "__is__javaArray", + napi_util::get_true(m_env))) {} } proxy = GetOrCreateProxy(javaObjectID, jsWrapper); } @@ -375,7 +836,8 @@ ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const std::string &typeN return proxy; } -void ObjectManager::Link(napi_value object, uint32_t javaObjectID, jclass clazz) { +void ObjectManager::Link(napi_value object, uint32_t javaObjectID, jclass clazz, + MetadataNode *node) { if (!IsRuntimeJsObject(object)) { std::string errMsg("Trying to link invalid 'this' to a Java object"); throw NativeScriptException(errMsg); @@ -383,32 +845,35 @@ void ObjectManager::Link(napi_value object, uint32_t javaObjectID, jclass clazz) DEBUG_WRITE("Linking js object and java instance id: %d", javaObjectID); + napi_status status; auto jsInstanceInfo = new JSInstanceInfo(javaObjectID, clazz); + jsInstanceInfo->node = node; napi_ref objectHandle = napi_util::make_ref(m_env, object, 1); napi_value jsInfo; - napi_create_external(m_env, jsInstanceInfo, JSObjectFinalizerCallback, jsInstanceInfo, &jsInfo); - napi_set_named_property(m_env, object, PRIVATE_JSINFO, jsInfo); + NAPI_GUARD(napi_create_external(m_env, jsInstanceInfo, JSObjectFinalizerCallback, jsInstanceInfo, &jsInfo)) {} + NAPI_GUARD(napi_set_named_property(m_env, object, PRIVATE_JSINFO, jsInfo)) {} // Wrapped but does not handle data lifecycle. only used for fast access. - napi_wrap(m_env, object, jsInstanceInfo, [](napi_env env, void *data, void *hint) {}, jsInstanceInfo, - nullptr); + NAPI_GUARD(napi_wrap(m_env, object, jsInstanceInfo, [](napi_env env, void *data, void *hint) {}, jsInstanceInfo, + nullptr)) {} m_idToObject.emplace(javaObjectID, objectHandle); } bool ObjectManager::CloneLink(napi_value src, napi_value dest) { + napi_status status; auto jsInfo = GetJSInstanceInfo(src); auto success = jsInfo != nullptr; if (success) { napi_value external; - napi_create_external(m_env, jsInfo, [](napi_env env, void* d1, void*d2) {}, jsInfo, &external); - napi_set_named_property(m_env, dest, PRIVATE_JSINFO, external); - napi_wrap(m_env, dest, jsInfo, [](napi_env env, void *data, void *hint) {}, jsInfo, - nullptr); + NAPI_GUARD(napi_create_external(m_env, jsInfo, [](napi_env env, void* d1, void*d2) {}, jsInfo, &external)) {} + NAPI_GUARD(napi_set_named_property(m_env, dest, PRIVATE_JSINFO, external)) {} + NAPI_GUARD(napi_wrap(m_env, dest, jsInfo, [](napi_env env, void *data, void *hint) {}, jsInfo, + nullptr)) {} } return success; @@ -421,16 +886,6 @@ string ObjectManager::GetClassName(jobject javaObject) { return GetClassName((jclass) objectClass); } -bool ObjectManager::GetIsSuper(int objectId, napi_value value) { - auto it = m_idToSuper.find(objectId); - if (it != m_idToSuper.end()) return it->second; - napi_value superValue; - napi_get_named_property(m_env, value, PRIVATE_CALLSUPER, &superValue); - bool isSuper = napi_util::get_bool(m_env, superValue); - m_idToSuper.emplace(objectId, isSuper); - return isSuper; -} - string ObjectManager::GetClassName(jclass clazz) { JEnv env; JniLocalRef javaCanonicalName(env.CallObjectMethod(clazz, GET_NAME_METHOD_ID)); @@ -506,15 +961,24 @@ void ObjectManager::DeleteWeakGlobalRefCallback(const jweak &object, void *state jEnv.DeleteWeakGlobalRef(object); } +bool ObjectManager::ValidateWeakGlobalRefCallback(const int &javaObjectID, const jweak &object, + void *state) { + JEnv jEnv; + // A weak ref that is now IsSameObject(NULL) points to a collected object and + // must not be reused; report it as invalid so the cache evicts it. + return !jEnv.isSameObject(object, NULL); +} + napi_value ObjectManager::GetEmptyObject() { + napi_status status; napi_value emptyObjCtorFunc = napi_util::get_ref_value(m_env, m_jsObjectCtor); napi_value ex; - napi_get_and_clear_last_exception(m_env, &ex); + NAPI_GUARD(napi_get_and_clear_last_exception(m_env, &ex)) {} napi_value jsWrapper = nullptr; - napi_new_instance(m_env, emptyObjCtorFunc, 0, nullptr, &jsWrapper); + NAPI_GUARD(napi_new_instance(m_env, emptyObjCtorFunc, 0, nullptr, &jsWrapper)) {} if (napi_util::is_null_or_undefined(m_env, jsWrapper)) { return nullptr; @@ -529,6 +993,7 @@ napi_value ObjectManager::JSObjectConstructorCallback(napi_env env, napi_callbac } void ObjectManager::ReleaseObjectNow(napi_env env, int javaObjectId) { + napi_status status; auto rt = Runtime::GetRuntimeUnchecked(env); if (!rt || rt->is_destroying) return; ObjectManager *objMgr = rt->GetObjectManager(); @@ -543,13 +1008,13 @@ void ObjectManager::ReleaseObjectNow(napi_env env, int javaObjectId) { auto found = objMgr->m_idToProxy.find(javaObjectId); if (found != objMgr->m_idToProxy.end()) { - napi_delete_reference(env, found->second); + NAPI_GUARD(napi_delete_reference(env, found->second)) {} objMgr->m_idToProxy.erase(javaObjectId); } found = objMgr->m_idToObject.find(javaObjectId); if (found != objMgr->m_idToObject.end()) { - napi_delete_reference(env, found->second); + NAPI_GUARD(napi_delete_reference(env, found->second)) {} objMgr->m_idToObject.erase(javaObjectId); } @@ -557,14 +1022,16 @@ void ObjectManager::ReleaseObjectNow(napi_env env, int javaObjectId) { } void ObjectManager::ReleaseNativeObject(napi_env env, napi_value object) { + napi_status status; int32_t javaObjectId = -1; JSInstanceInfo *jsInstanceInfo; #ifdef USE_HOST_OBJECT - void* data; + // Non-host object → miss (an error status on some engines); expected, ignore. + void* data = nullptr; napi_get_host_object_data(env, object, &data); if (data) { - jsInstanceInfo = reinterpret_cast(data); + jsInstanceInfo = reinterpret_cast(data)->instanceInfo; } else { #endif jsInstanceInfo = GetJSInstanceInfo(object); @@ -577,7 +1044,7 @@ void ObjectManager::ReleaseNativeObject(napi_env env, napi_value object) { } if (javaObjectId == -1) { - napi_throw_error(env, "0", "Trying to release a non native object!"); + NAPI_GUARD(napi_throw_error(env, "0", "Trying to release a non native object!")) {} return; } @@ -585,6 +1052,7 @@ void ObjectManager::ReleaseNativeObject(napi_env env, napi_value object) { } void ObjectManager::OnGarbageCollected(JNIEnv *jEnv, jintArray object_ids) { + napi_status status; JEnv jenv(jEnv); jsize length = jenv.GetArrayLength(object_ids); int *cppArray = jenv.GetIntArrayElements(object_ids, nullptr); @@ -594,7 +1062,7 @@ void ObjectManager::OnGarbageCollected(JNIEnv *jEnv, jintArray object_ids) { int javaObjectId = cppArray[i]; auto itFound = this->m_idToObject.find(javaObjectId); if (itFound != this->m_idToObject.end()) { - napi_delete_reference(m_env, itFound->second); + NAPI_GUARD(napi_delete_reference(m_env, itFound->second)) {} this->m_idToObject.erase(javaObjectId); if (rt && !rt->is_destroying) { diff --git a/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.h b/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.h index 4c524491..b8e4b393 100644 --- a/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.h +++ b/test-app/runtime/src/main/cpp/runtime/objectmanager/ObjectManager.h @@ -14,6 +14,7 @@ #include #include "Constants.h" +class MetadataNode; namespace tns { class ObjectManager { @@ -24,7 +25,8 @@ namespace tns { void Init(napi_env env); - JniLocalRef GetJavaObjectByJsObject(napi_value object, int *objectId = nullptr); + JniLocalRef GetJavaObjectByJsObject(napi_value object, int *objectId = nullptr, + bool *isSuper = nullptr); JniLocalRef GetJavaObjectByJsObjectFast(napi_value object); @@ -49,15 +51,21 @@ namespace tns { napi_value GetOrCreateProxyWeak(jint javaObjectID, napi_value instance); - void Link(napi_value object, uint32_t javaObjectID, jclass clazz); + void Link(napi_value object, uint32_t javaObjectID, jclass clazz, + MetadataNode *node = nullptr); + + // Returns the class metadata stored on the per-instance JSInstanceInfo + // (host proxy's, or the raw instance's wrap). Used by + // MetadataNode::GetInstanceMetadata under USE_HOST_OBJECT. + MetadataNode *GetInstanceNode(napi_value object); bool CloneLink(napi_value src, napi_value dest); bool IsRuntimeJsObject(napi_value object); - std::string GetClassName(jobject javaObject); + static std::string GetClassName(jobject javaObject); - std::string GetClassName(jclass clazz); + static std::string GetClassName(jclass clazz); int GenerateNewObjectID(); @@ -81,8 +89,6 @@ namespace tns { inline static void ReleaseObjectNow(napi_env env, int javaObjectId); - bool GetIsSuper(int objectId, napi_value value); - bool IsHostObject(napi_value object); private: @@ -96,8 +102,61 @@ namespace tns { uint32_t JavaObjectID; jclass ObjectClazz; + // Cached super-call flag (-1 = unresolved, 0 = false, 1 = true). + int8_t isSuper = -1; + // Per-instance class metadata. Under USE_HOST_OBJECT this replaces the + // "#instance_metadata" property; reachable via GetInstanceNode. + MetadataNode *node = nullptr; + }; + +#ifdef USE_HOST_OBJECT + // Backing context for a host-object proxy. The new napi_create_host_object + // takes no target/getter/setter, so everything needed to proxy to the + // underlying instance is carried here in the `data` pointer. + struct HostObjectProxy { + ObjectManager *objectManager; + JSInstanceInfo *instanceInfo; // java object id holder + bool isPrimary; // owns instanceInfo + marks weak on GC + napi_env env; + napi_ref target; // strong ref to the wrapped instance + bool isArray; + std::string arraySignature; // jni array signature (arrays only) + int8_t isSuper = -1; // cached super-call flag (-1=unresolved) + int64_t arrayLength = -1; // cached fixed length (arrays only; -1=unresolved) }; + napi_value CreateHostObjectProxy(napi_value instance, + JSInstanceInfo *instanceInfo, + bool isPrimary); + + // napi_host_object_methods callbacks (transparent proxy to `target`). + static napi_value HostObjectGet(napi_env env, napi_value host, + napi_value property, void *data); + static void HostObjectSet(napi_env env, napi_value host, + napi_value property, napi_value value, + void *data); + static int HostObjectHas(napi_env env, napi_value host, + napi_value property, void *data); + static int HostObjectDelete(napi_env env, napi_value host, + napi_value property, void *data); + static napi_value HostObjectOwnKeys(napi_env env, napi_value host, + void *data); + // Fast numeric index access: native get/setValueAtIndex (arrays only). + static napi_value HostObjectIndexedGet(napi_env env, napi_value host, + uint32_t index, void *data); + static void HostObjectIndexedSet(napi_env env, napi_value host, + uint32_t index, napi_value value, + void *data); + static napi_value HostObjectSuperGetter(napi_env env, + napi_callback_info info); + static void HostObjectProxyFinalizer(napi_env env, void *data, + void *hint); + // Actual cleanup, deferred to the runtime's safe post-GC finalizer drain + // (Runtime::PostFinalizer) so its reference-deleting Node-API calls are legal. + static void HostObjectProxyPostFinalizer(napi_env env, void *data, + void *hint); +#endif + JSInstanceInfo *GetJSInstanceInfo(napi_value object); @@ -119,13 +178,14 @@ namespace tns { static void DeleteWeakGlobalRefCallback(const jweak &object, void *state); + static bool ValidateWeakGlobalRefCallback(const int &javaObjectID, const jweak &object, void *state); + jobject m_javaRuntimeObject; napi_env m_env; robin_hood::unordered_map m_idToProxy; robin_hood::unordered_map m_idToObject; - robin_hood::unordered_map m_idToSuper; robin_hood::unordered_set m_weakObjectIds; robin_hood::unordered_set m_markedAsWeakIds; @@ -137,9 +197,9 @@ namespace tns { DirectBuffer m_outBuff; - jclass JAVA_LANG_CLASS; + static jclass JAVA_LANG_CLASS; - jmethodID GET_NAME_METHOD_ID; + static jmethodID GET_NAME_METHOD_ID; jmethodID GET_JAVAOBJECT_BY_ID_METHOD_ID; diff --git a/test-app/runtime/src/main/cpp/runtime/timers/Timers.cpp b/test-app/runtime/src/main/cpp/runtime/timers/Timers.cpp index 108fc119..bb3cc9ed 100644 --- a/test-app/runtime/src/main/cpp/runtime/timers/Timers.cpp +++ b/test-app/runtime/src/main/cpp/runtime/timers/Timers.cpp @@ -2,18 +2,25 @@ #include "ArgConverter.h" #include "Runtime.h" #include "NativeScriptException.h" -#include -#include -#include +#include "JEnv.h" +#include +#include #include "Util.h" #include "NativeScriptAssert.h" /** - * Overall rules when modifying this file: - * `sortedTimers_` must always be sorted by dueTime - * `sortedTimers_`. `deletedTimers_` and `stopped` modifications MUST be done while locked with the mutex - * `threadLoop` must not access anything that is not `sortedTimers_` or `stopped` or any atomic var - * ALL changes and scheduling of a TimerTask MUST be done when locked in an isolate to ensure consistency + * Timers ride the runtime thread's Java MessageQueue via a per-runtime + * com.tns.TimerHandler bound to the isolate's Looper: + * - each scheduled timer enqueues one anonymous "due token" message via + * sendMessageAtTime, so timers share a single queue with Handler.post/ + * postDelayed and fire in exact MessageQueue order; + * - a native list (sortedTimers_) sorted by exact (sub-millisecond) due time + * picks the earliest-due timer per token, preserving the relative ordering of + * JS timers despite the millisecond-quantized Java queue. + * + * Everything below runs on the runtime thread (Init, the setTimeout/clear + * callbacks, and FireTimer via TimerHandler.handleMessage), so no locking is + * needed — sortedTimers_/timerMap_ are only touched there. */ // Takes a value and transform into a positive number @@ -42,6 +49,11 @@ static double now_ms() { using namespace tns; +jclass Timers::TIMER_HANDLER_CLASS = nullptr; +jmethodID Timers::TIMER_HANDLER_CTOR = nullptr; +jmethodID Timers::TIMER_HANDLER_POST = nullptr; +jmethodID Timers::TIMER_HANDLER_RELEASE = nullptr; + void Timers::Init(napi_env env, napi_value global) { env_ = env; // TODO: remove the __ns__ prefix once this is validated @@ -50,24 +62,37 @@ void Timers::Init(napi_env env, napi_value global) { napi_util::napi_set_function(env, global, "__ns__clearTimeout", ClearTimer, this); napi_util::napi_set_function(env, global, "__ns__clearInterval", ClearTimer, this); - napi_value object; - napi_add_finalizer(env, global, this, [](napi_env env, void *finalizeData, void *finalizeHint) { - auto thiz = reinterpret_cast(finalizeData); - delete thiz; - }, nullptr, nullptr); - - auto res = pipe(fd_); - assert(res != -1); - res = fcntl(fd_[1], F_SETFL, O_NONBLOCK); - assert(res != -1); - // TODO: check success of fd - looper_ = ALooper_prepare(0); - ALooper_acquire(looper_); - ALooper_addFd(looper_, fd_[0], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, - PumpTimerLoopCallback, this); - ALooper_wake(looper_); - watcher_ = std::thread(&Timers::threadLoop, this); - stopped = false; + napi_status status; + // Non-fatal to timer operation if this fails (only affects GC cleanup of + // `this`), so log for diagnostics but continue with handler setup. + NAPI_GUARD(napi_add_finalizer(env, global, this, + [](napi_env env, void *finalizeData, void *finalizeHint) { + auto thiz = reinterpret_cast(finalizeData); + delete thiz; + }, nullptr, nullptr)) {} + + JEnv jEnv; + if (TIMER_HANDLER_CLASS == nullptr) { + TIMER_HANDLER_CLASS = jEnv.FindClass("com/tns/TimerHandler"); + assert(TIMER_HANDLER_CLASS != nullptr); + TIMER_HANDLER_CTOR = jEnv.GetMethodID(TIMER_HANDLER_CLASS, "", "(J)V"); + TIMER_HANDLER_POST = jEnv.GetMethodID(TIMER_HANDLER_CLASS, "post", "(J)V"); + TIMER_HANDLER_RELEASE = jEnv.GetMethodID(TIMER_HANDLER_CLASS, "release", "()V"); + } + + // Bind a TimerHandler to the current (runtime) thread's Looper. + jobject localHandler = jEnv.NewObject(TIMER_HANDLER_CLASS, TIMER_HANDLER_CTOR, + reinterpret_cast(this)); + handler_ = jEnv.NewGlobalRef(localHandler); + stopped_ = false; +} + +void Timers::postTimer(const std::shared_ptr &task, double now) { + // Due-now timers post at (long)now so they tie (FIFO) with a same-ms + // postDelayed(0); future timers post at ceil(dueTime) so they never fire early. + jlong when = task->dueTime_ <= now ? (jlong) now : (jlong) std::ceil(task->dueTime_); + JEnv jEnv; + jEnv.CallVoidMethod(handler_, TIMER_HANDLER_POST, when); } void Timers::addTask(const std::shared_ptr& task) { @@ -83,34 +108,15 @@ void Timers::addTask(const std::shared_ptr& task) { task->startTime_ = now; } timerMap_.emplace(task->id_, task); - auto newTime = task->NextTime(now); - task->dueTime_ = newTime; - bool needsScheduling = true; - if (!isBufferFull.load() && task->dueTime_ <= now) { - auto result = write(fd_[1], &task->id_, sizeof(int)); - if (result != -1 || errno != EAGAIN) { - needsScheduling = false; - } else { - isBufferFull = true; - } - } - if (needsScheduling) { - { - std::lock_guard lock(mutex); - auto it = sortedTimers_.begin(); - auto dueTime = task->dueTime_; - it = std::upper_bound(sortedTimers_.begin(), sortedTimers_.end(), dueTime, - [](const double &value, - const std::shared_ptr &ref) { - return ref->dueTime > value; - }); - auto ref = std::make_shared(); - ref->dueTime = task->dueTime_; - ref->id = task->id_; - sortedTimers_.insert(it, ref); - } - taskReady.notify_one(); - } + task->dueTime_ = task->NextTime(now); + + auto it = std::upper_bound(sortedTimers_.begin(), sortedTimers_.end(), task->dueTime_, + [](const double &value, const TimerReference &ref) { + return ref.dueTime > value; + }); + sortedTimers_.insert(it, TimerReference{task->id_, task->dueTime_}); + + postTimer(task, now); } void Timers::removeTask(const std::shared_ptr &task) { @@ -119,75 +125,116 @@ void Timers::removeTask(const std::shared_ptr &task) { void Timers::removeTask(const int &taskId) { auto it = timerMap_.find(taskId); - if (it != timerMap_.end()) { - auto wasScheduled = it->second->queued_; - it->second->Unschedule(); - timerMap_.erase(it); - { - std::lock_guard lock(mutex); - if (wasScheduled) { - // was scheduled, notify the thread so it doesn't trigger again - this->deletedTimers_.insert(taskId); - } else { - // was not scheduled, remove it to reduce memory footprint - this->deletedTimers_.erase(taskId); + if (it == timerMap_.end()) { + return; + } + auto task = it->second; + if (task->queued_) { + // Remove the pending due-token reference (matched by id at its dueTime). + auto lo = std::lower_bound(sortedTimers_.begin(), sortedTimers_.end(), task->dueTime_, + [](const TimerReference &ref, const double &value) { + return ref.dueTime < value; + }); + for (auto ref = lo; ref != sortedTimers_.end() && ref->dueTime == task->dueTime_; ++ref) { + if (ref->id == taskId) { + sortedTimers_.erase(ref); + break; } } + // A token already enqueued in the Java queue is left to no-op in FireTimer. } + task->Unschedule(); + timerMap_.erase(it); } -void Timers::threadLoop() { - std::unique_lock lk(mutex); - while (!stopped) { - if (!sortedTimers_.empty()) { - auto timer = sortedTimers_.at(0); - if (deletedTimers_.find(timer->id) != deletedTimers_.end()) { - sortedTimers_.erase(sortedTimers_.begin()); - deletedTimers_.erase(timer->id); - continue; - } - auto now = now_ms(); - // timer has reached its time, fire it and keep going - if (timer->dueTime <= now) { - sortedTimers_.erase(sortedTimers_.begin()); - auto result = write(fd_[1], &timer->id, sizeof(int)); - if (result == -1 && errno == EAGAIN) { - isBufferFull = true; - while (!stopped && deletedTimers_.find(timer->id) != deletedTimers_.end() && - write(fd_[1], &timer->id, sizeof(int)) == -1 && errno == EAGAIN) { - bufferFull.wait(lk); - } - } else if (isBufferFull.load() && - (sortedTimers_.empty() || sortedTimers_.at(0)->dueTime > now)) { - // we had a successful write and the next timer is not due - // mark the buffer as free to re-enable the setTimeout with 0 optimization - isBufferFull = false; - } - } else { - taskReady.wait_for(lk, std::chrono::milliseconds((int) (timer->dueTime - now))); - } - } else { - taskReady.wait(lk); +void Timers::FireTimer() { + if (stopped_ || env_ == nullptr) { + return; + } + + NapiScope scope(env_); + + if (sortedTimers_.empty()) { + return; // leftover token + } + auto ref = sortedTimers_.front(); + if (ref.dueTime > now_ms()) { + return; // front not due yet → leftover token + } + sortedTimers_.erase(sortedTimers_.begin()); + + auto it = timerMap_.find(ref.id); + if (it == timerMap_.end()) { + return; + } + auto task = it->second; + task->queued_ = false; + nesting = task->nestingLevel_; + + if (task->repeats_) { + // Follow chromium's non-drifting interval scheduling: anchor the next + // fire to the ideal dueTime rather than "now". + task->startTime_ = task->dueTime_; + addTask(task); + } + + napi_value cb = napi_util::get_ref_value(env_, task->callback_); + napi_value recv = napi_util::get_ref_value(env_, task->thisArg); + size_t argc = task->args_ == nullptr ? 0 : task->args_->size(); + napi_status status; + if (argc > 0) { + std::vector argv(argc); + for (size_t i = 0; i < argc; i++) { + argv[i] = napi_util::get_ref_value(env_, task->args_->at(i)); + } + NAPI_GUARD(napi_call_function(env_, recv, cb, argc, argv.data(), nullptr)) {} + } else { + NAPI_GUARD(napi_call_function(env_, recv, cb, 0, nullptr, nullptr)) {} + } + + // task is not queued, so it's either a setTimeout or a cleared setInterval: + // remove it (which releases its JS references via Unschedule). + if (!task->queued_) { + removeTask(task); + } + + nesting = 0; + + // A pending exception left on the env fails every subsequent napi call + // (NAPI_PREAMBLE returns napi_pending_exception), which would silently stop + // all timer dispatch. Clear it and surface it to Java instead — cleanup + // above must run first so the task's references are released. + bool pendingException = false; + napi_is_exception_pending(env_, &pendingException); + if (pendingException) { + napi_value error = nullptr; + NAPI_GUARD(napi_get_and_clear_last_exception(env_, &error)) {} + if (error != nullptr) { + throw NativeScriptException(env_, error, "Error in timer callback"); } } } void Timers::Destroy() { - { - std::lock_guard lock(mutex); - if (stopped) { - return; - } - stopped = true; + if (stopped_) { + return; + } + stopped_ = true; + + if (handler_ != nullptr) { + JEnv jEnv; + jEnv.CallVoidMethod(handler_, TIMER_HANDLER_RELEASE); + jEnv.DeleteGlobalRef(handler_); + handler_ = nullptr; + } + + // Release any references still held by pending tasks. + for (auto &entry: timerMap_) { + entry.second->Unschedule(); } - bufferFull.notify_one(); - taskReady.notify_all(); - watcher_.join(); - auto mainLooper = Runtime::GetMainLooper(); - ALooper_removeFd(mainLooper, fd_[0]); - close(fd_[0]); timerMap_.clear(); - ALooper_release(looper_); + sortedTimers_.clear(); + env_ = nullptr; } Timers::~Timers() { @@ -205,8 +252,11 @@ napi_value Timers::SetIntervalCallback(napi_env env, napi_callback_info info) { napi_value Timers::ClearTimer(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; - void *data; - napi_get_cb_info(env, info, &argc, args, nullptr, &data); + void *data = nullptr; + napi_status status; + NAPI_GUARD(napi_get_cb_info(env, info, &argc, args, nullptr, &data)) { + return nullptr; + } int id = -1; if (argc > 0) { @@ -222,7 +272,7 @@ napi_value Timers::ClearTimer(napi_env env, napi_callback_info info) { } napi_value Timers::SetTimer(napi_env env, napi_callback_info info, bool repeatable) { - NAPI_CALLBACK_BEGIN_VARGS() + NAPI_CALLBACK_BEGIN_VARGS_FAST(8) auto thiz = reinterpret_cast(data); @@ -231,7 +281,9 @@ napi_value Timers::SetTimer(napi_env env, napi_callback_info info, bool repeatab if (!napi_util::is_of_type(env, argv[0], napi_function)) { napi_value result; - napi_get_undefined(env, &result); + NAPI_GUARD(napi_get_undefined(env, &result)) { + return nullptr; + } return result; } @@ -259,76 +311,31 @@ napi_value Timers::SetTimer(napi_env env, napi_callback_info info, bool repeatab thiz->addTask(task); } napi_value result; - napi_create_int32(env, id, &result); - return result; -} - -/** - * ALooper callback. - * Responsible for checking if the callback is still scheduled and entering the isolate to trigger it - */ -int Timers::PumpTimerLoopCallback(int fd, int events, void *data) { - int timerId; - read(fd, &timerId, sizeof(int)); - - auto thiz = static_cast(data); - auto env = thiz->env_; - if (thiz->stopped || env == nullptr) { - return 0; + NAPI_GUARD(napi_create_int32(env, id, &result)) { + return nullptr; } - - auto it = thiz->timerMap_.find(timerId); - if (it != thiz->timerMap_.end()) { - NapiScope scope(env); - auto task = it->second; - // task is no longer in queue to be executed - task->queued_ = false; - thiz->nesting = task->nestingLevel_; - if (task->repeats_) { - // the reason we're doing this in kind of a convoluted way is to follow more closely the chromium implementation than the node implementation - // imagine an interval of 1000ms - // node's setInterval drifts slightly (1000, 2001, 3001, 4002, some busy work 5050, 6050) - // chromium will be consistent: (1000, 2001, 3000, 4000, some busy work 5050, 6000) - task->startTime_ = task->dueTime_; - thiz->addTask(task); - } - - - napi_value cb = napi_util::get_ref_value(env, task->callback_); - size_t argc = task->args_ == nullptr ? 0 : task->args_->size(); - if (argc > 0) { - std::vector argv(argc); - for (size_t i = 0; i < argc; i++) { - argv[i] = napi_util::get_ref_value(env, task->args_->at(i)); - } - napi_call_function(env, napi_util::get_ref_value(env, task->thisArg), cb, argc, - argv.data(), nullptr); - } else { - napi_call_function(env, napi_util::get_ref_value(env, task->thisArg), cb, 0, nullptr, - nullptr); - } - - // task is not queued, so it's either a setTimeout or a cleared setInterval - // ensure we remove it - if (!task->queued_) { - thiz->removeTask(task); - if (argc > 0) { - for (size_t i = 0; i < argc; i++) { - napi_delete_reference(env, task->args_->at(i)); - } - } - napi_delete_reference(env, task->thisArg); - } - - - thiz->nesting = 0; - } - thiz->bufferFull.notify_one(); - return 1; + return result; } void Timers::InitStatic(napi_env env, napi_value global) { auto timers = new Timers(); timers->Init(env, global); +} -} \ No newline at end of file +// Reverse native for com.tns.TimerHandler.nativeFireTimer (bound by symbol name). +extern "C" JNIEXPORT void JNICALL +Java_com_tns_TimerHandler_nativeFireTimer(JNIEnv* env, jclass clazz, jlong timersPtr) { + try { + reinterpret_cast(timersPtr)->FireTimer(); + } catch (NativeScriptException& e) { + e.ReThrowToJava(nullptr); + } catch (std::exception& e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToJava(nullptr); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToJava(nullptr); + } +} diff --git a/test-app/runtime/src/main/cpp/runtime/timers/Timers.h b/test-app/runtime/src/main/cpp/runtime/timers/Timers.h index 2e399b32..214e7883 100644 --- a/test-app/runtime/src/main/cpp/runtime/timers/Timers.h +++ b/test-app/runtime/src/main/cpp/runtime/timers/Timers.h @@ -1,11 +1,9 @@ #ifndef TEST_APP_TIMERS_H #define TEST_APP_TIMERS_H -#include +#include #include "js_native_api.h" #include "ObjectManager.h" -#include "condition_variable" -#include "thread" #include "robin_hood.h" namespace tns { @@ -36,8 +34,24 @@ namespace tns { return startTime_ + frequency_ * (div.quot + 1); } + // Releases the JS references held by this task. Requires a live napi_env, + // so it is called from the runtime thread (FireTimer / removeTask). inline void Unschedule() { + if (env_ != nullptr) { + if (callback_ != nullptr) { + napi_delete_reference(env_, callback_); + } + if (thisArg != nullptr) { + napi_delete_reference(env_, thisArg); + } + if (args_ != nullptr) { + for (auto ref: *args_) { + napi_delete_reference(env_, ref); + } + } + } callback_ = nullptr; + thisArg = nullptr; args_.reset(); env_ = nullptr; queued_ = false; @@ -53,7 +67,6 @@ namespace tns { * this helper parameter is used in the following way: * task scheduled means queued_ = true * this is set to false right before the callback is executed - * if this is false then it's not on the background thread queue */ bool queued_ = false; double frequency_ = 0; @@ -71,26 +84,26 @@ namespace tns { public: /** * Initializes the global functions setTimeout, setInterval, clearTimeout and clearInterval - * also creates helper threads and binds the timers to the executing thread + * and binds a Java TimerHandler to the executing thread's Looper. * @param env target environment - * @param globalObjectTemplate global template + * @param global global object */ void Init(napi_env env, napi_value global); static void InitStatic(napi_env env, napi_value global); /** - * Disposes the timers. This will clear all references and stop all thread. - * MUST be called in the same thread Init was called - * This methods blocks until the threads are stopped. - * This method doesn't need to be called most of the time as it's called on object destruction - * Reusing this class is not advised + * Fires the earliest-due timer. Invoked from Java (TimerHandler.handleMessage) + * on the runtime thread, once per posted "due token". */ - void Destroy(); + void FireTimer(); /** - * Calls Destruct + * Disposes the timers, releasing all references and the Java handler. + * MUST be called on the thread Init was called on. Idempotent. */ + void Destroy(); + ~Timers(); private: @@ -102,34 +115,33 @@ namespace tns { static napi_value ClearTimer(napi_env env, napi_callback_info info); - void threadLoop(); - - static int PumpTimerLoopCallback(int fd, int events, void *data); - void addTask(const std::shared_ptr& task); void removeTask(const std::shared_ptr &task); void removeTask(const int &taskId); + // Enqueues one "due token" on the Java MessageQueue for this task. Due-now + // timers post at (long)now so they tie (FIFO) with a same-ms postDelayed(0); + // future timers post at ceil(dueTime) so they never fire early. + void postTimer(const std::shared_ptr &task, double now); + napi_env env_ = nullptr; - ALooper *looper_; int currentTimerId = 0; int nesting = 0; // stores the map of timer tasks robin_hood::unordered_map> timerMap_; - std::vector> sortedTimers_; - // sets are faster than vector iteration - // so we use this to avoid redundant isolate locks and we don't care about the - // background thread lost cycles - std::set deletedTimers_; - int fd_[2]; - std::atomic_bool isBufferFull = ATOMIC_VAR_INIT(false); - std::condition_variable taskReady; - std::condition_variable bufferFull; - std::mutex mutex; - std::thread watcher_; - bool stopped = false; + // sorted by exact (sub-millisecond) dueTime; touched only on the runtime thread + std::vector sortedTimers_; + // global ref to the com.tns.TimerHandler bound to this thread's Looper + jobject handler_ = nullptr; + bool stopped_ = false; + + // Cached (process-wide) TimerHandler JNI ids. + static jclass TIMER_HANDLER_CLASS; + static jmethodID TIMER_HANDLER_CTOR; + static jmethodID TIMER_HANDLER_POST; + static jmethodID TIMER_HANDLER_RELEASE; }; } diff --git a/test-app/runtime/src/main/cpp/runtime/version/Version.h b/test-app/runtime/src/main/cpp/runtime/version/Version.h index 64e7312e..10226599 100644 --- a/test-app/runtime/src/main/cpp/runtime/version/Version.h +++ b/test-app/runtime/src/main/cpp/runtime/version/Version.h @@ -1,7 +1,7 @@ #ifndef VERSION_H #define VERSION_H -#define NATIVE_SCRIPT_RUNTIME_VERSION "8.8.5" +#define NATIVE_SCRIPT_RUNTIME_VERSION "9.0.0" #define NATIVE_SCRIPT_RUNTIME_COMMIT_SHA "no commit sha was provided by build.gradle build" #endif //VERSION_H \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/runtime/weakref/WeakRef.cpp b/test-app/runtime/src/main/cpp/runtime/weakref/WeakRef.cpp index 3d15512e..e2c63d6b 100644 --- a/test-app/runtime/src/main/cpp/runtime/weakref/WeakRef.cpp +++ b/test-app/runtime/src/main/cpp/runtime/weakref/WeakRef.cpp @@ -8,72 +8,100 @@ using namespace tns; WeakRef::WeakRef(napi_env env, napi_value value) : env_(env), ref_(nullptr) { - napi_create_reference(env, value, 1, &ref_); + napi_status status; + NAPI_GUARD(napi_create_reference(env, value, 1, &ref_)) {} } WeakRef::~WeakRef() { if (ref_ != nullptr) { - napi_delete_reference(env_, ref_); + napi_status status; + NAPI_GUARD(napi_delete_reference(env_, ref_)) {} } } void WeakRef::Init(napi_env env) { + napi_status status; napi_value global; - napi_get_global(env, &global); + NAPI_GUARD(napi_get_global(env, &global)) { + return; + } napi_property_descriptor properties[] = { { "get", 0, Deref, 0, 0, 0, napi_default, 0 }, { "deref", 0, Deref, 0, 0, 0, napi_default, 0 } }; napi_value wr; - napi_get_named_property(env, global, "WeakRef", &wr); + NAPI_GUARD(napi_get_named_property(env, global, "WeakRef", &wr)) { + return; + } if (napi_util::is_null_or_undefined(env, wr)) { napi_value cons; - napi_define_class(env, "WeakRef", NAPI_AUTO_LENGTH, New, nullptr, 2, properties, &cons); - napi_set_named_property(env, global, "WeakRef", cons); + NAPI_GUARD(napi_define_class(env, "WeakRef", NAPI_AUTO_LENGTH, New, nullptr, 2, properties, &cons)) { + return; + } + NAPI_GUARD(napi_set_named_property(env, global, "WeakRef", cons)) {} } } napi_value WeakRef::New(napi_env env, napi_callback_info info) { + napi_status status; napi_value target; - napi_get_new_target(env, info, &target); + // Leave a pending exception before bailing so `new WeakRef(...)` throws + // instead of silently evaluating to undefined. + NAPI_GUARD(napi_get_new_target(env, info, &target)) { + napi_throw_error(env, nullptr, "Failed to construct WeakRef."); + return nullptr; + } bool is_constructor = target != nullptr; if (!is_constructor) { - napi_throw_error(env, nullptr, "WeakRef must be called as a constructor"); + NAPI_GUARD(napi_throw_error(env, nullptr, "WeakRef must be called as a constructor")) {} return nullptr; } size_t arg_len; - napi_get_cb_info(env, info, &arg_len, nullptr, nullptr, nullptr); + NAPI_GUARD(napi_get_cb_info(env, info, &arg_len, nullptr, nullptr, nullptr)) { + napi_throw_error(env, nullptr, "Failed to construct WeakRef."); + return nullptr; + } if (arg_len != 1) { - napi_throw_error(env, nullptr, "WeakRef constructor must be called with one argument"); + NAPI_GUARD(napi_throw_error(env, nullptr, "WeakRef constructor must be called with one argument")) {} return nullptr; } size_t argc = 1; napi_value args[1]; napi_value jsThis; - napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr); + NAPI_GUARD(napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr)) { + napi_throw_error(env, nullptr, "Failed to construct WeakRef."); + return nullptr; + } auto obj = new WeakRef(env, args[0]); - napi_wrap(env, jsThis, reinterpret_cast(obj), [](napi_env env, void* data, void* hint) { + NAPI_GUARD(napi_wrap(env, jsThis, reinterpret_cast(obj), [](napi_env env, void* data, void* hint) { delete reinterpret_cast(data); - }, nullptr, nullptr); + }, nullptr, nullptr)) {} return jsThis; } napi_value WeakRef::Deref(napi_env env, napi_callback_info info) { + napi_status status; napi_value jsThis; - napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + NAPI_GUARD(napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr)) { + return nullptr; + } WeakRef* obj; - napi_unwrap(env, jsThis, reinterpret_cast(&obj)); + NAPI_GUARD(napi_unwrap(env, jsThis, reinterpret_cast(&obj))) { + return nullptr; + } napi_value result; - napi_get_reference_value(env, obj->ref_, &result); + NAPI_GUARD(napi_get_reference_value(env, obj->ref_, &result)) { + return nullptr; + } return result; } diff --git a/test-app/runtime/src/main/cpp/runtime/workers/ConcurrentQueue.cpp b/test-app/runtime/src/main/cpp/runtime/workers/ConcurrentQueue.cpp new file mode 100644 index 00000000..9c68a7ce --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/ConcurrentQueue.cpp @@ -0,0 +1,98 @@ +#include "ConcurrentQueue.h" + +#include +#include + +#include +#include + +#include "NativeScriptAssert.h" + +namespace tns { + +void ConcurrentQueue::Initialize(ALooper* looper, ALooper_callbackFunc performWork, + void* data) { + std::unique_lock lock(initializationMutex_); + if (terminated_) { + return; + } + + int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (fd == -1) { + DEBUG_WRITE_FORCE("ConcurrentQueue: eventfd failed: %s", strerror(errno)); + return; + } + + if (ALooper_addFd(looper, fd, ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, + performWork, data) != 1) { + DEBUG_WRITE_FORCE("ConcurrentQueue: ALooper_addFd failed"); + close(fd); + return; + } + + this->looper_ = looper; + ALooper_acquire(this->looper_); + this->fd_ = fd; +} + +void ConcurrentQueue::Push(std::shared_ptr message) { + // The lifecycle lock is held across the enqueue + wakeup so a concurrent + // Terminate() can never leave a message stranded in a queue nothing will + // ever drain. + std::unique_lock lock(initializationMutex_); + if (terminated_) { + // the consumer is gone - drop the message + return; + } + + { + std::unique_lock mlock(this->mutex_); + this->messagesQueue_.push(message); + } + + if (this->fd_ != -1) { + // The eventfd counter coalesces multiple signals into one wakeup, + // which is fine because the drain callback uses PopAll(). + uint64_t value = 1; + write(this->fd_, &value, sizeof(value)); + } +} + +std::vector> ConcurrentQueue::PopAll() { + std::unique_lock mlock(this->mutex_); + std::vector> messages; + + while (!this->messagesQueue_.empty()) { + messages.push_back(this->messagesQueue_.front()); + this->messagesQueue_.pop(); + } + + return messages; +} + +void ConcurrentQueue::Terminate() { + // Must run on the looper's own thread: removing an fd concurrently with an + // in-flight callback dispatch is racy. + std::unique_lock lock(initializationMutex_); + terminated_ = true; + + if (this->fd_ != -1) { + ALooper_removeFd(this->looper_, this->fd_); + close(this->fd_); + this->fd_ = -1; + } + + if (this->looper_ != nullptr) { + ALooper_release(this->looper_); + this->looper_ = nullptr; + } + + // Release anything a racing Push() enqueued before it observed terminated_. + { + std::unique_lock mlock(this->mutex_); + std::queue> empty; + this->messagesQueue_.swap(empty); + } +} + +} // namespace tns diff --git a/test-app/runtime/src/main/cpp/runtime/workers/ConcurrentQueue.h b/test-app/runtime/src/main/cpp/runtime/workers/ConcurrentQueue.h new file mode 100644 index 00000000..148d3d9a --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/ConcurrentQueue.h @@ -0,0 +1,37 @@ +#ifndef CONCURRENTQUEUE_H_ +#define CONCURRENTQUEUE_H_ + +#include +#include +#include +#include +#include + +#include "WorkerMessage.h" + +namespace tns { + +/* + * Thread-safe message inbox attached to an ALooper. + * Push() may be called from any thread; Initialize()/PopAll()/Terminate() must + * be called on the looper's thread. + */ +struct ConcurrentQueue { +public: + void Initialize(ALooper* looper, ALooper_callbackFunc performWork, void* data); + void Push(std::shared_ptr message); + std::vector> PopAll(); + void Terminate(); + +private: + std::queue> messagesQueue_; + ALooper* looper_ = nullptr; + int fd_ = -1; + bool terminated_ = false; + std::mutex mutex_; + std::mutex initializationMutex_; +}; + +} // namespace tns + +#endif /* CONCURRENTQUEUE_H_ */ diff --git a/test-app/runtime/src/main/cpp/runtime/workers/LooperTasks.cpp b/test-app/runtime/src/main/cpp/runtime/workers/LooperTasks.cpp new file mode 100644 index 00000000..0040d03b --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/LooperTasks.cpp @@ -0,0 +1,100 @@ +#include "LooperTasks.h" + +#include +#include + +#include +#include +#include + +#include "NativeScriptAssert.h" +#include "NativeScriptException.h" + +namespace tns { + +void LooperTasks::Initialize(ALooper* looper) { + std::lock_guard lock(mutex_); + + int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (fd == -1) { + DEBUG_WRITE_FORCE("LooperTasks: eventfd failed: %s", strerror(errno)); + return; + } + + if (ALooper_addFd(looper, fd, ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, + LooperTasks::TasksReadyCallback, this) != 1) { + DEBUG_WRITE_FORCE("LooperTasks: ALooper_addFd failed"); + close(fd); + return; + } + + looper_ = looper; + ALooper_acquire(looper_); + fd_ = fd; +} + +void LooperTasks::Post(std::function task) { + std::lock_guard lock(mutex_); + if (terminated_) { + // The owning runtime is shutting down (or gone) - drop the task. + return; + } + + tasks_.push(std::move(task)); + + if (fd_ != -1) { + uint64_t value = 1; + write(fd_, &value, sizeof(value)); + } +} + +void LooperTasks::Terminate() { + // Must run on the looper's own thread. + std::lock_guard lock(mutex_); + terminated_ = true; + + if (fd_ != -1) { + ALooper_removeFd(looper_, fd_); + close(fd_); + fd_ = -1; + } + + if (looper_ != nullptr) { + ALooper_release(looper_); + looper_ = nullptr; + } +} + +int LooperTasks::TasksReadyCallback(int fd, int events, void* data) { + uint64_t value; + read(fd, &value, sizeof(value)); + + static_cast(data)->Drain(); + return 1; +} + +void LooperTasks::Drain() { + std::vector> tasks; + { + std::lock_guard lock(mutex_); + while (!tasks_.empty()) { + tasks.push_back(std::move(tasks_.front())); + tasks_.pop(); + } + } + + for (auto& task : tasks) { + // A C++ exception must never propagate out of an ALooper callback. + try { + task(); + } catch (NativeScriptException& ex) { + ex.ReThrowToJava(nullptr); + } catch (std::exception& ex) { + DEBUG_WRITE_FORCE("Error: c++ exception in looper task: %s", ex.what()); + } catch (...) { + DEBUG_WRITE_FORCE("Error: unknown c++ exception in looper task!"); + } + } +} + +} // namespace tns diff --git a/test-app/runtime/src/main/cpp/runtime/workers/LooperTasks.h b/test-app/runtime/src/main/cpp/runtime/workers/LooperTasks.h new file mode 100644 index 00000000..2c1394fb --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/LooperTasks.h @@ -0,0 +1,40 @@ +#ifndef LOOPERTASKS_H_ +#define LOOPERTASKS_H_ + +#include +#include +#include +#include + +namespace tns { + +/* + * A task queue bound to one runtime's looper. Each Runtime (main or worker) + * owns one; child workers post their outbound messages, errors and cleanup + * notifications onto their parent runtime's queue. + * + * Post() may be called from any thread; tasks posted after Terminate() are + * dropped. Initialize()/Terminate() must run on the looper's own thread. + * Held via shared_ptr by the owning Runtime and via weak_ptr by child + * WorkerWrappers, so a child posting to an already-destroyed parent is safe. + */ +class LooperTasks { +public: + void Initialize(ALooper* looper); + void Post(std::function task); + void Terminate(); + +private: + static int TasksReadyCallback(int fd, int events, void* data); + void Drain(); + + std::mutex mutex_; + std::queue> tasks_; + ALooper* looper_ = nullptr; + int fd_ = -1; + bool terminated_ = false; +}; + +} // namespace tns + +#endif /* LOOPERTASKS_H_ */ diff --git a/test-app/runtime/src/main/cpp/runtime/workers/WorkerMessage.h b/test-app/runtime/src/main/cpp/runtime/workers/WorkerMessage.h new file mode 100644 index 00000000..3d262f86 --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/WorkerMessage.h @@ -0,0 +1,53 @@ +#ifndef WORKER_MESSAGE_H_ +#define WORKER_MESSAGE_H_ + +#include + +namespace tns { +namespace worker { + +// A worker message carried across the C++ inbox/task rails. +// +// This napi (multi-engine) port uses JSON string payloads (matching the fork's +// existing worker semantics) rather than V8's structured clone. Data messages +// hold a JSON string; Error messages carry the fields the parent's onerror +// handler needs. +enum class MessageKind { Data, Error }; + +struct Message { + MessageKind kind = MessageKind::Data; + + // Data payload (JSON string produced by JsonStringifyObject / consumed by JSON.parse). + std::string data; + + // Error fields (kind == Error). + std::string errorMessage; + std::string errorStackTrace; + std::string errorFilename; + int errorLineNo = 0; + + Message() = default; + + static Message MakeData(std::string json) { + Message m; + m.kind = MessageKind::Data; + m.data = std::move(json); + return m; + } + + static Message MakeError(std::string message, std::string stackTrace, + std::string filename, int lineNo) { + Message m; + m.kind = MessageKind::Error; + m.errorMessage = std::move(message); + m.errorStackTrace = std::move(stackTrace); + m.errorFilename = std::move(filename); + m.errorLineNo = lineNo; + return m; + } +}; + +} // namespace worker +} // namespace tns + +#endif /* WORKER_MESSAGE_H_ */ diff --git a/test-app/runtime/src/main/cpp/runtime/workers/WorkerWrapper.cpp b/test-app/runtime/src/main/cpp/runtime/workers/WorkerWrapper.cpp new file mode 100644 index 00000000..21fa72e0 --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/WorkerWrapper.cpp @@ -0,0 +1,692 @@ +#include "WorkerWrapper.h" + +#include +#include +#include + +#include +#include + +#include "ArgConverter.h" +#include "CallbackHandlers.h" +#include "GlobalHelpers.h" +#include "JEnv.h" +#include "JniLocalRef.h" +#include "LooperTasks.h" +#include "NativeScriptAssert.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include "jsr.h" +#include "native_api_util.h" + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) +#include "JsV8InspectorClient.h" +#include "WorkerInspectorClient.h" +#endif + +namespace tns { + +WorkerWrapper::WorkerWrapper(napi_env parentEnv, int workerId, std::string workerPath, + std::string callingDir, int priority, napi_value workerObject) + : parentEnv_(parentEnv), + // runs on the parent's thread, where the parent runtime is alive + parentTasks_(Runtime::GetRuntime(parentEnv)->GetLooperTasks()), + workerEnv_(nullptr), + runtime_(nullptr), + workerId_(workerId), + workerPath_(std::move(workerPath)), + callingDir_(std::move(callingDir)), + // workerPath_ (not workerPath) - the parameter was just moved from + threadName_("W" + std::to_string(workerId) + ": " + workerPath_), + priority_(priority), + poWorker_(nullptr), + isClosing_(false), + isTerminating_(false), + isDisposed_(false), + javaLooperRef_(nullptr) { + napi_status status; + NAPI_GUARD(napi_create_reference(parentEnv, workerObject, 1, &poWorker_)) {} +} + +void WorkerWrapper::Start() { + auto self = shared_from_this(); + std::thread thread([self]() { + self->BackgroundLooper(self); + }); + thread.detach(); +} + +void WorkerWrapper::PostMessage(std::shared_ptr message) { + if (!isTerminating_ && !isClosing_) { + queue_.Push(message); + } +} + +void WorkerWrapper::PostMessageToParent(std::shared_ptr message) { + if (isTerminating_) { + return; + } + + auto parentTasks = parentTasks_.lock(); + if (parentTasks == nullptr) { + // the parent runtime is gone (e.g. a parent worker that shut down) + return; + } + + int workerId = workerId_; + parentTasks->Post([workerId, message]() { + WorkerWrapper::FireMessageOnParentWorkerObject(workerId, message); + }); +} + +void WorkerWrapper::Terminate() { + if (isClosing_ || isDisposed_) { + // The worker is already shutting down on its own; nothing to do. + return; + } + + bool wasTerminating = isTerminating_.exchange(true); + if (wasTerminating) { + return; + } + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) + { + // A worker paused at a breakpoint sits in the inspector's nested pause + // loop, not in Looper.loop() - kick it loose so the cooperative looper + // quit below can take effect. + std::lock_guard lock(inspectorMutex_); + if (inspector_ != nullptr) { + inspector_->NotifyTerminating(); + } + } +#endif + + // Cooperative: there is no cross-thread "interrupt running JS" primitive in + // napi (v8::TerminateExecution has no equivalent), so we simply quit the + // worker's Java looper. Any JS already running finishes its current turn; + // once the callback unwinds, Looper.loop() returns and the thread shuts + // down. A worker stuck in a synchronous busy-loop will NOT be preempted. + QuitLooper(); +} + +void WorkerWrapper::Close() { + bool wasClosing = isClosing_.exchange(true); + if (wasClosing) { + return; + } + + // Once the current callback unwinds, Looper.loop() returns and the + // thread proceeds to cleanup. Pending messages are dropped, matching the + // previous front-of-queue TerminateAndCloseThread behavior. + QuitLooper(); +} + +void WorkerWrapper::QuitLooper() { + std::lock_guard lock(looperMutex_); + if (javaLooperRef_ != nullptr) { + JEnv env; + env.CallVoidMethod(javaLooperRef_, LOOPER_QUIT_METHOD_ID); + } +} + +int WorkerWrapper::DrainCallback(int fd, int events, void* data) { + uint64_t value; + read(fd, &value, sizeof(value)); + + auto wrapper = static_cast(data); + wrapper->DrainPendingTasks(); + return 1; +} + +void WorkerWrapper::DrainPendingTasks() { + napi_env env = workerEnv_.load(); + if (env == nullptr || isTerminating_) { + return; + } + + auto messages = queue_.PopAll(); + if (messages.empty()) { + return; + } + + NapiScope scope(env); + napi_status status; + napi_value globalObject; + NAPI_GUARD(napi_get_global(env, &globalObject)) { + return; + } + + for (auto& message : messages) { + if (isTerminating_ || isClosing_) { + break; + } + + napi_value callback; + NAPI_GUARD(napi_get_named_property(env, globalObject, "onmessage", &callback)) {} + if (!napi_util::is_of_type(env, callback, napi_function)) { + DEBUG_WRITE( + "WORKER: couldn't fire a worker's `onmessage` callback because it isn't implemented!"); + continue; + } + + napi_value event; + NAPI_GUARD(napi_create_object(env, &event)) {} + napi_value data = tns::JsonParseString(env, message->data); + if (!napi_util::is_null_or_undefined(env, data)) { + NAPI_GUARD(napi_set_named_property(env, event, "data", data)) {} + } + + napi_value args[1] = {event}; + napi_value result; + status = napi_call_function(env, globalObject, callback, 1, args, &result); + if (status == napi_pending_exception && !isTerminating_) { + napi_value error; + NAPI_GUARD(napi_get_and_clear_last_exception(env, &error)) {} + CallbackHandlers::CallWorkerScopeOnErrorHandle(env, error); + } + } +} + +void WorkerWrapper::FireMessageOnParentWorkerObject(int workerId, + std::shared_ptr message) { + auto wrapper = WorkerWrapper::GetById(workerId); + if (wrapper == nullptr) { + DEBUG_WRITE("MAIN: no worker instance was found with workerId=%d.", workerId); + return; + } + + napi_env env = wrapper->parentEnv_; + NapiScope scope(env); + + if (wrapper->poWorker_ == nullptr) { + DEBUG_WRITE( + "MAIN: couldn't fire a worker(id=%d) object's `onmessage` callback because the worker has been cleared.", + workerId); + return; + } + + napi_value worker = napi_util::get_ref_value(env, wrapper->poWorker_); + if (napi_util::is_null_or_undefined(env, worker)) { + return; + } + + napi_status status; + napi_value callback; + NAPI_GUARD(napi_get_named_property(env, worker, "onmessage", &callback)) {} + if (!napi_util::is_of_type(env, callback, napi_function)) { + DEBUG_WRITE( + "MAIN: couldn't fire a worker(id=%d) object's `onmessage` callback because it isn't implemented.", + workerId); + return; + } + + napi_value event; + NAPI_GUARD(napi_create_object(env, &event)) {} + napi_value data = tns::JsonParseString(env, message->data); + NAPI_GUARD(napi_set_named_property(env, event, "data", data)) {} + + napi_value args[1] = {event}; + napi_value result; + status = napi_call_function(env, worker, callback, 1, args, &result); + if (status == napi_pending_exception) { + napi_value error; + NAPI_GUARD(napi_get_and_clear_last_exception(env, &error)) {} + // Surface to Java; LooperTasks::Drain wraps this in a try/catch. + throw NativeScriptException(env, error, "Error calling onmessage on Worker object"); + } +} + +void WorkerWrapper::PassUncaughtExceptionFromWorkerToParent(const std::string& message, + const std::string& filename, + const std::string& stackTrace, + int lineno) { + auto parentTasks = parentTasks_.lock(); + if (parentTasks == nullptr) { + // the parent runtime is gone (e.g. a parent worker that shut down) + return; + } + + int workerId = workerId_; + std::string threadName = threadName_; + + parentTasks->Post([workerId, message, filename, stackTrace, lineno, threadName]() { + WorkerWrapper::FireErrorOnParentWorkerObject(workerId, message, stackTrace, filename, + lineno, threadName); + }); +} + +void WorkerWrapper::FireErrorOnParentWorkerObject(int workerId, const std::string& message, + const std::string& stackTrace, + const std::string& filename, int lineno, + const std::string& threadName) { + auto wrapper = WorkerWrapper::GetById(workerId); + if (wrapper == nullptr) { + DEBUG_WRITE("MAIN: no worker instance was found with workerId=%d.", workerId); + return; + } + + napi_env env = wrapper->parentEnv_; + NapiScope scope(env); + + if (wrapper->poWorker_ == nullptr) { + DEBUG_WRITE( + "MAIN: couldn't fire a worker(id=%d) object's `onerror` callback because the worker has been cleared.", + workerId); + return; + } + + napi_value worker = napi_util::get_ref_value(env, wrapper->poWorker_); + if (napi_util::is_null_or_undefined(env, worker)) { + return; + } + + napi_status status; + napi_value callback; + NAPI_GUARD(napi_get_named_property(env, worker, "onerror", &callback)) {} + + if (napi_util::is_of_type(env, callback, napi_function)) { + napi_value errEvent; + napi_value msgValue; + NAPI_GUARD(napi_create_string_utf8(env, message.c_str(), NAPI_AUTO_LENGTH, &msgValue)) {} + napi_value codeValue; + NAPI_GUARD(napi_create_string_utf8(env, "", 0, &codeValue)) {} + NAPI_GUARD(napi_create_error(env, codeValue, msgValue, &errEvent)) {} + + // Combine the worker-side stack trace with the Worker object's captured + // construction stack (main thread), mirroring the old fork behavior. + napi_value mainStackValue; + NAPI_GUARD(napi_get_named_property(env, worker, "__stack__", &mainStackValue)) {} + std::string fullStack = stackTrace; + if (napi_util::is_of_type(env, mainStackValue, napi_string)) { + std::string mainStack = ArgConverter::ConvertToString(env, mainStackValue); + auto nl = mainStack.find_first_of("\n"); + if (nl != std::string::npos) { + fullStack = stackTrace + "\n" + mainStack.substr(nl + 1); + } + } + + napi_value fullStackValue; + NAPI_GUARD(napi_create_string_utf8(env, fullStack.c_str(), fullStack.size(), &fullStackValue)) {} + NAPI_GUARD(napi_set_named_property(env, errEvent, "stack", fullStackValue)) {} + + napi_value args[1] = {errEvent}; + napi_value result; + status = napi_call_function(env, worker, callback, 1, args, &result); + + if (status == napi_pending_exception) { + napi_value exception; + NAPI_GUARD(napi_get_and_clear_last_exception(env, &exception)) {} + throw NativeScriptException(env, exception, "Error calling onerror on Worker object"); + } + + // If the handler returns a truthy value, the exception is handled. + if (!napi_util::is_null_or_undefined(env, result)) { + bool handled = false; + NAPI_GUARD(napi_get_value_bool(env, result, &handled)) {} + if (handled) { + return; + } + } + } + + DEBUG_WRITE( + "Unhandled exception in '%s' thread. file: %s, line %d, message: %s\nStackTrace: %s", + threadName.c_str(), filename.c_str(), lineno, message.c_str(), stackTrace.c_str()); +} + +void WorkerWrapper::BackgroundLooper(std::shared_ptr self) { + JavaVM* jvm = Runtime::GetJVM(); + JNIEnv* jniEnv = nullptr; + + JavaVMAttachArgs attachArgs; + attachArgs.version = JNI_VERSION_1_6; + attachArgs.name = const_cast(threadName_.c_str()); + attachArgs.group = nullptr; + jvm->AttachCurrentThread(&jniEnv, &attachArgs); + + // pthread names are limited to 15 chars + pthread_setname_np(pthread_self(), threadName_.substr(0, 15).c_str()); + + int runtimeId = -1; + + try { + JEnv env; + + // Performs the cgroup/scheduling-policy move in addition to the nice + // value, exactly like the previous Java-side + // Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND) call. + env.CallStaticVoidMethod(PROCESS_CLASS, SET_THREAD_PRIORITY_METHOD_ID, priority_); + + if (!isTerminating_ && !isClosing_) { + // Prepares the Java Looper for this thread and creates the + // per-worker com.tns.Runtime (which creates the worker env on this + // thread via initNativeScript). + JniLocalRef callingDir(env.NewStringUTF(callingDir_.c_str())); + runtimeId = env.CallStaticIntMethod(RUNTIME_CLASS, INIT_WORKER_RUNTIME_METHOD_ID, + workerId_, (jstring) callingDir); + runtime_ = Runtime::GetRuntime(runtimeId); + + { + std::lock_guard lock(looperMutex_); + JniLocalRef looper(env.CallStaticObjectMethod(LOOPER_CLASS, MY_LOOPER_METHOD_ID)); + javaLooperRef_ = env.NewGlobalRef(looper); + } + + // Looper.prepare() ran above, so ALooper_forThread() returns the + // native looper backing the Java one - fds added here are pumped by + // Looper.loop(). + queue_.Initialize(ALooper_forThread(), WorkerWrapper::DrainCallback, this); + + napi_env napiEnv = runtime_->GetNapiEnv(); + workerEnv_.store(napiEnv); + { + std::lock_guard lock(registryMutex_); + envRegistry_[napiEnv] = this; + } + + if (!isTerminating_) { + NapiScope scope(napiEnv); + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) + // Expose this worker to an attached Chrome DevTools frontend + // as a child target, mirroring the iOS runtime. Created before + // the script runs so the worker's scripts are visible to the + // debugger from the start. + CreateInspector(napiEnv); +#endif + + runtime_->RunWorker(workerPath_); + + napi_status status; + bool pending = false; + NAPI_GUARD(napi_is_exception_pending(napiEnv, &pending)) {} + if (pending) { + napi_value error; + NAPI_GUARD(napi_get_and_clear_last_exception(napiEnv, &error)) {} + + std::string emsg; + std::string estack; + if (napi_util::is_of_type(napiEnv, error, napi_object)) { + napi_value m; + napi_value s; + NAPI_GUARD(napi_get_named_property(napiEnv, error, "message", &m)) {} + NAPI_GUARD(napi_get_named_property(napiEnv, error, "stack", &s)) {} + if (napi_util::is_of_type(napiEnv, m, napi_string)) { + emsg = ArgConverter::ConvertToString(napiEnv, m); + } + if (napi_util::is_of_type(napiEnv, s, napi_string)) { + estack = ArgConverter::ConvertToString(napiEnv, s); + } + } else { + napi_value m; + NAPI_GUARD(napi_coerce_to_string(napiEnv, error, &m)) {} + emsg = ArgConverter::ConvertToString(napiEnv, m); + } + + if (!isTerminating_) { + PassUncaughtExceptionFromWorkerToParent(emsg, workerPath_, estack, 0); + } + } + } + + // Deliver messages that were posted before the worker was ready. + DrainPendingTasks(); + + if (!isTerminating_ && !isClosing_) { + // Blocks, pumping Java Handler messages (cross-thread Java->JS + // calls), timers and the worker inbox until quit() is called. + env.CallStaticVoidMethod(RUNTIME_CLASS, RUN_WORKER_LOOP_METHOD_ID); + } + } + } catch (NativeScriptException& ex) { + if (jniEnv->ExceptionCheck()) { + jniEnv->ExceptionClear(); + } + if (!isTerminating_) { + PassUncaughtExceptionFromWorkerToParent(std::string(ex.what()), workerPath_, "", 0); + } + } catch (std::exception& ex) { + DEBUG_WRITE_FORCE("Worker(id=%d) error: c++ exception: %s", workerId_, ex.what()); + } catch (...) { + DEBUG_WRITE_FORCE("Worker(id=%d) error: unknown c++ exception!", workerId_); + } + + // ----- Shutdown (close, terminate or bootstrap failure) ----- + + isTerminating_ = true; + + // Terminate any workers this worker created (nested workers). Their Worker + // object refs live in this env, so they must be released before the env is + // disposed below. Each child cascades to its own children during shutdown. + { + napi_env napiEnv = workerEnv_.load(); + if (napiEnv != nullptr) { + TerminateChildren(napiEnv); + } + } + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) + // The inspector must be gone before the Runtime (and with it the env/isolate) + // is disposed below; unregistering also tells DevTools the target is gone. + DestroyInspector(); +#endif + + // On this thread: safe to unregister the inbox fd from the looper. + queue_.Terminate(); + + if (runtime_ != nullptr) { + napi_env napiEnv = workerEnv_.load(); + + try { + // Java-side detach (GcListener.unsubscribe + runtimeCache.remove) + // must happen before the env is disposed. + JEnv env; + env.CallStaticVoidMethod(RUNTIME_CLASS, DETACH_WORKER_RUNTIME_METHOD_ID, runtimeId); + } catch (NativeScriptException& ex) { + if (jniEnv->ExceptionCheck()) { + jniEnv->ExceptionClear(); + } + DEBUG_WRITE_FORCE("Worker(id=%d) error while detaching Java runtime: %s", workerId_, + ex.what()); + } + + { + std::lock_guard lock(registryMutex_); + if (napiEnv != nullptr) { + envRegistry_.erase(napiEnv); + } + } + + workerEnv_.store(nullptr); + + // Enters the engine scope, tears down the runtime and frees the env, + // then deletes the Runtime (which frees the underlying engine runtime). + Runtime::DisposeWorkerRuntime(runtime_); + runtime_ = nullptr; + } + + { + std::lock_guard lock(looperMutex_); + if (javaLooperRef_ != nullptr) { + jniEnv->DeleteGlobalRef(javaLooperRef_); + javaLooperRef_ = nullptr; + } + } + + isDisposed_ = true; + + // Notify the parent thread so the Worker object's ref and the registry + // entry are released (no-op if terminate() or the parent's own shutdown + // already cleared them). + if (auto parentTasks = parentTasks_.lock()) { + int workerId = workerId_; + parentTasks->Post([workerId]() { + WorkerWrapper::ClearWorkerOnParent(workerId); + }); + } + + // ART aborts if a native thread exits while still attached. This must be + // the very last JNI-touching action on this thread. + jvm->DetachCurrentThread(); +} + +int WorkerWrapper::NextWorkerId() { + return nextWorkerId_.fetch_add(1, std::memory_order_relaxed) + 1; +} + +std::shared_ptr WorkerWrapper::GetById(int workerId) { + std::lock_guard lock(registryMutex_); + auto it = registry_.find(workerId); + return it != registry_.end() ? it->second : nullptr; +} + +void WorkerWrapper::Insert(int workerId, std::shared_ptr wrapper) { + std::lock_guard lock(registryMutex_); + registry_.emplace(workerId, std::move(wrapper)); +} + +void WorkerWrapper::ClearWorkerOnParent(int workerId) { + std::shared_ptr wrapper; + { + std::lock_guard lock(registryMutex_); + auto it = registry_.find(workerId); + if (it == registry_.end()) { + return; + } + wrapper = it->second; + registry_.erase(it); + } + + if (wrapper->poWorker_ != nullptr) { + NapiScope scope(wrapper->parentEnv_); + napi_status status; + NAPI_GUARD(napi_delete_reference(wrapper->parentEnv_, wrapper->poWorker_)) {} + wrapper->poWorker_ = nullptr; + } +} + +void WorkerWrapper::TerminateChildren(napi_env parentEnv) { + std::vector> children; + { + std::lock_guard lock(registryMutex_); + for (auto& entry : registry_) { + if (entry.second->parentEnv_ == parentEnv) { + children.push_back(entry.second); + } + } + } + + for (auto& child : children) { + DEBUG_WRITE("Terminating nested worker(id=%d) because its parent is shutting down", + child->workerId_); + child->Terminate(); + ClearWorkerOnParent(child->workerId_); + } +} + +WorkerWrapper* WorkerWrapper::FromEnv(napi_env env) { + std::lock_guard lock(registryMutex_); + auto it = envRegistry_.find(env); + return it != envRegistry_.end() ? it->second : nullptr; +} + +void WorkerWrapper::EnsureJniCached() { + if (RUNTIME_CLASS != nullptr) { + return; + } + + JEnv env; + + RUNTIME_CLASS = env.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); + INIT_WORKER_RUNTIME_METHOD_ID = + env.GetStaticMethodID(RUNTIME_CLASS, "initWorkerRuntime", "(ILjava/lang/String;)I"); + RUN_WORKER_LOOP_METHOD_ID = env.GetStaticMethodID(RUNTIME_CLASS, "runWorkerLoop", "()V"); + DETACH_WORKER_RUNTIME_METHOD_ID = + env.GetStaticMethodID(RUNTIME_CLASS, "detachWorkerRuntime", "(I)V"); + + LOOPER_CLASS = env.FindClass("android/os/Looper"); + assert(LOOPER_CLASS != nullptr); + MY_LOOPER_METHOD_ID = env.GetStaticMethodID(LOOPER_CLASS, "myLooper", "()Landroid/os/Looper;"); + LOOPER_QUIT_METHOD_ID = env.GetMethodID(LOOPER_CLASS, "quit", "()V"); + + PROCESS_CLASS = env.FindClass("android/os/Process"); + assert(PROCESS_CLASS != nullptr); + SET_THREAD_PRIORITY_METHOD_ID = + env.GetStaticMethodID(PROCESS_CLASS, "setThreadPriority", "(I)V"); +} + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) +void WorkerWrapper::CreateInspector(napi_env env) { + // Only when the root inspector client exists (debuggable app, created on + // the main thread during runtime init) - never construct it from here. + JsV8InspectorClient* root = JsV8InspectorClient::GetInstanceIfCreated(); + if (root == nullptr) { + return; + } + + // Same url scheme the module loader reports in Debugger.scriptParsed. + // workerPath_ may still be relative to the caller's dir at this point + // (resolution happens in require); callingDir_ ends with '/'. + std::string url = + "file://" + (workerPath_[0] == '/' ? workerPath_ : callingDir_ + workerPath_); + + auto* client = new WorkerInspectorClient(workerId_, env, ALooper_forThread(), url); + { + std::lock_guard lock(inspectorMutex_); + inspector_ = client; + } + + // Register only once fully constructed: registration makes the client + // reachable from the socket thread. + root->RegisterWorkerTarget(workerId_, client); +} + +void WorkerWrapper::DestroyInspector() { + WorkerInspectorClient* client = nullptr; + { + std::lock_guard lock(inspectorMutex_); + client = inspector_; + inspector_ = nullptr; + } + + if (client == nullptr) { + return; + } + + // Unregister first: after this returns no other thread can reach the + // client through the root's registry. + JsV8InspectorClient* root = JsV8InspectorClient::GetInstanceIfCreated(); + if (root != nullptr) { + root->UnregisterWorkerTarget(workerId_); + } + + delete client; +} + +void WorkerWrapper::ConsoleLog(v8_inspector::ConsoleAPIType method, + const std::vector>& args) { + std::lock_guard lock(inspectorMutex_); + if (inspector_ != nullptr) { + inspector_->consoleLog(method, args); + } +} +#endif + +std::mutex WorkerWrapper::registryMutex_; +std::map> WorkerWrapper::registry_; +std::map WorkerWrapper::envRegistry_; +std::atomic_int WorkerWrapper::nextWorkerId_(0); + +jclass WorkerWrapper::RUNTIME_CLASS = nullptr; +jclass WorkerWrapper::LOOPER_CLASS = nullptr; +jclass WorkerWrapper::PROCESS_CLASS = nullptr; +jmethodID WorkerWrapper::INIT_WORKER_RUNTIME_METHOD_ID = nullptr; +jmethodID WorkerWrapper::RUN_WORKER_LOOP_METHOD_ID = nullptr; +jmethodID WorkerWrapper::DETACH_WORKER_RUNTIME_METHOD_ID = nullptr; +jmethodID WorkerWrapper::MY_LOOPER_METHOD_ID = nullptr; +jmethodID WorkerWrapper::LOOPER_QUIT_METHOD_ID = nullptr; +jmethodID WorkerWrapper::SET_THREAD_PRIORITY_METHOD_ID = nullptr; + +} // namespace tns diff --git a/test-app/runtime/src/main/cpp/runtime/workers/WorkerWrapper.h b/test-app/runtime/src/main/cpp/runtime/workers/WorkerWrapper.h new file mode 100644 index 00000000..29fc7195 --- /dev/null +++ b/test-app/runtime/src/main/cpp/runtime/workers/WorkerWrapper.h @@ -0,0 +1,216 @@ +#ifndef WORKERWRAPPER_H_ +#define WORKERWRAPPER_H_ + +#include + +#include +#include +#include +#include +#include + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) +#include + +#include "v8.h" +#include +#endif + +#include "ConcurrentQueue.h" +#include "WorkerMessage.h" +#include "js_native_api.h" + +namespace tns { + +class LooperTasks; +class Runtime; +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) +class WorkerInspectorClient; +#endif + +/* + * Owns a worker's native thread and its lifecycle, mirroring the iOS + * runtime's WorkerWrapper. The thread is a std::thread that attaches to the + * JVM, prepares a Java Looper (so worker/plugin code can keep using Android + * Handlers) and then drives that single looper, which pumps both Java + * messages and the worker's C++ inbox (ConcurrentQueue + eventfd). + * + * This is the napi (multi-engine) port of the old V8 WorkerWrapper. It uses: + * - napi_env instead of v8::Isolate* + * - JSON string payloads (worker::Message) instead of V8 ValueSerializer + * - a cooperative Terminate() (quit the looper) instead of + * v8::TerminateExecution (which has no napi equivalent) + * + * Messaging: + * - parent -> worker: queue_ + eventfd wakeup on the worker looper + * - worker -> parent: the parent runtime's LooperTasks queue + * + * The parent may be the main thread or another worker (nested workers); a + * worker's children are terminated when the worker itself shuts down. + */ +class WorkerWrapper : public std::enable_shared_from_this { +public: + WorkerWrapper(napi_env parentEnv, int workerId, std::string workerPath, + std::string callingDir, int priority, napi_value workerObject); + + int WorkerId() const { return workerId_; } + bool IsTerminating() const { return isTerminating_; } + bool IsClosing() const { return isClosing_; } + bool IsDisposed() const { return isDisposed_; } + + /* + * Spawns the (detached) worker thread. The thread holds a shared_ptr to + * this wrapper, keeping it alive until the thread fully shuts down. + */ + void Start(); + + /* + * parent -> worker. Queues a JSON message and wakes the worker looper. + * Messages posted before the worker finishes bootstrapping are drained + * right after the worker script runs. + */ + void PostMessage(std::shared_ptr message); + + /* + * worker -> parent. Posts a task that fires the Worker object's + * `onmessage` on the parent runtime's thread. + */ + void PostMessageToParent(std::shared_ptr message); + + /* + * Called on the parent's thread. Cooperatively interrupts the worker by + * quitting its Java looper (no v8::TerminateExecution equivalent exists in + * napi, so a runaway busy-loop in the worker is NOT preempted; the loop + * stops once the current JS callback unwinds). + */ + void Terminate(); + + /* + * Called on the worker thread (self.close()). Lets the current callback + * unwind, then the looper quits and the thread shuts down gracefully. + */ + void Close(); + + /* + * Posts the worker object's `onerror` invocation to the parent's thread. + * Strings only - must not hold any napi handles from the worker env. + */ + void PassUncaughtExceptionFromWorkerToParent(const std::string& message, + const std::string& filename, + const std::string& stackTrace, + int lineno); + + /* + * Registry of live workers, keyed by workerId. Replaces the old + * CallbackHandlers::id2WorkerMap. Guarded by a mutex because the worker + * shutdown path posts cleanup from the worker thread. + */ + static int NextWorkerId(); + static std::shared_ptr GetById(int workerId); + static void Insert(int workerId, std::shared_ptr wrapper); + + /* + * Parent thread only: resets the Worker object's napi_ref and removes the + * wrapper from the registry. Idempotent. + */ + static void ClearWorkerOnParent(int workerId); + + /* + * Terminates and clears all workers whose parent is the given env. + * Must run on the parent's thread, before the parent env is disposed + * (the children's Worker object refs live in that env). + * Cascades: each child terminates its own children during shutdown. + */ + static void TerminateChildren(napi_env parentEnv); + + /* + * Resolves the wrapper of a worker env via the env->wrapper registry. + * Returns nullptr on the main env. + */ + static WorkerWrapper* FromEnv(napi_env env); + + /* + * Resolves the jclass/jmethodID handles used by the worker thread + * bootstrap, before the first worker starts. The first call is always on + * the main thread (nested workers require a main-thread worker first), + * where class loading is safe. + */ + static void EnsureJniCached(); + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) + /* + * Worker thread (console.* fires on the isolate's own thread). Forwards + * to the worker's inspector so the log reaches DevTools' worker target. + */ + void ConsoleLog(v8_inspector::ConsoleAPIType method, + const std::vector>& args); +#endif + +private: + void BackgroundLooper(std::shared_ptr self); + void DrainPendingTasks(); + void QuitLooper(); + static int DrainCallback(int fd, int events, void* data); + static void FireMessageOnParentWorkerObject(int workerId, + std::shared_ptr message); + static void FireErrorOnParentWorkerObject(int workerId, const std::string& message, + const std::string& stackTrace, + const std::string& filename, int lineno, + const std::string& threadName); + + napi_env parentEnv_; + // The parent runtime's task queue; weak so a child outliving its parent + // just drops its posts instead of touching a dead runtime. + std::weak_ptr parentTasks_; + std::atomic workerEnv_; + Runtime* runtime_; + + const int workerId_; + const std::string workerPath_; + const std::string callingDir_; + const std::string threadName_; + const int priority_; + + napi_ref poWorker_; + + std::atomic_bool isClosing_; + std::atomic_bool isTerminating_; + std::atomic_bool isDisposed_; + + ConcurrentQueue queue_; + + std::mutex looperMutex_; + jobject javaLooperRef_; + +#if defined(__V8__) && defined(APPLICATION_IN_DEBUG) + /* + * Expose this worker to an attached Chrome DevTools frontend as a child + * target. Created on the worker thread before the script runs, destroyed + * on the worker thread before the env/isolate is disposed. + */ + void CreateInspector(napi_env env); + void DestroyInspector(); + + WorkerInspectorClient* inspector_ = nullptr; + std::mutex inspectorMutex_; +#endif + + static std::mutex registryMutex_; + static std::map> registry_; + static std::map envRegistry_; + static std::atomic_int nextWorkerId_; + + static jclass RUNTIME_CLASS; + static jclass LOOPER_CLASS; + static jclass PROCESS_CLASS; + static jmethodID INIT_WORKER_RUNTIME_METHOD_ID; + static jmethodID RUN_WORKER_LOOP_METHOD_ID; + static jmethodID DETACH_WORKER_RUNTIME_METHOD_ID; + static jmethodID MY_LOOPER_METHOD_ID; + static jmethodID LOOPER_QUIT_METHOD_ID; + static jmethodID SET_THREAD_PRIORITY_METHOD_ID; +}; + +} // namespace tns + +#endif /* WORKERWRAPPER_H_ */ diff --git a/test-app/runtime/src/main/cpp/zip/include/zip.h b/test-app/runtime/src/main/cpp/zip/include/zip.h index d898c409..c2a8a8b4 100644 --- a/test-app/runtime/src/main/cpp/zip/include/zip.h +++ b/test-app/runtime/src/main/cpp/zip/include/zip.h @@ -3,10 +3,10 @@ /* zip.h -- exported declarations. - Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. - The authors can be contacted at + The authors can be contacted at Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -34,288 +34,495 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if defined(__has_feature) + #if !__has_feature(nullability) + #define _Nullable + #define _Nonnull + #endif +#else + #define _Nullable + #define _Nonnull +#endif +#ifdef __cplusplus +extern "C" { +#if 0 +} /* fix autoindent */ +#endif +#endif + +#include #ifndef ZIP_EXTERN +#ifndef ZIP_STATIC #ifdef _WIN32 #define ZIP_EXTERN __declspec(dllimport) #elif defined(__GNUC__) && __GNUC__ >= 4 -#define ZIP_EXTERN __attribute__ ((visibility ("default"))) +#define ZIP_EXTERN __attribute__((visibility("default"))) #else #define ZIP_EXTERN #endif +#else +#define ZIP_EXTERN #endif - -#ifdef __cplusplus -extern "C" { #endif -#include +#ifndef ZIP_DEPRECATED +#if defined(__GNUC__) || defined(__clang__) +#define ZIP_DEPRECATED(x) __attribute__((deprecated(x))) +#elif defined(_MSC_VER) +#define ZIP_DEPRECATED(x) __declspec(deprecated(x)) +#else +#define ZIP_DEPRECATED(x) +#endif +#endif -#include #include +#include #include /* flags for zip_open */ -#define ZIP_CREATE 1 -#define ZIP_EXCL 2 -#define ZIP_CHECKCONS 4 -#define ZIP_TRUNCATE 8 +#define ZIP_CREATE 1 +#define ZIP_EXCL 2 +#define ZIP_CHECKCONS 4 +#define ZIP_TRUNCATE 8 +#define ZIP_RDONLY 16 /* flags for zip_name_locate, zip_fopen, zip_stat, ... */ -#define ZIP_FL_NOCASE 1u /* ignore case on name lookup */ -#define ZIP_FL_NODIR 2u /* ignore directory component */ -#define ZIP_FL_COMPRESSED 4u /* read compressed data */ -#define ZIP_FL_UNCHANGED 8u /* use original data, ignoring changes */ -#define ZIP_FL_RECOMPRESS 16u /* force recompression of data */ -#define ZIP_FL_ENCRYPTED 32u /* read encrypted data (implies ZIP_FL_COMPRESSED) */ -#define ZIP_FL_ENC_GUESS 0u /* guess string encoding (is default) */ -#define ZIP_FL_ENC_RAW 64u /* get unmodified string */ -#define ZIP_FL_ENC_STRICT 128u /* follow specification strictly */ -#define ZIP_FL_LOCAL 256u /* in local header */ -#define ZIP_FL_CENTRAL 512u /* in central directory */ +#define ZIP_FL_NOCASE 1u /* ignore case on name lookup */ +#define ZIP_FL_NODIR 2u /* ignore directory component */ +#define ZIP_FL_COMPRESSED 4u /* read compressed data */ +#define ZIP_FL_UNCHANGED 8u /* use original data, ignoring changes */ +/* 16u was ZIP_FL_RECOMPRESS, which is deprecated */ +#define ZIP_FL_ENCRYPTED 32u /* read encrypted data (implies ZIP_FL_COMPRESSED) */ +#define ZIP_FL_ENC_GUESS 0u /* guess string encoding (is default) */ +#define ZIP_FL_ENC_RAW 64u /* get unmodified string */ +#define ZIP_FL_ENC_STRICT 128u /* follow specification strictly */ +#define ZIP_FL_LOCAL 256u /* in local header */ +#define ZIP_FL_CENTRAL 512u /* in central directory */ /* 1024u reserved for internal use */ -#define ZIP_FL_ENC_UTF_8 2048u /* string is UTF-8 encoded */ -#define ZIP_FL_ENC_CP437 4096u /* string is CP437 encoded */ -#define ZIP_FL_OVERWRITE 8192u /* zip_file_add: if file with name exists, overwrite (replace) it */ +#define ZIP_FL_ENC_UTF_8 2048u /* string is UTF-8 encoded */ +#define ZIP_FL_ENC_CP437 4096u /* string is CP437 encoded */ +#define ZIP_FL_OVERWRITE 8192u /* zip_file_add: if file with name exists, overwrite (replace) it */ /* archive global flags flags */ -#define ZIP_AFL_TORRENT 1u /* torrent zipped */ -#define ZIP_AFL_RDONLY 2u /* read only -- cannot be cleared */ +#define ZIP_AFL_RDONLY 2u /* read only -- cannot be cleared */ +#define ZIP_AFL_IS_TORRENTZIP 4u /* current archive is torrentzipped */ +#define ZIP_AFL_WANT_TORRENTZIP 8u /* write archive in torrentzip format */ +#define ZIP_AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE 16u /* don't remove file if archive is empty */ /* create a new extra field */ -#define ZIP_EXTRA_FIELD_ALL ZIP_UINT16_MAX -#define ZIP_EXTRA_FIELD_NEW ZIP_UINT16_MAX - -/* flags for compression and encryption sources */ +#define ZIP_EXTRA_FIELD_ALL ZIP_UINT16_MAX +#define ZIP_EXTRA_FIELD_NEW ZIP_UINT16_MAX -#define ZIP_CODEC_DECODE 0 /* decompress/decrypt (encode flag not set) */ -#define ZIP_CODEC_ENCODE 1 /* compress/encrypt */ +/* length parameter to various functions */ +#define ZIP_LENGTH_TO_END 0 +#define ZIP_LENGTH_UNCHECKED (-2) /* only supported by zip_source_file and its variants */ /* libzip error codes */ -#define ZIP_ER_OK 0 /* N No error */ -#define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ -#define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ -#define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ -#define ZIP_ER_SEEK 4 /* S Seek error */ -#define ZIP_ER_READ 5 /* S Read error */ -#define ZIP_ER_WRITE 6 /* S Write error */ -#define ZIP_ER_CRC 7 /* N CRC error */ -#define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ -#define ZIP_ER_NOENT 9 /* N No such file */ -#define ZIP_ER_EXISTS 10 /* N File already exists */ -#define ZIP_ER_OPEN 11 /* S Can't open file */ -#define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ -#define ZIP_ER_ZLIB 13 /* Z Zlib error */ -#define ZIP_ER_MEMORY 14 /* N Malloc failure */ -#define ZIP_ER_CHANGED 15 /* N Entry has been changed */ -#define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ -#define ZIP_ER_EOF 17 /* N Premature EOF */ -#define ZIP_ER_INVAL 18 /* N Invalid argument */ -#define ZIP_ER_NOZIP 19 /* N Not a zip archive */ -#define ZIP_ER_INTERNAL 20 /* N Internal error */ -#define ZIP_ER_INCONS 21 /* N Zip archive inconsistent */ -#define ZIP_ER_REMOVE 22 /* S Can't remove file */ -#define ZIP_ER_DELETED 23 /* N Entry has been deleted */ -#define ZIP_ER_ENCRNOTSUPP 24 /* N Encryption method not supported */ -#define ZIP_ER_RDONLY 25 /* N Read-only archive */ -#define ZIP_ER_NOPASSWD 26 /* N No password provided */ -#define ZIP_ER_WRONGPASSWD 27 /* N Wrong password provided */ +#define ZIP_ER_OK 0 /* N No error */ +#define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ +#define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ +#define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ +#define ZIP_ER_SEEK 4 /* S Seek error */ +#define ZIP_ER_READ 5 /* S Read error */ +#define ZIP_ER_WRITE 6 /* S Write error */ +#define ZIP_ER_CRC 7 /* N CRC error */ +#define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ +#define ZIP_ER_NOENT 9 /* N No such file */ +#define ZIP_ER_EXISTS 10 /* N File already exists */ +#define ZIP_ER_OPEN 11 /* S Can't open file */ +#define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ +#define ZIP_ER_ZLIB 13 /* Z Zlib error */ +#define ZIP_ER_MEMORY 14 /* N Malloc failure */ +#define ZIP_ER_CHANGED 15 /* N Entry has been changed */ +#define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ +#define ZIP_ER_EOF 17 /* N Premature end of file */ +#define ZIP_ER_INVAL 18 /* N Invalid argument */ +#define ZIP_ER_NOZIP 19 /* N Not a zip archive */ +#define ZIP_ER_INTERNAL 20 /* N Internal error */ +#define ZIP_ER_INCONS 21 /* L Zip archive inconsistent */ +#define ZIP_ER_REMOVE 22 /* S Can't remove file */ +#define ZIP_ER_DELETED 23 /* N Entry has been deleted */ +#define ZIP_ER_ENCRNOTSUPP 24 /* N Encryption method not supported */ +#define ZIP_ER_RDONLY 25 /* N Read-only archive */ +#define ZIP_ER_NOPASSWD 26 /* N No password provided */ +#define ZIP_ER_WRONGPASSWD 27 /* N Wrong password provided */ +#define ZIP_ER_OPNOTSUPP 28 /* N Operation not supported */ +#define ZIP_ER_INUSE 29 /* N Resource still in use */ +#define ZIP_ER_TELL 30 /* S Tell error */ +#define ZIP_ER_COMPRESSED_DATA 31 /* N Compressed data invalid */ +#define ZIP_ER_CANCELLED 32 /* N Operation cancelled */ +#define ZIP_ER_DATA_LENGTH 33 /* N Unexpected length of data */ +#define ZIP_ER_NOT_ALLOWED 34 /* N Not allowed in torrentzip */ +#define ZIP_ER_TRUNCATED_ZIP 35 /* N Possibly truncated or corrupted zip archive */ /* type of system error value */ -#define ZIP_ET_NONE 0 /* sys_err unused */ -#define ZIP_ET_SYS 1 /* sys_err is errno */ -#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ +#define ZIP_ET_NONE 0 /* sys_err unused */ +#define ZIP_ET_SYS 1 /* sys_err is errno */ +#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ +#define ZIP_ET_LIBZIP 3 /* sys_err is libzip error code */ /* compression methods */ -#define ZIP_CM_DEFAULT -1 /* better of deflate or store */ -#define ZIP_CM_STORE 0 /* stored (uncompressed) */ -#define ZIP_CM_SHRINK 1 /* shrunk */ -#define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ -#define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ -#define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ -#define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ -#define ZIP_CM_IMPLODE 6 /* imploded */ +#define ZIP_CM_DEFAULT -1 /* better of deflate or store */ +#define ZIP_CM_STORE 0 /* stored (uncompressed) */ +#define ZIP_CM_SHRINK 1 /* shrunk */ +#define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ +#define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ +#define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ +#define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ +#define ZIP_CM_IMPLODE 6 /* imploded */ /* 7 - Reserved for Tokenizing compression algorithm */ -#define ZIP_CM_DEFLATE 8 /* deflated */ -#define ZIP_CM_DEFLATE64 9 /* deflate64 */ -#define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ +#define ZIP_CM_DEFLATE 8 /* deflated */ +#define ZIP_CM_DEFLATE64 9 /* deflate64 */ +#define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ /* 11 - Reserved by PKWARE */ -#define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ +#define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ /* 13 - Reserved by PKWARE */ -#define ZIP_CM_LZMA 14 /* LZMA (EFS) */ +#define ZIP_CM_LZMA 14 /* LZMA (EFS) */ /* 15-17 - Reserved by PKWARE */ -#define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ -#define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ -#define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ -#define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ +#define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ +#define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ +/* 20 - old value for Zstandard */ +#define ZIP_CM_LZMA2 33 +#define ZIP_CM_ZSTD 93 /* Zstandard compressed data */ +#define ZIP_CM_XZ 95 /* XZ compressed data */ +#define ZIP_CM_JPEG 96 /* Compressed Jpeg data */ +#define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ +#define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ /* encryption methods */ -#define ZIP_EM_NONE 0 /* not encrypted */ -#define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ -#if 0 /* Strong Encryption Header not parsed yet */ -#define ZIP_EM_DES 0x6601 /* strong encryption: DES */ -#define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ -#define ZIP_EM_3DES_168 0x6603 -#define ZIP_EM_3DES_112 0x6609 -#define ZIP_EM_AES_128 0x660e -#define ZIP_EM_AES_192 0x660f -#define ZIP_EM_AES_256 0x6610 -#define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ -#define ZIP_EM_RC4 0x6801 +#define ZIP_EM_NONE 0 /* not encrypted */ +#define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ +#if 0 /* Strong Encryption Header not parsed yet */ +#define ZIP_EM_DES 0x6601 /* strong encryption: DES */ +#define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ +#define ZIP_EM_3DES_168 0x6603 +#define ZIP_EM_3DES_112 0x6609 +#define ZIP_EM_PKZIP_AES_128 0x660e +#define ZIP_EM_PKZIP_AES_192 0x660f +#define ZIP_EM_PKZIP_AES_256 0x6610 +#define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ +#define ZIP_EM_RC4 0x6801 #endif -#define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ - -#define ZIP_OPSYS_DOS 0x00u -#define ZIP_OPSYS_AMIGA 0x01u -#define ZIP_OPSYS_OPENVMS 0x02u -#define ZIP_OPSYS_UNIX 0x03u -#define ZIP_OPSYS_VM_CMS 0x04u -#define ZIP_OPSYS_ATARI_ST 0x05u -#define ZIP_OPSYS_OS_2 0x06u -#define ZIP_OPSYS_MACINTOSH 0x07u -#define ZIP_OPSYS_Z_SYSTEM 0x08u -#define ZIP_OPSYS_CPM 0x09u -#define ZIP_OPSYS_WINDOWS_NTFS 0x0au -#define ZIP_OPSYS_MVS 0x0bu -#define ZIP_OPSYS_VSE 0x0cu -#define ZIP_OPSYS_ACORN_RISC 0x0du -#define ZIP_OPSYS_VFAT 0x0eu -#define ZIP_OPSYS_ALTERNATE_MVS 0x0fu -#define ZIP_OPSYS_BEOS 0x10u -#define ZIP_OPSYS_TANDEM 0x11u -#define ZIP_OPSYS_OS_400 0x12u -#define ZIP_OPSYS_OS_X 0x13u - -#define ZIP_OPSYS_DEFAULT ZIP_OPSYS_UNIX - +#define ZIP_EM_AES_128 0x0101 /* Winzip AES encryption */ +#define ZIP_EM_AES_192 0x0102 +#define ZIP_EM_AES_256 0x0103 +#define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ + +#define ZIP_OPSYS_DOS 0x00u +#define ZIP_OPSYS_AMIGA 0x01u +#define ZIP_OPSYS_OPENVMS 0x02u +#define ZIP_OPSYS_UNIX 0x03u +#define ZIP_OPSYS_VM_CMS 0x04u +#define ZIP_OPSYS_ATARI_ST 0x05u +#define ZIP_OPSYS_OS_2 0x06u +#define ZIP_OPSYS_MACINTOSH 0x07u +#define ZIP_OPSYS_Z_SYSTEM 0x08u +#define ZIP_OPSYS_CPM 0x09u +#define ZIP_OPSYS_WINDOWS_NTFS 0x0au +#define ZIP_OPSYS_MVS 0x0bu +#define ZIP_OPSYS_VSE 0x0cu +#define ZIP_OPSYS_ACORN_RISC 0x0du +#define ZIP_OPSYS_VFAT 0x0eu +#define ZIP_OPSYS_ALTERNATE_MVS 0x0fu +#define ZIP_OPSYS_BEOS 0x10u +#define ZIP_OPSYS_TANDEM 0x11u +#define ZIP_OPSYS_OS_400 0x12u +#define ZIP_OPSYS_OS_X 0x13u + +#define ZIP_OPSYS_DEFAULT ZIP_OPSYS_UNIX enum zip_source_cmd { - ZIP_SOURCE_OPEN, /* prepare for reading */ - ZIP_SOURCE_READ, /* read data */ - ZIP_SOURCE_CLOSE, /* reading is done */ - ZIP_SOURCE_STAT, /* get meta information */ - ZIP_SOURCE_ERROR, /* get error information */ - ZIP_SOURCE_FREE /* cleanup and free resources */ + ZIP_SOURCE_OPEN, /* prepare for reading */ + ZIP_SOURCE_READ, /* read data */ + ZIP_SOURCE_CLOSE, /* reading is done */ + ZIP_SOURCE_STAT, /* get meta information */ + ZIP_SOURCE_ERROR, /* get error information */ + ZIP_SOURCE_FREE, /* cleanup and free resources */ + ZIP_SOURCE_SEEK, /* set position for reading */ + ZIP_SOURCE_TELL, /* get read position */ + ZIP_SOURCE_BEGIN_WRITE, /* prepare for writing */ + ZIP_SOURCE_COMMIT_WRITE, /* writing is done */ + ZIP_SOURCE_ROLLBACK_WRITE, /* discard written changes */ + ZIP_SOURCE_WRITE, /* write data */ + ZIP_SOURCE_SEEK_WRITE, /* set position for writing */ + ZIP_SOURCE_TELL_WRITE, /* get write position */ + ZIP_SOURCE_SUPPORTS, /* check whether source supports command */ + ZIP_SOURCE_REMOVE, /* remove file */ + ZIP_SOURCE_RESERVED_1, /* previously used internally */ + ZIP_SOURCE_BEGIN_WRITE_CLONING, /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */ + ZIP_SOURCE_ACCEPT_EMPTY, /* whether empty files are valid archives */ + ZIP_SOURCE_GET_FILE_ATTRIBUTES, /* get additional file attributes */ + ZIP_SOURCE_SUPPORTS_REOPEN, /* allow reading from changed entry */ + ZIP_SOURCE_GET_DOS_TIME /* get last modification time in DOS format */ }; +typedef enum zip_source_cmd zip_source_cmd_t; + +#define ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd) (((zip_int64_t)1) << (cmd)) -#define ZIP_SOURCE_ERR_LOWER -2 +#define ZIP_SOURCE_CHECK_SUPPORTED(supported, cmd) (((supported) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd)) != 0) -#define ZIP_STAT_NAME 0x0001u -#define ZIP_STAT_INDEX 0x0002u -#define ZIP_STAT_SIZE 0x0004u -#define ZIP_STAT_COMP_SIZE 0x0008u -#define ZIP_STAT_MTIME 0x0010u -#define ZIP_STAT_CRC 0x0020u -#define ZIP_STAT_COMP_METHOD 0x0040u -#define ZIP_STAT_ENCRYPTION_METHOD 0x0080u -#define ZIP_STAT_FLAGS 0x0100u +/* clang-format off */ + +#define ZIP_SOURCE_SUPPORTS_READABLE (ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_OPEN) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_READ) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_CLOSE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_STAT) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ERROR) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_FREE)) + +#define ZIP_SOURCE_SUPPORTS_SEEKABLE (ZIP_SOURCE_SUPPORTS_READABLE \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SUPPORTS)) + +#define ZIP_SOURCE_SUPPORTS_WRITABLE (ZIP_SOURCE_SUPPORTS_SEEKABLE \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_COMMIT_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ROLLBACK_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_REMOVE)) + +/* clang-format on */ + +/* for use by sources */ +struct zip_source_args_seek { + zip_int64_t offset; + int whence; +}; + +typedef struct zip_source_args_seek zip_source_args_seek_t; +#define ZIP_SOURCE_GET_ARGS(type, data, len, error) ((len) < sizeof(type) ? zip_error_set((error), ZIP_ER_INVAL, 0), (type *)NULL : (type *)(data)) + + +/* error information */ +/* use zip_error_*() to access */ +struct zip_error { + int zip_err; /* libzip error code (ZIP_ER_*) */ + int sys_err; /* copy of errno (E*) or zlib error code */ + char *_Nullable str; /* string representation or NULL */ +}; + +#define ZIP_STAT_NAME 0x0001u +#define ZIP_STAT_INDEX 0x0002u +#define ZIP_STAT_SIZE 0x0004u +#define ZIP_STAT_COMP_SIZE 0x0008u +#define ZIP_STAT_MTIME 0x0010u +#define ZIP_STAT_CRC 0x0020u +#define ZIP_STAT_COMP_METHOD 0x0040u +#define ZIP_STAT_ENCRYPTION_METHOD 0x0080u +#define ZIP_STAT_FLAGS 0x0100u struct zip_stat { - zip_uint64_t valid; /* which fields have valid values */ - const char *name; /* name of the file */ - zip_uint64_t index; /* index within archive */ - zip_uint64_t size; /* size of file (uncompressed) */ - zip_uint64_t comp_size; /* size of file (compressed) */ - time_t mtime; /* modification time */ - zip_uint32_t crc; /* crc of file data */ - zip_uint16_t comp_method; /* compression method used */ - zip_uint16_t encryption_method; /* encryption method used */ - zip_uint32_t flags; /* reserved for future use */ + zip_uint64_t valid; /* which fields have valid values */ + const char *_Nullable name; /* name of the file */ + zip_uint64_t index; /* index within archive */ + zip_uint64_t size; /* size of file (uncompressed) */ + zip_uint64_t comp_size; /* size of file (compressed) */ + time_t mtime; /* modification time */ + zip_uint32_t crc; /* crc of file data */ + zip_uint16_t comp_method; /* compression method used */ + zip_uint16_t encryption_method; /* encryption method used */ + zip_uint32_t flags; /* reserved for future use */ }; +struct zip_buffer_fragment { + zip_uint8_t *_Nonnull data; + zip_uint64_t length; +}; + +struct zip_file_attributes { + zip_uint64_t valid; /* which fields have valid values */ + zip_uint8_t version; /* version of this struct, currently 1 */ + zip_uint8_t host_system; /* host system on which file was created */ + zip_uint8_t ascii; /* flag whether file is ASCII text */ + zip_uint8_t version_needed; /* minimum version needed to extract file */ + zip_uint32_t external_file_attributes; /* external file attributes (host-system specific) */ + zip_uint16_t general_purpose_bit_flags; /* general purpose big flags, only some bits are honored */ + zip_uint16_t general_purpose_bit_mask; /* which bits in general_purpose_bit_flags are valid */ +}; + +#define ZIP_FILE_ATTRIBUTES_HOST_SYSTEM 0x0001u +#define ZIP_FILE_ATTRIBUTES_ASCII 0x0002u +#define ZIP_FILE_ATTRIBUTES_VERSION_NEEDED 0x0004u +#define ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES 0x0008u +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS 0x0010u + struct zip; struct zip_file; struct zip_source; -typedef zip_uint32_t zip_flags_t; - -typedef zip_int64_t (*zip_source_callback)(void *, void *, zip_uint64_t, - enum zip_source_cmd); +typedef struct zip zip_t; +typedef struct zip_error zip_error_t; +typedef struct zip_file zip_file_t; +typedef struct zip_file_attributes zip_file_attributes_t; +typedef struct zip_source zip_source_t; +typedef struct zip_stat zip_stat_t; +typedef struct zip_buffer_fragment zip_buffer_fragment_t; +typedef zip_uint32_t zip_flags_t; +typedef zip_int64_t (*zip_source_callback)(void *_Nullable, void *_Nullable, zip_uint64_t, zip_source_cmd_t); +typedef zip_int64_t (*zip_source_layered_callback)(zip_source_t *_Nonnull, void *_Nullable, void *_Nullable, zip_uint64_t, enum zip_source_cmd); +typedef void (*zip_progress_callback)(zip_t *_Nonnull, double, void *_Nullable); +typedef int (*zip_cancel_callback)(zip_t *_Nonnull, void *_Nullable); #ifndef ZIP_DISABLE_DEPRECATED -ZIP_EXTERN zip_int64_t zip_add(struct zip *, const char *, struct zip_source *); /* use zip_file_add */ -ZIP_EXTERN zip_int64_t zip_add_dir(struct zip *, const char *); /* use zip_dir_add */ -ZIP_EXTERN const char *zip_get_file_comment(struct zip *, zip_uint64_t, int *, int); /* use zip_file_get_comment */ -ZIP_EXTERN int zip_get_num_files(struct zip *); /* use zip_get_num_entries instead */ -ZIP_EXTERN int zip_rename(struct zip *, zip_uint64_t, const char *); /* use zip_file_rename */ -ZIP_EXTERN int zip_replace(struct zip *, zip_uint64_t, struct zip_source *); /* use zip_file_replace */ -ZIP_EXTERN int zip_set_file_comment(struct zip *, zip_uint64_t, const char *, int); /* use zip_file_set_comment */ +#define ZIP_FL_RECOMPRESS 16u /* force recompression of data */ + +typedef void (*zip_progress_callback_t)(double); +ZIP_DEPRECATED("use 'zip_register_progress_callback_with_state' instead") ZIP_EXTERN void zip_register_progress_callback(zip_t *_Nonnull, zip_progress_callback_t _Nullable); + +ZIP_DEPRECATED("use 'zip_file_add' instead") ZIP_EXTERN zip_int64_t zip_add(zip_t *_Nonnull, const char *_Nonnull, zip_source_t *_Nonnull); +ZIP_DEPRECATED("use 'zip_dir_add' instead") ZIP_EXTERN zip_int64_t zip_add_dir(zip_t *_Nonnull, const char *_Nonnull); +ZIP_DEPRECATED("use 'zip_file_get_comment' instead") ZIP_EXTERN const char *_Nullable zip_get_file_comment(zip_t *_Nonnull, zip_uint64_t, int *_Nullable, int); +ZIP_DEPRECATED("use 'zip_get_num_entries' instead") ZIP_EXTERN int zip_get_num_files(zip_t *_Nonnull); +ZIP_DEPRECATED("use 'zip_file_rename' instead") ZIP_EXTERN int zip_rename(zip_t *_Nonnull, zip_uint64_t, const char *_Nonnull); +ZIP_DEPRECATED("use 'zip_file_replace' instead") ZIP_EXTERN int zip_replace(zip_t *_Nonnull, zip_uint64_t, zip_source_t *_Nonnull); +ZIP_DEPRECATED("use 'zip_file_set_comment' instead") ZIP_EXTERN int zip_set_file_comment(zip_t *_Nonnull, zip_uint64_t, const char *_Nullable, int); +ZIP_DEPRECATED("use 'zip_error_init_with_code' and 'zip_error_system_type' instead") ZIP_EXTERN int zip_error_get_sys_type(int); +ZIP_DEPRECATED("use 'zip_get_error' instead") ZIP_EXTERN void zip_error_get(zip_t *_Nonnull, int *_Nullable, int *_Nullable); +ZIP_DEPRECATED("use 'zip_error_strerror' instead") ZIP_EXTERN int zip_error_to_str(char *_Nonnull, zip_uint64_t, int, int); +ZIP_DEPRECATED("use 'zip_file_get_error' instead") ZIP_EXTERN void zip_file_error_get(zip_file_t *_Nonnull, int *_Nullable, int *_Nullable); +ZIP_DEPRECATED("use 'zip_source_zip_file' instead") ZIP_EXTERN zip_source_t *_Nullable zip_source_zip(zip_t *_Nonnull, zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t); +ZIP_DEPRECATED("use 'zip_source_zip_file_create' instead") ZIP_EXTERN zip_source_t *_Nullable zip_source_zip_create(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); #endif -ZIP_EXTERN int zip_archive_set_tempdir(struct zip *, const char *); -ZIP_EXTERN int zip_close(struct zip *); -ZIP_EXTERN int zip_delete(struct zip *, zip_uint64_t); -ZIP_EXTERN zip_int64_t zip_dir_add(struct zip *, const char *, zip_flags_t); -ZIP_EXTERN void zip_discard(struct zip *); -ZIP_EXTERN void zip_error_clear(struct zip *); -ZIP_EXTERN void zip_error_get(struct zip *, int *, int *); -ZIP_EXTERN int zip_error_get_sys_type(int); -ZIP_EXTERN int zip_error_to_str(char *, zip_uint64_t, int, int); -ZIP_EXTERN int zip_fclose(struct zip_file *); -ZIP_EXTERN struct zip *zip_fdopen(int, int, int *); -ZIP_EXTERN zip_int64_t zip_file_add(struct zip *, const char *, struct zip_source *, zip_flags_t); -ZIP_EXTERN void zip_file_error_clear(struct zip_file *); -ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *); -ZIP_EXTERN int zip_file_extra_field_delete(struct zip *, zip_uint64_t, zip_uint16_t, zip_flags_t); -ZIP_EXTERN int zip_file_extra_field_delete_by_id(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); -ZIP_EXTERN int zip_file_extra_field_set(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t, const zip_uint8_t *, zip_uint16_t, zip_flags_t); -ZIP_EXTERN zip_int16_t zip_file_extra_fields_count(struct zip *, zip_uint64_t, zip_flags_t); -ZIP_EXTERN zip_int16_t zip_file_extra_fields_count_by_id(struct zip *, zip_uint64_t, zip_uint16_t, zip_flags_t); -ZIP_EXTERN const zip_uint8_t *zip_file_extra_field_get(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t *, zip_uint16_t *, zip_flags_t); -ZIP_EXTERN const zip_uint8_t *zip_file_extra_field_get_by_id(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_uint16_t *, zip_flags_t); -ZIP_EXTERN const char *zip_file_get_comment(struct zip *, zip_uint64_t, zip_uint32_t *, zip_flags_t); -ZIP_EXTERN int zip_file_get_external_attributes(struct zip *, zip_uint64_t, zip_flags_t, zip_uint8_t *, zip_uint32_t *); -ZIP_EXTERN int zip_file_rename(struct zip *, zip_uint64_t, const char *, zip_flags_t); -ZIP_EXTERN int zip_file_replace(struct zip *, zip_uint64_t, struct zip_source *, zip_flags_t); -ZIP_EXTERN int zip_file_set_comment(struct zip *, zip_uint64_t, const char *, zip_uint16_t, zip_flags_t); -ZIP_EXTERN int zip_file_set_external_attributes(struct zip *, zip_uint64_t, zip_flags_t, zip_uint8_t, zip_uint32_t); -ZIP_EXTERN const char *zip_file_strerror(struct zip_file *); -ZIP_EXTERN struct zip_file *zip_fopen(struct zip *, const char *, zip_flags_t); -ZIP_EXTERN struct zip_file *zip_fopen_encrypted(struct zip *, const char *, zip_flags_t, const char *); -ZIP_EXTERN struct zip_file *zip_fopen_index(struct zip *, zip_uint64_t, zip_flags_t); -ZIP_EXTERN struct zip_file *zip_fopen_index_encrypted(struct zip *, zip_uint64_t, zip_flags_t, const char *); -ZIP_EXTERN zip_int64_t zip_fread(struct zip_file *, void *, zip_uint64_t); -ZIP_EXTERN const char *zip_get_archive_comment(struct zip *, int *, zip_flags_t); -ZIP_EXTERN int zip_get_archive_flag(struct zip *, zip_flags_t, zip_flags_t); -ZIP_EXTERN const char *zip_get_name(struct zip *, zip_uint64_t, zip_flags_t); -ZIP_EXTERN zip_int64_t zip_get_num_entries(struct zip *, zip_flags_t); -ZIP_EXTERN zip_int64_t zip_name_locate(struct zip *, const char *, zip_flags_t); -ZIP_EXTERN struct zip *zip_open(const char *, int, int *); -ZIP_EXTERN int zip_set_archive_comment(struct zip *, const char *, zip_uint16_t); -ZIP_EXTERN int zip_set_archive_flag(struct zip *, zip_flags_t, int); -ZIP_EXTERN int zip_set_default_password(struct zip *, const char *); -ZIP_EXTERN int zip_set_file_compression(struct zip *, zip_uint64_t, zip_int32_t, zip_uint32_t); -ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *, zip_uint64_t, int); -ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *, zip_uint64_t, zip_int64_t); -ZIP_EXTERN struct zip_source *zip_source_filep(struct zip *, FILE *, zip_uint64_t, zip_int64_t); -ZIP_EXTERN void zip_source_free(struct zip_source *); -ZIP_EXTERN struct zip_source *zip_source_function(struct zip *, zip_source_callback, void *); -ZIP_EXTERN struct zip_source *zip_source_zip(struct zip *, struct zip *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t); -ZIP_EXTERN int zip_stat(struct zip *, const char *, zip_flags_t, struct zip_stat *); -ZIP_EXTERN int zip_stat_index(struct zip *, zip_uint64_t, zip_flags_t, struct zip_stat *); -ZIP_EXTERN void zip_stat_init(struct zip_stat *); -ZIP_EXTERN const char *zip_strerror(struct zip *); -ZIP_EXTERN int zip_unchange(struct zip *, zip_uint64_t); -ZIP_EXTERN int zip_unchange_all(struct zip *); -ZIP_EXTERN int zip_unchange_archive(struct zip *); +ZIP_EXTERN int zip_close(zip_t *_Nonnull); +ZIP_EXTERN int zip_delete(zip_t *_Nonnull, zip_uint64_t); +ZIP_EXTERN zip_int64_t zip_dir_add(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN void zip_discard(zip_t *_Nonnull); + +ZIP_EXTERN zip_error_t *_Nonnull zip_get_error(zip_t *_Nonnull); +ZIP_EXTERN void zip_error_clear(zip_t *_Nonnull); +ZIP_EXTERN int zip_error_code_zip(const zip_error_t *_Nonnull); +ZIP_EXTERN int zip_error_code_system(const zip_error_t *_Nonnull); +ZIP_EXTERN void zip_error_fini(zip_error_t *_Nonnull); +ZIP_EXTERN void zip_error_init(zip_error_t *_Nonnull); +ZIP_EXTERN void zip_error_init_with_code(zip_error_t *_Nonnull, int); +ZIP_EXTERN void zip_error_set(zip_error_t *_Nullable, int, int); +ZIP_EXTERN void zip_error_set_from_source(zip_error_t *_Nonnull, zip_source_t *_Nullable); +ZIP_EXTERN const char *_Nonnull zip_error_strerror(zip_error_t *_Nonnull); +ZIP_EXTERN int zip_error_system_type(const zip_error_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_error_to_data(const zip_error_t *_Nonnull, void *_Nonnull, zip_uint64_t); + +ZIP_EXTERN int zip_fclose(zip_file_t *_Nonnull); +ZIP_EXTERN zip_t *_Nullable zip_fdopen(int, int, int *_Nullable); +ZIP_EXTERN zip_int64_t zip_file_add(zip_t *_Nonnull, const char *_Nonnull, zip_source_t *_Nonnull, zip_flags_t); +ZIP_EXTERN void zip_file_attributes_init(zip_file_attributes_t *_Nonnull); +ZIP_EXTERN void zip_file_error_clear(zip_file_t *_Nonnull); +ZIP_EXTERN int zip_file_extra_field_delete(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_extra_field_delete_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_extra_field_set(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, const zip_uint8_t *_Nullable, zip_uint16_t, zip_flags_t); +ZIP_EXTERN zip_int16_t zip_file_extra_fields_count(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); +ZIP_EXTERN zip_int16_t zip_file_extra_fields_count_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN const zip_uint8_t *_Nullable zip_file_extra_field_get(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t *_Nullable, zip_uint16_t *_Nullable, zip_flags_t); +ZIP_EXTERN const zip_uint8_t *_Nullable zip_file_extra_field_get_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_uint16_t *_Nullable, zip_flags_t); +ZIP_EXTERN const char *_Nullable zip_file_get_comment(zip_t *_Nonnull, zip_uint64_t, zip_uint32_t *_Nullable, zip_flags_t); +ZIP_EXTERN zip_error_t *_Nonnull zip_file_get_error(zip_file_t *_Nonnull); +ZIP_EXTERN int zip_file_get_external_attributes(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint8_t *_Nullable, zip_uint32_t *_Nullable); +ZIP_EXTERN int zip_file_is_seekable(zip_file_t *_Nonnull); +ZIP_EXTERN int zip_file_rename(zip_t *_Nonnull, zip_uint64_t, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN int zip_file_replace(zip_t *_Nonnull, zip_uint64_t, zip_source_t *_Nonnull, zip_flags_t); +ZIP_EXTERN int zip_file_set_comment(zip_t *_Nonnull, zip_uint64_t, const char *_Nullable, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_set_dostime(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_set_encryption(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, const char *_Nullable); +ZIP_EXTERN int zip_file_set_external_attributes(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint8_t, zip_uint32_t); +ZIP_EXTERN int zip_file_set_mtime(zip_t *_Nonnull, zip_uint64_t, time_t, zip_flags_t); +ZIP_EXTERN const char *_Nonnull zip_file_strerror(zip_file_t *_Nonnull); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen_encrypted(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t, const char *_Nullable); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen_index(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen_index_encrypted(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, const char *_Nullable); +ZIP_EXTERN zip_int64_t zip_fread(zip_file_t *_Nonnull, void *_Nonnull, zip_uint64_t); +ZIP_EXTERN zip_int8_t zip_fseek(zip_file_t *_Nonnull, zip_int64_t, int); +ZIP_EXTERN zip_int64_t zip_ftell(zip_file_t *_Nonnull); +ZIP_EXTERN const char *_Nullable zip_get_archive_comment(zip_t *_Nonnull, int *_Nullable, zip_flags_t); +ZIP_EXTERN int zip_get_archive_flag(zip_t *_Nonnull, zip_flags_t, zip_flags_t); +ZIP_EXTERN const char *_Nullable zip_get_name(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); +ZIP_EXTERN zip_int64_t zip_get_num_entries(zip_t *_Nonnull, zip_flags_t); +ZIP_EXTERN const char *_Nonnull zip_libzip_version(void); +ZIP_EXTERN zip_int64_t zip_name_locate(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN zip_t *_Nullable zip_open(const char *_Nonnull, int, int *_Nullable); +ZIP_EXTERN zip_t *_Nullable zip_open_from_source(zip_source_t *_Nonnull, int, zip_error_t *_Nullable); +ZIP_EXTERN int zip_register_progress_callback_with_state(zip_t *_Nonnull, double, zip_progress_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); +ZIP_EXTERN int zip_register_cancel_callback_with_state(zip_t *_Nonnull, zip_cancel_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); +ZIP_EXTERN int zip_set_archive_comment(zip_t *_Nonnull, const char *_Nullable, zip_uint16_t); +ZIP_EXTERN int zip_set_archive_flag(zip_t *_Nonnull, zip_flags_t, int); +ZIP_EXTERN int zip_set_default_password(zip_t *_Nonnull, const char *_Nullable); +ZIP_EXTERN int zip_set_file_compression(zip_t *_Nonnull, zip_uint64_t, zip_int32_t, zip_uint32_t); +ZIP_EXTERN int zip_source_begin_write(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_begin_write_cloning(zip_source_t *_Nonnull, zip_uint64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer(zip_t *_Nonnull, const void *_Nullable, zip_uint64_t, int); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_create(const void *_Nullable, zip_uint64_t, int, zip_error_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_fragment(zip_t *_Nonnull, const zip_buffer_fragment_t *_Nonnull, zip_uint64_t, int); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_fragment_create(const zip_buffer_fragment_t *_Nullable, zip_uint64_t, int, zip_error_t *_Nullable); +ZIP_EXTERN int zip_source_close(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_commit_write(zip_source_t *_Nonnull); +ZIP_EXTERN zip_error_t *_Nonnull zip_source_error(zip_source_t *_Nonnull); +ZIP_EXTERN zip_source_t *_Nullable zip_source_file(zip_t *_Nonnull, const char *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_file_create(const char *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_filep(zip_t *_Nonnull, FILE *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_filep_create(FILE *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN void zip_source_free(zip_source_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_function(zip_t *_Nonnull, zip_source_callback _Nonnull, void *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_function_create(zip_source_callback _Nonnull, void *_Nullable, zip_error_t *_Nullable); +ZIP_EXTERN int zip_source_get_file_attributes(zip_source_t *_Nonnull, zip_file_attributes_t *_Nonnull); +ZIP_EXTERN int zip_source_is_deleted(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_is_seekable(zip_source_t *_Nonnull); +ZIP_EXTERN void zip_source_keep(zip_source_t *_Nonnull); +ZIP_EXTERN zip_source_t *_Nullable zip_source_layered(zip_t *_Nullable, zip_source_t *_Nonnull, zip_source_layered_callback _Nonnull, void *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_layered_create(zip_source_t *_Nonnull, zip_source_layered_callback _Nonnull, void *_Nullable, zip_error_t *_Nullable); +ZIP_EXTERN zip_int64_t zip_source_make_command_bitmap(zip_source_cmd_t, ...); +ZIP_EXTERN int zip_source_open(zip_source_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_pass_to_lower_layer(zip_source_t *_Nonnull, void *_Nullable, zip_uint64_t, zip_source_cmd_t); +ZIP_EXTERN zip_int64_t zip_source_read(zip_source_t *_Nonnull, void *_Nonnull, zip_uint64_t); +ZIP_EXTERN void zip_source_rollback_write(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_seek(zip_source_t *_Nonnull, zip_int64_t, int); +ZIP_EXTERN zip_int64_t zip_source_seek_compute_offset(zip_uint64_t, zip_uint64_t, void *_Nonnull, zip_uint64_t, zip_error_t *_Nullable); +ZIP_EXTERN int zip_source_seek_write(zip_source_t *_Nonnull, zip_int64_t, int); +ZIP_EXTERN int zip_source_stat(zip_source_t *_Nonnull, zip_stat_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_tell(zip_source_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_tell_write(zip_source_t *_Nonnull); +#ifdef _WIN32 +ZIP_EXTERN zip_source_t *_Nullable zip_source_win32a(zip_t *_Nonnull, const char *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_win32a_create(const char *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_win32handle(zip_t *_Nonnull, void *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_win32handle_create(void *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_win32w(zip_t *_Nonnull, const wchar_t *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_win32w_create(const wchar_t *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +#endif +ZIP_EXTERN zip_source_t *_Nullable zip_source_window_create(zip_source_t *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN zip_int64_t zip_source_write(zip_source_t *_Nonnull, const void *_Nullable, zip_uint64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_zip_file(zip_t *_Nonnull, zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t, const char *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_zip_file_create(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t, const char *_Nullable, zip_error_t *_Nullable); +ZIP_EXTERN int zip_stat(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t, zip_stat_t *_Nonnull); +ZIP_EXTERN int zip_stat_index(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_stat_t *_Nonnull); +ZIP_EXTERN void zip_stat_init(zip_stat_t *_Nonnull); +ZIP_EXTERN const char *_Nonnull zip_strerror(zip_t *_Nonnull); +ZIP_EXTERN int zip_unchange(zip_t *_Nonnull, zip_uint64_t); +ZIP_EXTERN int zip_unchange_all(zip_t *_Nonnull); +ZIP_EXTERN int zip_unchange_archive(zip_t *_Nonnull); +ZIP_EXTERN int zip_compression_method_supported(zip_int32_t method, int compress); +ZIP_EXTERN int zip_encryption_method_supported(zip_uint16_t method, int encode); #ifdef __cplusplus } #endif -#endif /* _HAD_ZIP_H */ \ No newline at end of file +#endif /* _HAD_ZIP_H */ diff --git a/test-app/runtime/src/main/cpp/zip/include/zipconf.h b/test-app/runtime/src/main/cpp/zip/include/zipconf.h index 52b84287..0e20740b 100644 --- a/test-app/runtime/src/main/cpp/zip/include/zipconf.h +++ b/test-app/runtime/src/main/cpp/zip/include/zipconf.h @@ -4,44 +4,45 @@ /* zipconf.h -- platform specific include file - This file was generated automatically by ./make_zipconf.sh - based on ../config.h. + This file was generated automatically by CMake + based on ../cmake-zipconf.h.in. */ -#define LIBZIP_VERSION "0.11" -#define LIBZIP_VERSION_MAJOR 0 +#define LIBZIP_VERSION "1.11.4" +#define LIBZIP_VERSION_MAJOR 1 #define LIBZIP_VERSION_MINOR 11 -#define LIBZIP_VERSION_MICRO 2 +#define LIBZIP_VERSION_MICRO 4 +#define ZIP_STATIC + +#if !defined(__STDC_FORMAT_MACROS) +#define __STDC_FORMAT_MACROS 1 +#endif #include typedef int8_t zip_int8_t; -#define ZIP_INT8_MIN INT8_MIN -#define ZIP_INT8_MAX INT8_MAX - typedef uint8_t zip_uint8_t; -#define ZIP_UINT8_MAX UINT8_MAX - typedef int16_t zip_int16_t; -#define ZIP_INT16_MIN INT16_MIN -#define ZIP_INT16_MAX INT16_MAX - typedef uint16_t zip_uint16_t; -#define ZIP_UINT16_MAX UINT16_MAX - typedef int32_t zip_int32_t; -#define ZIP_INT32_MIN INT32_MIN -#define ZIP_INT32_MAX INT32_MAX - typedef uint32_t zip_uint32_t; -#define ZIP_UINT32_MAX UINT32_MAX - typedef int64_t zip_int64_t; -#define ZIP_INT64_MIN INT64_MIN -#define ZIP_INT64_MAX INT64_MAX - typedef uint64_t zip_uint64_t; -#define ZIP_UINT64_MAX UINT64_MAX +#define ZIP_INT8_MIN (-ZIP_INT8_MAX-1) +#define ZIP_INT8_MAX 0x7f +#define ZIP_UINT8_MAX 0xff + +#define ZIP_INT16_MIN (-ZIP_INT16_MAX-1) +#define ZIP_INT16_MAX 0x7fff +#define ZIP_UINT16_MAX 0xffff + +#define ZIP_INT32_MIN (-ZIP_INT32_MAX-1L) +#define ZIP_INT32_MAX 0x7fffffffL +#define ZIP_UINT32_MAX 0xffffffffLU + +#define ZIP_INT64_MIN (-ZIP_INT64_MAX-1LL) +#define ZIP_INT64_MAX 0x7fffffffffffffffLL +#define ZIP_UINT64_MAX 0xffffffffffffffffULL -#endif /* zipconf.h */ \ No newline at end of file +#endif /* zipconf.h */ diff --git a/test-app/runtime/src/main/java/com/tns/DexFactory.java b/test-app/runtime/src/main/java/com/tns/DexFactory.java index 3622803f..539390b1 100644 --- a/test-app/runtime/src/main/java/com/tns/DexFactory.java +++ b/test-app/runtime/src/main/java/com/tns/DexFactory.java @@ -1,5 +1,6 @@ package com.tns; +import android.os.Build; import android.util.Log; import com.tns.bindings.AnnotationDescriptor; @@ -18,11 +19,17 @@ import java.io.InputStreamReader; import java.io.InvalidClassException; import java.io.OutputStreamWriter; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import dalvik.system.BaseDexClassLoader; import dalvik.system.DexClassLoader; public class DexFactory { @@ -34,15 +41,21 @@ public class DexFactory { private final String dexThumb; private final ClassLoader classLoader; private final ClassStorageService classStorageService; + private final boolean injectIntoParentClassLoader; private ProxyGenerator proxyGenerator; private HashMap> injectedDexClasses = new HashMap>(); DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, String dexThumb, ClassStorageService classStorageService) { + this(logger, classLoader, dexBaseDir, dexThumb, classStorageService, false); + } + + DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, String dexThumb, ClassStorageService classStorageService, boolean injectIntoParentClassLoader) { this.logger = logger; this.classLoader = classLoader; this.dexDir = dexBaseDir; this.dexThumb = dexThumb; + this.injectIntoParentClassLoader = injectIntoParentClassLoader; this.odexDir = new File(this.dexDir, "odex"); this.proxyGenerator = new ProxyGenerator(this.dexDir.getAbsolutePath()); @@ -157,13 +170,34 @@ public Class resolveClass(String baseClassName, String name, String className } jarFile.setReadOnly(); - Class result; - DexClassLoader dexClassLoader = new DexClassLoader(jarFilePath, this.odexDir.getAbsolutePath(), null, classLoader); + Class result = null; + String classNameToLoad = isInterface ? fullClassName : desiredDexClassName; + + if (injectIntoParentClassLoader && classLoader instanceof BaseDexClassLoader) { + // If the proxy class is already loadable through the app class loader, + // reuse it instead of injecting a duplicate dex. Runtime-implemented + // interfaces all share the same generated proxy name (e.g. + // com.tns.gen.java.lang.Runnable), so a second implementation must NOT + // re-inject a jar defining a class already present in the class loader — + // ART rejects duplicate dex files in the same hierarchy. The generic + // proxy dispatches to the right JS object at call time, so reusing it is + // correct. + try { + result = classLoader.loadClass(classNameToLoad); + } catch (ClassNotFoundException notInjectedYet) { + if (injectDexIntoClassLoader((BaseDexClassLoader) classLoader, jarFilePath)) { + try { + result = classLoader.loadClass(classNameToLoad); + } catch (ClassNotFoundException e) { + // fall through to the isolated DexClassLoader below + } + } + } + } - if (isInterface) { - result = dexClassLoader.loadClass(fullClassName); - } else { - result = dexClassLoader.loadClass(desiredDexClassName); + if (result == null) { + DexClassLoader dexClassLoader = new DexClassLoader(jarFilePath, this.odexDir.getAbsolutePath(), null, classLoader); + result = dexClassLoader.loadClass(classNameToLoad); } classStorageService.storeClass(result.getName(), result); @@ -371,4 +405,81 @@ private String getCachedProxyThumb(File proxyDir) { return null; } + + /** + * Injects a DEX jar into the app's PathClassLoader so that classes in it are + * findable by Class.forName(). This is needed because Android framework components + * (e.g. FragmentFactory) use Class.forName() to instantiate classes by name, but + * NativeScript's dynamically-generated classes normally live in isolated DexClassLoaders + * that Class.forName() doesn't search. + * + * The jar must be added through the target class loader's own DexPathList so that + * the resulting DexFile has the PathClassLoader as its only owner. Opening the jar + * through a separate DexClassLoader first and splicing its dex element would leave + * the same DexFile claimed by two loaders, which ART rejects on non-debuggable + * builds with "Attempt to register dex file ... with multiple class loaders". + * + * @return true if the jar was injected and the class can be loaded through the + * target class loader, false if the caller should fall back to an + * isolated DexClassLoader. + */ + private boolean injectDexIntoClassLoader(BaseDexClassLoader targetClassLoader, String jarFilePath) { + try { + if (Build.VERSION.SDK_INT >= 24) { + // BaseDexClassLoader.addDexPath exists since API 24 + Method addDexPath = BaseDexClassLoader.class.getDeclaredMethod("addDexPath", String.class); + addDexPath.setAccessible(true); + addDexPath.invoke(targetClassLoader, jarFilePath); + } else { + appendDexElements(targetClassLoader, jarFilePath); + } + return true; + } catch (Exception e) { + Log.w("JS", "Failed to inject dex into parent classloader: " + e); + return false; + } + } + + /** + * Pre API 24 equivalent of BaseDexClassLoader.addDexPath: builds the dex elements + * through DexPathList's static factory methods (so no temporary class loader is + * involved) and splices them into the target loader's dexElements array. This is + * the same technique MultiDex used on these OS versions. + */ + private void appendDexElements(BaseDexClassLoader targetClassLoader, String jarFilePath) throws Exception { + Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList"); + pathListField.setAccessible(true); + Object pathList = pathListField.get(targetClassLoader); + + ArrayList files = new ArrayList(); + files.add(new File(jarFilePath)); + ArrayList suppressedExceptions = new ArrayList(); + + Object newElements; + if (Build.VERSION.SDK_INT >= 23) { + Method makePathElements = pathList.getClass().getDeclaredMethod("makePathElements", List.class, File.class, List.class); + makePathElements.setAccessible(true); + newElements = makePathElements.invoke(null, files, this.odexDir, suppressedExceptions); + } else { + Method makeDexElements = pathList.getClass().getDeclaredMethod("makeDexElements", ArrayList.class, File.class, ArrayList.class); + makeDexElements.setAccessible(true); + newElements = makeDexElements.invoke(null, files, this.odexDir, suppressedExceptions); + } + + if (!suppressedExceptions.isEmpty()) { + throw suppressedExceptions.get(0); + } + + Field dexElementsField = pathList.getClass().getDeclaredField("dexElements"); + dexElementsField.setAccessible(true); + Object oldElements = dexElementsField.get(pathList); + + int oldLen = Array.getLength(oldElements); + int newLen = Array.getLength(newElements); + Object merged = Array.newInstance(oldElements.getClass().getComponentType(), oldLen + newLen); + System.arraycopy(oldElements, 0, merged, 0, oldLen); + System.arraycopy(newElements, 0, merged, oldLen, newLen); + + dexElementsField.set(pathList, merged); + } } diff --git a/test-app/runtime/src/main/java/com/tns/FinalizerHandler.java b/test-app/runtime/src/main/java/com/tns/FinalizerHandler.java new file mode 100644 index 00000000..2e73a993 --- /dev/null +++ b/test-app/runtime/src/main/java/com/tns/FinalizerHandler.java @@ -0,0 +1,59 @@ +package com.tns; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +/** + * Drains the runtime's deferred-finalizer queue on the runtime thread's message + * loop. + * + * Native finalizers run during the JS engine's GC, where deleting JS references + * (or making any other JS/GC-state-changing call) is unsafe on every engine. + * Such finalizers instead enqueue their cleanup in the native FinalizerQueue and + * call {@link #schedule()}; this handler then drains that queue at the next loop + * tick — a point guaranteed to be outside any GC sweep, with JS unwound to the + * host. This mirrors the engine-specific node_api_post_finalizer but works + * uniformly for every engine the runtime embeds. + */ +final class FinalizerHandler extends Handler { + private static final int MSG_DRAIN = 1; + + private final long nativeQueuePtr; + private boolean released; + + // constructed from native code (FinalizerQueue) on the runtime thread + FinalizerHandler(long nativeQueuePtr) { + super(Looper.myLooper()); + this.nativeQueuePtr = nativeQueuePtr; + } + + // Requests a drain on the next loop tick. Coalesced to at most one in-flight + // message. Safe to call from any thread (Handler messaging is thread-safe), + // which matters because GC finalizers may run on a background JS thread. + @RuntimeCallable + void schedule() { + if (released) { + return; + } + if (!hasMessages(MSG_DRAIN)) { + sendMessage(obtainMessage(MSG_DRAIN)); + } + } + + @RuntimeCallable + void release() { + released = true; + removeCallbacksAndMessages(null); + } + + @Override + public void handleMessage(Message msg) { + if (released || msg.what != MSG_DRAIN) { + return; + } + nativeDrainFinalizers(nativeQueuePtr); + } + + private static native void nativeDrainFinalizers(long nativeQueuePtr); +} diff --git a/test-app/runtime/src/main/java/com/tns/JavaScriptErrorMessage.java b/test-app/runtime/src/main/java/com/tns/JavaScriptErrorMessage.java deleted file mode 100644 index 851bccd0..00000000 --- a/test-app/runtime/src/main/java/com/tns/JavaScriptErrorMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.tns; - -public class JavaScriptErrorMessage { - private String message; - private String stackTrace; - private String filename; - private int lineno; - private String threadName; - - JavaScriptErrorMessage(String message, String stackTrace, String filename, int lineno) { - this.message = message; - this.filename = filename; - this.stackTrace = stackTrace; - this.lineno = lineno; - } - - JavaScriptErrorMessage(String message, String stackTrace, String filename, int lineno, String threadName) { - this(message, stackTrace, filename, lineno); - this.threadName = threadName; - } - - public String getMessage() { - return message; - } - - public String getStackTrace() { - return stackTrace; - } - - public String getFilename() { - return filename; - } - - public int getLineno() { - return lineno; - } - - public String getThreadName() { - return threadName; - } -} diff --git a/test-app/runtime/src/main/java/com/tns/MessageType.java b/test-app/runtime/src/main/java/com/tns/MessageType.java deleted file mode 100644 index efd94e18..00000000 --- a/test-app/runtime/src/main/java/com/tns/MessageType.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.tns; - -/** - * Created by pkanev on 8/30/2016. - */ -public class MessageType { - public static int Handshake = 0; - public static int MainToWorker = 1; - public static int WorkerToMain = 2; - public static int TerminateThread = 4; - public static int CloseWorker = 6; - public static int BubbleUpException = 7; - public static int TerminateAndCloseThread = 8; -} diff --git a/test-app/runtime/src/main/java/com/tns/NativeScriptAbstractMap.java b/test-app/runtime/src/main/java/com/tns/NativeScriptAbstractMap.java index fc7a8d49..3de04826 100644 --- a/test-app/runtime/src/main/java/com/tns/NativeScriptAbstractMap.java +++ b/test-app/runtime/src/main/java/com/tns/NativeScriptAbstractMap.java @@ -49,7 +49,7 @@ public abstract class NativeScriptAbstractMap implements Map { * * @since 1.6 */ - public static class SimpleImmutableEntry implements Map.Entry, Serializable { + public static class SimpleImmutableEntry implements Map.Entry, Serializable { private static final long serialVersionUID = 7138329143949025153L; private final K key; @@ -111,7 +111,7 @@ public String toString() { * * @since 1.6 */ - public static class SimpleEntry implements Map.Entry, Serializable { + public static class SimpleEntry implements Map.Entry, Serializable { private static final long serialVersionUID = -8499721149061103585L; private final K key; diff --git a/test-app/runtime/src/main/java/com/tns/Runtime.java b/test-app/runtime/src/main/java/com/tns/Runtime.java index c9a5c160..961a8541 100644 --- a/test-app/runtime/src/main/java/com/tns/Runtime.java +++ b/test-app/runtime/src/main/java/com/tns/Runtime.java @@ -28,6 +28,9 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Collections; +import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; @@ -43,17 +46,35 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat private native void runModule(int runtimeId, String filePath) throws NativeScriptException; - private native void runWorker(int runtimeId, String filePath) throws NativeScriptException; - private native Object runScript(int runtimeId, String filePath) throws NativeScriptException; private native Object callJSMethodNative(int runtimeId, int javaObjectID, Class claz, String methodName, int retType, boolean isConstructor, Object... packagedArgs) throws NativeScriptException; private native void createJSInstanceNative(int runtimeId, Object javaObject, int javaObjectID, String canonicalName); - private native int generateNewObjectId(int runtimeId); + // Dynamic JNI lookup for @CriticalNative / @FastNative is unimplemented on + // Android 8-10 and buggy on Android 11; it works reliably only on Android 12+ + // (API 31). For 8-11 the annotated methods are bound via RegisterNatives in + // JNI_OnLoad; older devices fall back to the auto-bound *Legacy variants. + private static final boolean SUPPORTS_OPTIMIZED_NATIVE = android.os.Build.VERSION.SDK_INT >= 26; + + @CriticalNative + private static native int generateNewObjectIdCritical(int runtimeId); + private static native int generateNewObjectIdLegacy(int runtimeId); + + private static int generateNewObjectId(int runtimeId) { + return SUPPORTS_OPTIMIZED_NATIVE + ? generateNewObjectIdCritical(runtimeId) + : generateNewObjectIdLegacy(runtimeId); + } + + @FastNative + private native boolean notifyGcFast(int runtimeId, int[] javaObjectIds); + private native boolean notifyGcLegacy(int runtimeId, int[] javaObjectIds); - private native boolean notifyGc(int runtimeId, int[] javaObjectIds); + private boolean notifyGc(int runtimeId, int[] javaObjectIds) { + return notifyGcLegacy(runtimeId, javaObjectIds); + } private native void lock(int runtimeId); @@ -61,21 +82,33 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat private native void passExceptionToJsNative(int runtimeId, Throwable ex, String message, String fullStackTrace, String jsStackTrace, boolean isDiscarded, boolean isPendingError); - private static native int getCurrentRuntimeId(); + @CriticalNative + private static native int getCurrentRuntimeIdCritical(); + private static native int getCurrentRuntimeIdLegacy(); - public static native int getPointerSize(); - - public static native void SetManualInstrumentationMode(String mode); - - private static native void WorkerGlobalOnMessageCallback(int runtimeId, String message); + public static int getCurrentRuntimeId() { + return SUPPORTS_OPTIMIZED_NATIVE ? getCurrentRuntimeIdCritical() : getCurrentRuntimeIdLegacy(); + } - private static native void WorkerObjectOnMessageCallback(int runtimeId, int workerId, String message); + @CriticalNative + private static native int getPointerSizeCritical(); + private static native int getPointerSizeLegacy(); - private static native void TerminateWorkerCallback(int runtimeId); + public static int getPointerSize() { + return SUPPORTS_OPTIMIZED_NATIVE ? getPointerSizeCritical() : getPointerSizeLegacy(); + } - private static native void ClearWorkerPersistent(int runtimeId, int workerId); + @FastNative + private static native void setManualInstrumentationModeFast(String mode); + private static native void setManualInstrumentationModeLegacy(String mode); - private static native void CallWorkerObjectOnErrorHandleMain(int runtimeId, int workerId, String message, String stackTrace, String filename, int lineno, String threadName) throws NativeScriptException; + public static void SetManualInstrumentationMode(String mode) { + if (SUPPORTS_OPTIMIZED_NATIVE) { + setManualInstrumentationModeFast(mode); + } else { + setManualInstrumentationModeLegacy(mode); + } + } private static native void ResetDateTimeConfigurationCache(int runtimeId); @@ -104,15 +137,23 @@ public static void passSuppressedExceptionToJs(Throwable ex, String methodName) "Primitive types need to be manually wrapped in their respective Object wrappers.\n" + "If you are creating an instance of an inner class, make sure to always provide reference to the outer `this` as the first argument."; - private HashMap strongInstances = new HashMap<>(); + private Map strongInstances = new HashMap<>(); + + private Map> weakInstances = new HashMap<>(); - private HashMap> weakInstances = new HashMap<>(); + private Map strongJavaObjectToID = new NativeScriptHashMap(); - private NativeScriptHashMap strongJavaObjectToID = new NativeScriptHashMap(); + private Map weakJavaObjectToID = new NativeScriptWeakHashMap(); - private NativeScriptWeakHashMap weakJavaObjectToID = new NativeScriptWeakHashMap(); + private Map, JavaScriptImplementation> loadedJavaScriptExtends = new HashMap, JavaScriptImplementation>(); - private final Map, JavaScriptImplementation> loadedJavaScriptExtends = new HashMap, JavaScriptImplementation>(); + // Classes already registered via classStorageService.storeClass, so repeated + // object registrations of the same class (e.g. thousands of java.util.Date + // instances) skip the redundant getClass().getName() string + cache/classloader + // puts. Class uses identity equals/hashCode, so a plain concurrent set is + // correct and thread-safe. + private final java.util.Set> storedClasses = + java.util.Collections.newSetFromMap(new ConcurrentHashMap, Boolean>()); private final java.lang.Runtime dalvikRuntime = java.lang.Runtime.getRuntime(); @@ -156,34 +197,27 @@ public int compare(Method lhs, Method rhs) { private final StaticConfiguration config; private static StaticConfiguration staticConfiguration; + // Config flags hoisted out of the boxed AppConfig.values[] Object[] and cached + // as primitive fields, so every JS->Java callback dispatch avoids two enum + // ordinal lookups + Boolean unboxes. Set once in init(); config is immutable + // after load. + private boolean cachedDiscardUncaughtJsExceptions; + private boolean cachedEnableMultithreadedJavascript; + private final DynamicConfiguration dynamicConfig; private final int runtimeId; - private boolean isTerminating; - /* - Used to map to Handler, for messaging between Main and the other Workers + The worker's id (0 for the main thread's runtime) */ private final int workerId; - /* - Used by all Worker threads to communicate with the Main thread - */ - private Handler mainThreadHandler; - private static AtomicInteger nextRuntimeId = new AtomicInteger(0); private final static ThreadLocal currentRuntime = new ThreadLocal(); private final static Map runtimeCache = new ConcurrentHashMap<>(); - public static Map> pendingWorkerMessages = new ConcurrentHashMap<>(); public static boolean nativeLibraryLoaded; - /* - Holds reference to all Worker Threads' handlers - Note: Should only be used on the main thread - */ - private Map workerIdToHandler = new HashMap<>(); - private static final ClassStorageService classStorageService = new ClassStorageServiceImpl(ClassCacheImpl.INSTANCE, ClassLoadersCollectionImpl.INSTANCE); public Runtime(ClassResolver classResolver, GcListener gcListener, StaticConfiguration config, DynamicConfiguration dynamicConfig, int runtimeId, int workerId, HashMap strongInstances, HashMap> weakInstances, NativeScriptHashMap strongJavaObjectToId, NativeScriptWeakHashMap weakJavaObjectToId) { @@ -216,8 +250,16 @@ public Runtime(StaticConfiguration config, DynamicConfiguration dynamicConfigura this.dynamicConfig = dynamicConfiguration; this.threadScheduler = dynamicConfiguration.myThreadScheduler; this.workerId = dynamicConfiguration.workerId; - if (dynamicConfiguration.mainThreadScheduler != null) { - this.mainThreadHandler = dynamicConfiguration.mainThreadScheduler.getHandler(); + + // if multithreadedJS, make all instance maps concurrent or synchronized: + if (config.appConfig.getEnableMultithreadedJavascript()) { + this.strongInstances = new ConcurrentHashMap<>(); + this.weakInstances = new ConcurrentHashMap<>(); + // loadedJavaScriptExtends can store null values (unsupported by ConcurrentHashMap), + // so use a synchronized map instead. + this.loadedJavaScriptExtends = Collections.synchronizedMap(new HashMap<>()); + this.strongJavaObjectToID = Collections.synchronizedMap(new NativeScriptHashMap<>()); + this.weakJavaObjectToID = Collections.synchronizedMap(new NativeScriptWeakHashMap<>()); } classResolver = new ClassResolver(classStorageService); @@ -381,225 +423,74 @@ public void releaseNativeCounterpart(int nativeObjectId) { } } - private static class WorkerThreadHandler extends Handler { - - WorkerThreadHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - if (currentRuntime.isTerminating) { - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + currentRuntime.workerId + ") is terminating, it will not process the message."); - } - - return; - } - - /* - Handle messages coming from the Main thread - */ - if (msg.arg1 == MessageType.MainToWorker) { - /* - Calls the Worker script's onmessage implementation with arg -> msg.obj.toString() - */ - WorkerGlobalOnMessageCallback(currentRuntime.runtimeId, msg.obj.toString()); - } else if (msg.arg1 == MessageType.TerminateThread) { - currentRuntime.isTerminating = true; - GcListener.unsubscribe(currentRuntime); - - runtimeCache.remove(currentRuntime.runtimeId); - - TerminateWorkerCallback(currentRuntime.runtimeId); - - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + currentRuntime.workerId + ", name=\"" + Thread.currentThread().getName() + "\") has terminated execution. Don't make further function calls to it."); - } - - this.getLooper().quit(); - } else if (msg.arg1 == MessageType.TerminateAndCloseThread) { - Message msgToMain = Message.obtain(); - msgToMain.arg1 = MessageType.CloseWorker; - msgToMain.arg2 = currentRuntime.workerId; - - currentRuntime.mainThreadHandler.sendMessage(msgToMain); - - currentRuntime.isTerminating = true; - GcListener.unsubscribe(currentRuntime); - - runtimeCache.remove(currentRuntime.runtimeId); - - TerminateWorkerCallback(currentRuntime.runtimeId); - - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + currentRuntime.workerId + ", name=\"" + Thread.currentThread().getName() + "\") has terminated execution. Don't make further function calls to it."); - } - - this.getLooper().quit(); - } - } + /* + This method initializes the runtime and should always be called first and through the main thread + in order to set static configuration that all following workers can use + */ + public static Runtime initializeRuntimeWithConfiguration(StaticConfiguration config) { + staticConfiguration = config; + WorkThreadScheduler mainThreadScheduler = new WorkThreadScheduler(new Handler(Looper.myLooper())); + DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(0, mainThreadScheduler, null); + Runtime runtime = initRuntime(dynamicConfiguration); + return runtime; } - private static class WorkerThread extends HandlerThread { + /* + Called via JNI from a native-spawned worker thread (WorkerWrapper), after + the thread has attached to the JVM and before the worker script runs. + Prepares the thread's Java Looper - this must happen before initRuntime so + that the native runtime (timers, worker inbox, message loop) binds its fds + to the looper that runWorkerLoop later pumps - and creates the worker's + Runtime with a Looper-backed scheduler so cross-thread Java->JS calls + (dispatchCallJSMethodNative, runScript) keep working. + Should only be called after the static configuration has been initialized. + Returns the created runtime's id. + */ + @RuntimeCallable + public static int initWorkerRuntime(int workerId, String callingJsDir) { + Looper.prepare(); - private Integer workerId; - private ThreadScheduler mainThreadScheduler; - private String filePath; - private String callingJsDir; + WorkThreadScheduler workThreadScheduler = new WorkThreadScheduler(new Handler(Looper.myLooper())); + DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(workerId, workThreadScheduler, null, callingJsDir); - public WorkerThread(String name, Integer workerId, ThreadScheduler mainThreadScheduler, String callingJsDir) { - super("W" + workerId + ": " + name); - this.filePath = name; - this.workerId = workerId; - this.mainThreadScheduler = mainThreadScheduler; - this.callingJsDir = callingJsDir; + if (staticConfiguration.logger.isEnabled()) { + staticConfiguration.logger.write("Worker (id=" + workerId + ")'s Runtime is initializing!"); } - public void startRuntime() { - final Handler handler = new Handler(this.getLooper()); - - handler.post((new Runnable() { - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - - WorkThreadScheduler workThreadScheduler = new WorkThreadScheduler(new WorkerThreadHandler(handler.getLooper())); - - DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(workerId, workThreadScheduler, mainThreadScheduler, callingJsDir); - - if (staticConfiguration.logger.isEnabled()) { - staticConfiguration.logger.write("Worker (id=" + workerId + ")'s Runtime is initializing!"); - } - - Runtime runtime = initRuntime(dynamicConfiguration); - - if (staticConfiguration.logger.isEnabled()) { - staticConfiguration.logger.write("Worker (id=" + workerId + ")'s Runtime initialized!"); - } - - /* - Send a message to the Main Thread to `shake hands`, - Main Thread will cache the Worker Handler for later use - */ - Message msg = Message.obtain(); - msg.arg1 = MessageType.Handshake; - msg.arg2 = runtime.runtimeId; - - runtime.mainThreadHandler.sendMessage(msg); - runtime.runWorker(runtime.runtimeId, filePath); + Runtime runtime = initRuntime(dynamicConfiguration); - runtime.processPendingMessages(); - } - })); + if (staticConfiguration.logger.isEnabled()) { + staticConfiguration.logger.write("Worker (id=" + workerId + ")'s Runtime initialized!"); } - } - private void processPendingMessages() { - Queue messages = Runtime.pendingWorkerMessages.get(this.getWorkerId()); - if (messages == null) { - return; - } - - Handler handler = this.getHandler(); - while (!messages.isEmpty()) { - handler.sendMessage(messages.poll()); - } + return runtime.getRuntimeId(); } - private static class MainThreadHandler extends Handler { - public MainThreadHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - /* - Handle messages coming from a Worker thread - */ - if (msg.arg1 == MessageType.WorkerToMain) { - /* - Calls the Worker (with id - workerId) object's onmessage implementation with arg -> msg.obj.toString() - */ - WorkerObjectOnMessageCallback(Runtime.getCurrentRuntime().runtimeId, msg.arg2, msg.obj.toString()); - } - /* - Handle a 'Handshake' message sent from a new Worker, - so that the Main may cache it and send messages to it later - */ - else if (msg.arg1 == MessageType.Handshake) { - int senderRuntimeId = msg.arg2; - Runtime workerRuntime = runtimeCache.get(senderRuntimeId); - Runtime mainRuntime = Runtime.getCurrentRuntime(); - - // If worker has had its close/terminate called before the threads could shake hands - if (workerRuntime == null) { - if (mainRuntime.logger.isEnabled()) { - mainRuntime.logger.write("Main thread couldn't shake hands with worker (runtimeId: " + workerRuntime + ") because it has been terminated!"); - } - - return; - } - - /* - Main thread now has a reference to the Worker's handler, - so messaging between the two threads can begin - */ - mainRuntime.workerIdToHandler.put(workerRuntime.getWorkerId(), workerRuntime.getHandler()); - - if (mainRuntime.logger.isEnabled()) { - mainRuntime.logger.write("Worker thread (workerId:" + workerRuntime.getWorkerId() + ") shook hands with the main thread!"); - } - } else if (msg.arg1 == MessageType.CloseWorker) { - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - // remove reference to a Worker thread's handler that is in the process of closing - currentRuntime.workerIdToHandler.put(msg.arg2, null); - - ClearWorkerPersistent(currentRuntime.runtimeId, msg.arg2); - } - /* - Handle unhandled exceptions/errors coming from the worker thread - */ - else if (msg.arg1 == MessageType.BubbleUpException) { - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - int workerId = msg.arg2; - JavaScriptErrorMessage errorMessage = (JavaScriptErrorMessage) msg.obj; - - CallWorkerObjectOnErrorHandleMain(currentRuntime.runtimeId, workerId, errorMessage.getMessage(), errorMessage.getStackTrace(), errorMessage.getFilename(), errorMessage.getLineno(), errorMessage.getThreadName()); - } - } - } - - /* - This method initializes the runtime and should always be called first and through the main thread - in order to set static configuration that all following workers can use + Called via JNI from the native worker thread. Blocks pumping the worker's + looper (Java Handler messages and the native fds registered on it) until + the looper is quit by WorkerWrapper on terminate()/close(). */ - public static Runtime initializeRuntimeWithConfiguration(StaticConfiguration config) { - staticConfiguration = config; - WorkThreadScheduler mainThreadScheduler = new WorkThreadScheduler(new MainThreadHandler(Looper.myLooper())); - DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(0, mainThreadScheduler, null); - Runtime runtime = initRuntime(dynamicConfiguration); - return runtime; + @RuntimeCallable + public static void runWorkerLoop() { + Looper.loop(); } /* - This method should be called via native code only after the static configuration has been initialized. - It will use the static configuration for all following calls to initialize a new runtime. + Called via JNI from the native worker thread during shutdown, before the + worker's runtime is disposed. */ @RuntimeCallable - public static void initWorker(String jsFileName, String callingJsDir, int id) { - // This method will always be called from the Main thread - Runtime runtime = Runtime.getCurrentRuntime(); - ThreadScheduler mainThreadScheduler = runtime.getDynamicConfig().myThreadScheduler; + public static void detachWorkerRuntime(int runtimeId) { + Runtime runtime = runtimeCache.remove(runtimeId); + if (runtime != null) { + GcListener.unsubscribe(runtime); - WorkerThread worker = new WorkerThread(jsFileName, id, mainThreadScheduler, callingJsDir); - worker.start(); - worker.startRuntime(); + if (runtime.logger != null && runtime.logger.isEnabled()) { + runtime.logger.write("Worker(id=" + runtime.workerId + ", name=\"" + Thread.currentThread().getName() + "\") has terminated execution. Don't make further function calls to it."); + } + } + currentRuntime.remove(); } /* @@ -608,8 +499,18 @@ public static void initWorker(String jsFileName, String callingJsDir, int id) { */ private static Runtime initRuntime(DynamicConfiguration dynamicConfiguration) { Runtime runtime = new Runtime(staticConfiguration, dynamicConfiguration); - runtime.init(); - runtime.runScript(new File(staticConfiguration.appDir, "internal/ts_helpers.js")); + try { + runtime.init(); + runtime.runScript(new File(staticConfiguration.appDir, "internal/ts_helpers.js")); + } catch (Throwable t) { + // the constructor already registered the instance - roll back so a + // failed bootstrap doesn't leave a stale, half-initialized runtime + // reachable through the caches + runtimeCache.remove(runtime.getRuntimeId()); + currentRuntime.remove(); + GcListener.unsubscribe(runtime); + throw t; + } return runtime; } @@ -631,7 +532,17 @@ private void init(Logger logger, String appName, String nativeLibDir, File rootD try { this.logger = logger; - this.dexFactory = new DexFactory(logger, classLoader, dexDir, dexThumb, classStorageService); + // Cache the hot per-call config flags as primitives (see field decls). + if (appConfig != null) { + this.cachedDiscardUncaughtJsExceptions = appConfig.getDiscardUncaughtJsExceptions(); + this.cachedEnableMultithreadedJavascript = appConfig.getEnableMultithreadedJavascript(); + } + + // Only inject generated proxies into the app's PathClassLoader on the main + // thread, so Class.forName() (used by framework components like FragmentFactory) + // can find them. + boolean isMainThread = this.workerId == 0; + this.dexFactory = new DexFactory(logger, classLoader, dexDir, dexThumb, classStorageService, isMainThread); if (logger.isEnabled()) { logger.write("Initializing NativeScript JAVA"); @@ -987,7 +898,12 @@ private void makeInstanceStrong(Object instance, int objectId) { strongJavaObjectToID.put(instance, objectId); Class clazz = instance.getClass(); - classStorageService.storeClass(clazz.getName(), clazz); + // Only resolve the name + store the class the first time we see it; the + // storage is keyed by class name and the classloader set is idempotent, so + // repeated registrations of the same class are pure overhead. + if (storedClasses.add(clazz)) { + classStorageService.storeClass(clazz.getName(), clazz); + } if (logger != null && logger.isEnabled()) { logger.write("MakeInstanceStrong (" + objectId + ", " + instance.getClass().toString() + ")"); @@ -1211,23 +1127,32 @@ public static Object callJSMethodWithDelay(int runtimeId, Object javaObject, Str public static Object callJSMethod(int runtimeId, Object javaObject, String methodName, Class retType, boolean isConstructor, long delay, Object... args) throws NativeScriptException { Runtime runtime = Runtime.runtimeCache.get(runtimeId); - if (runtime == null) { + // Resolve the object's id on the found runtime once and reuse it (the common + // path previously looked it up here for the ownership check AND again in + // callJSMethodImpl). Re-resolve only when we switch to a different runtime. + Integer javaObjectID = (runtime != null) ? runtime.getJavaObjectID(javaObject) : null; + + // If we didn't find a runtime by id, or the one we found doesn't own this + // object, locate the runtime that actually created it. This happens when a + // worker fires a JS method on an object created in the main thread or another worker. + if (runtime == null || javaObjectID == null) { runtime = getObjectRuntime(javaObject); + javaObjectID = (runtime != null) ? runtime.getJavaObjectID(javaObject) : null; } if (runtime == null) { runtime = Runtime.getCurrentRuntime(); + javaObjectID = (runtime != null) ? runtime.getJavaObjectID(javaObject) : null; } if (runtime == null) { throw new NativeScriptException("Cannot find runtime for instance=" + ((javaObject == null) ? "null" : javaObject)); } - return runtime.callJSMethodImpl(javaObject, methodName, retType, isConstructor, delay, args); + return runtime.callJSMethodImpl(javaObjectID, javaObject, methodName, retType, isConstructor, delay, args); } - private Object callJSMethodImpl(Object javaObject, String methodName, Class retType, boolean isConstructor, long delay, Object... args) throws NativeScriptException { - Integer javaObjectID = getJavaObjectID(javaObject); + private Object callJSMethodImpl(Integer javaObjectID, Object javaObject, String methodName, Class retType, boolean isConstructor, long delay, Object... args) throws NativeScriptException { if (javaObjectID == null) { throw new NativeScriptException("Cannot find object id for instance=" + ((javaObject == null) ? "null" : javaObject)); } @@ -1344,8 +1269,8 @@ private Object dispatchCallJSMethodNative(final int javaObjectID, Class claz, boolean isWorkThread = threadScheduler.getThread().equals(Thread.currentThread()); final Object[] tmpArgs = extendConstructorArgs(methodName, isConstructor, args); - final boolean discardUncaughtJsExceptions = this.config.appConfig.getDiscardUncaughtJsExceptions(); - boolean enableMultithreadedJavascript = this.config.appConfig.getEnableMultithreadedJavascript(); + final boolean discardUncaughtJsExceptions = this.cachedDiscardUncaughtJsExceptions; + boolean enableMultithreadedJavascript = this.cachedEnableMultithreadedJavascript; if (enableMultithreadedJavascript || isWorkThread) { Object[] packagedArgs = packageArgs(tmpArgs); @@ -1455,163 +1380,6 @@ private static boolean useGlobalRefs() { return true; } - /* - ====================================================================== - ====================================================================== - Workers messaging callbacks - ====================================================================== - ====================================================================== - */ - @RuntimeCallable - public static void sendMessageFromMainToWorker(int workerId, String message) { - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - Message msg = Message.obtain(); - msg.obj = message; - msg.arg1 = MessageType.MainToWorker; - - boolean hasKey = currentRuntime.workerIdToHandler.containsKey(workerId); - Handler workerHandler = currentRuntime.workerIdToHandler.get(workerId); - - // TODO: Pete: Ensure that we won't end up in an endless loop. Can we get an invalid workerId? - /* - If workHandler is null then the new Worker Thread still hasn't completed initializing - - OR - - The workHandler is null because it has been closed; Check if its key is still in the map - */ - if (workerHandler == null) { - // Attempt to send a message to a closed worker, throw error or just log a message - if (hasKey) { - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + msg.arg2 + ") that you are trying to send a message to has been terminated. No message will be sent."); - } - - return; - } - - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + msg.arg2 + ")'s handler still not initialized. Requeueing message for Worker(id=" + msg.arg2 + ")"); - } - - if (pendingWorkerMessages.get(workerId) == null) { - pendingWorkerMessages.put(workerId, new ConcurrentLinkedQueue()); - } - - Queue messages = pendingWorkerMessages.get(workerId); - messages.add(msg); - - return; - } - - if (!workerHandler.getLooper().getThread().isAlive()) { - return; - } - - workerHandler.sendMessage(msg); - } - - @RuntimeCallable - public static void sendMessageFromWorkerToMain(String message) { - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - Message msg = Message.obtain(); - msg.arg1 = MessageType.WorkerToMain; - - /* - Send the workerId associated with the JavaScript Worker object - */ - msg.arg2 = currentRuntime.getWorkerId(); - msg.obj = message; - - currentRuntime.mainThreadHandler.sendMessage(msg); - } - - @RuntimeCallable - public static void workerObjectTerminate(int workerId) { - // Thread should always be main here - Runtime currentRuntime = Runtime.getCurrentRuntime(); - final long ResendDelay = 1000; - - Message msg = Message.obtain(); - - boolean hasKey = currentRuntime.workerIdToHandler.containsKey(workerId); - Handler workerHandler = currentRuntime.workerIdToHandler.get(workerId); - - msg.arg1 = MessageType.TerminateThread; - msg.arg2 = workerId; - - /* - If workHandler is null then the new Worker Thread still hasn't completed initializing - - OR - - The workHandler is null because it has been closed; Check if its key is still in the map - */ - if (workerHandler == null) { - // Attempt to send a message to a closed worker, throw error or just log a message - if (hasKey) { - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + msg.arg2 + ") is already terminated. No message will be sent."); - } - - return; - } else { - if (currentRuntime.logger.isEnabled()) { - currentRuntime.logger.write("Worker(id=" + msg.arg2 + ")'s handler still not initialized. Requeueing terminate() message for Worker(id=" + msg.arg2 + ")"); - } - - if (pendingWorkerMessages.get(workerId) == null) { - pendingWorkerMessages.put(workerId, new ConcurrentLinkedQueue()); - } - - Queue messages = pendingWorkerMessages.get(workerId); - messages.add(msg); - return; - } - } - - // Worker was closed during this 'terminate' call, nothing to do here - if (!workerHandler.getLooper().getThread().isAlive()) { - return; - } - - // 'terminate' message must be executed immediately - workerHandler.sendMessageAtFrontOfQueue(msg); - - // Set value for workerId key to null - currentRuntime.workerIdToHandler.put(workerId, null); - } - - @RuntimeCallable - public static void workerScopeClose() { - // Thread should always be a worker - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - Message msgToWorker = Message.obtain(); - msgToWorker.arg1 = MessageType.TerminateAndCloseThread; - - currentRuntime.getHandler().sendMessageAtFrontOfQueue(msgToWorker); - } - - @RuntimeCallable - public static void passUncaughtExceptionFromWorkerToMain(String message, String filename, String stackTrace, int lineno) { - // Thread should always be a worker - Runtime currentRuntime = Runtime.getCurrentRuntime(); - - Message msg = Message.obtain(); - msg.arg1 = MessageType.BubbleUpException; - msg.arg2 = currentRuntime.workerId; - - String threadName = currentRuntime.getHandler().getLooper().getThread().getName(); - JavaScriptErrorMessage error = new JavaScriptErrorMessage(message, stackTrace, filename, lineno, threadName); - - msg.obj = error; - - // TODO: Pete: Should we treat the message with higher priority? - currentRuntime.mainThreadHandler.sendMessage(msg); - } @Override protected void finalize() throws Throwable { diff --git a/test-app/runtime/src/main/java/com/tns/TimerHandler.java b/test-app/runtime/src/main/java/com/tns/TimerHandler.java new file mode 100644 index 00000000..4b3b0a74 --- /dev/null +++ b/test-app/runtime/src/main/java/com/tns/TimerHandler.java @@ -0,0 +1,46 @@ +package com.tns; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +/** + * Delivers native timer callbacks through the runtime thread's Java MessageQueue + * (instead of an ALooper fd), so setTimeout/setInterval share a single queue with + * Handler.post/postDelayed and fire in exact MessageQueue order. Each scheduled + * timer enqueues one anonymous "due token" message; native picks the earliest-due + * timer when the token is handled. + */ +final class TimerHandler extends Handler { + private static final int MSG_FIRE_TIMER = 1; + + private final long nativeTimersPtr; + private boolean released; + + // constructed from native code (Timers::Init) on the runtime thread + TimerHandler(long nativeTimersPtr) { + super(Looper.myLooper()); + this.nativeTimersPtr = nativeTimersPtr; + } + + @RuntimeCallable + void post(long uptimeMillis) { + sendMessageAtTime(obtainMessage(MSG_FIRE_TIMER), uptimeMillis); + } + + @RuntimeCallable + void release() { + released = true; + removeCallbacksAndMessages(null); // safe: this handler is timers-only + } + + @Override + public void handleMessage(Message msg) { + if (released || msg.what != MSG_FIRE_TIMER) { + return; + } + nativeFireTimer(nativeTimersPtr); + } + + private static native void nativeFireTimer(long nativeTimersPtr); +} diff --git a/test-app/runtime/src/main/java/fi/iki/elonen/NanoWSD.java b/test-app/runtime/src/main/java/fi/iki/elonen/NanoWSD.java index 59404e72..fcf219af 100644 --- a/test-app/runtime/src/main/java/fi/iki/elonen/NanoWSD.java +++ b/test-app/runtime/src/main/java/fi/iki/elonen/NanoWSD.java @@ -696,9 +696,9 @@ public String toString() { public void write(OutputStream out) throws IOException { byte header = 0; if (this.fin) { - header |= 0x80; + header |= (byte) 0x80; } - header |= this.opCode.getValue() & 0x0F; + header |= (byte) (this.opCode.getValue() & 0x0F); out.write(header); this._payloadLength = getBinaryPayload().length; diff --git a/test-app/runtime/src/main/libs/common/arm64-v8a/libzip.a b/test-app/runtime/src/main/libs/common/arm64-v8a/libzip.a index d99eec14..197b1ddb 100644 Binary files a/test-app/runtime/src/main/libs/common/arm64-v8a/libzip.a and b/test-app/runtime/src/main/libs/common/arm64-v8a/libzip.a differ diff --git a/test-app/runtime/src/main/libs/common/armeabi-v7a/libzip.a b/test-app/runtime/src/main/libs/common/armeabi-v7a/libzip.a index 0647e0e3..e85de37d 100644 Binary files a/test-app/runtime/src/main/libs/common/armeabi-v7a/libzip.a and b/test-app/runtime/src/main/libs/common/armeabi-v7a/libzip.a differ diff --git a/test-app/runtime/src/main/libs/common/x86/libzip.a b/test-app/runtime/src/main/libs/common/x86/libzip.a index 77f94ff7..7fbc8162 100644 Binary files a/test-app/runtime/src/main/libs/common/x86/libzip.a and b/test-app/runtime/src/main/libs/common/x86/libzip.a differ diff --git a/test-app/runtime/src/main/libs/common/x86_64/libzip.a b/test-app/runtime/src/main/libs/common/x86_64/libzip.a index d20f2722..dabcf028 100644 Binary files a/test-app/runtime/src/main/libs/common/x86_64/libzip.a and b/test-app/runtime/src/main/libs/common/x86_64/libzip.a differ diff --git a/test-app/runtime/src/main/libs/hermes/arm64-v8a/libhermes.so b/test-app/runtime/src/main/libs/hermes/arm64-v8a/libhermes.so deleted file mode 100644 index e348e472..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/arm64-v8a/libhermes.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/arm64-v8a/libjsi.so b/test-app/runtime/src/main/libs/hermes/arm64-v8a/libjsi.so deleted file mode 100644 index 2ab3889b..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/arm64-v8a/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/armeabi-v7a/libhermes.so b/test-app/runtime/src/main/libs/hermes/armeabi-v7a/libhermes.so deleted file mode 100644 index d1096c9e..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/armeabi-v7a/libhermes.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/armeabi-v7a/libjsi.so b/test-app/runtime/src/main/libs/hermes/armeabi-v7a/libjsi.so deleted file mode 100644 index 566d6c4b..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/armeabi-v7a/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/arm64-v8a/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/debug/arm64-v8a/libhermesvm.so new file mode 100644 index 00000000..3e8e0ed5 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/arm64-v8a/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/arm64-v8a/libjsi.so b/test-app/runtime/src/main/libs/hermes/debug/arm64-v8a/libjsi.so new file mode 100644 index 00000000..e66c306a Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/arm64-v8a/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/armeabi-v7a/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/debug/armeabi-v7a/libhermesvm.so new file mode 100644 index 00000000..58697a3e Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/armeabi-v7a/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/armeabi-v7a/libjsi.so b/test-app/runtime/src/main/libs/hermes/debug/armeabi-v7a/libjsi.so new file mode 100644 index 00000000..44f351ce Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/armeabi-v7a/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/x86/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/debug/x86/libhermesvm.so new file mode 100644 index 00000000..239db8b6 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/x86/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/x86/libjsi.so b/test-app/runtime/src/main/libs/hermes/debug/x86/libjsi.so new file mode 100644 index 00000000..b05889a8 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/x86/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/x86_64/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/debug/x86_64/libhermesvm.so new file mode 100644 index 00000000..5c155869 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/x86_64/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/debug/x86_64/libjsi.so b/test-app/runtime/src/main/libs/hermes/debug/x86_64/libjsi.so new file mode 100644 index 00000000..9431c50c Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/debug/x86_64/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/arm64-v8a/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/release/arm64-v8a/libhermesvm.so new file mode 100644 index 00000000..9c058e05 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/arm64-v8a/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/arm64-v8a/libjsi.so b/test-app/runtime/src/main/libs/hermes/release/arm64-v8a/libjsi.so new file mode 100644 index 00000000..fd550703 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/arm64-v8a/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/armeabi-v7a/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/release/armeabi-v7a/libhermesvm.so new file mode 100644 index 00000000..f7dda575 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/armeabi-v7a/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/armeabi-v7a/libjsi.so b/test-app/runtime/src/main/libs/hermes/release/armeabi-v7a/libjsi.so new file mode 100644 index 00000000..39366438 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/armeabi-v7a/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/x86/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/release/x86/libhermesvm.so new file mode 100644 index 00000000..885eb7e0 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/x86/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/x86/libjsi.so b/test-app/runtime/src/main/libs/hermes/release/x86/libjsi.so new file mode 100644 index 00000000..24728064 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/x86/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/x86_64/libhermesvm.so b/test-app/runtime/src/main/libs/hermes/release/x86_64/libhermesvm.so new file mode 100644 index 00000000..4a433739 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/x86_64/libhermesvm.so differ diff --git a/test-app/runtime/src/main/libs/hermes/release/x86_64/libjsi.so b/test-app/runtime/src/main/libs/hermes/release/x86_64/libjsi.so new file mode 100644 index 00000000..47dbb489 Binary files /dev/null and b/test-app/runtime/src/main/libs/hermes/release/x86_64/libjsi.so differ diff --git a/test-app/runtime/src/main/libs/hermes/x86/libhermes.so b/test-app/runtime/src/main/libs/hermes/x86/libhermes.so deleted file mode 100644 index 0a56760a..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/x86/libhermes.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/x86/libjsi.so b/test-app/runtime/src/main/libs/hermes/x86/libjsi.so deleted file mode 100644 index c392e7e1..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/x86/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/x86_64/libhermes.so b/test-app/runtime/src/main/libs/hermes/x86_64/libhermes.so deleted file mode 100644 index 87f7eefa..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/x86_64/libhermes.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/hermes/x86_64/libjsi.so b/test-app/runtime/src/main/libs/hermes/x86_64/libjsi.so deleted file mode 100644 index 386f1940..00000000 Binary files a/test-app/runtime/src/main/libs/hermes/x86_64/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/jsc/arm64-v8a/libJavaScriptCore.so b/test-app/runtime/src/main/libs/jsc/arm64-v8a/libJavaScriptCore.so new file mode 100755 index 00000000..bd1f65c3 Binary files /dev/null and b/test-app/runtime/src/main/libs/jsc/arm64-v8a/libJavaScriptCore.so differ diff --git a/test-app/runtime/src/main/libs/jsc/arm64-v8a/libJavaScriptCore_old.so b/test-app/runtime/src/main/libs/jsc/arm64-v8a/libJavaScriptCore_old.so new file mode 100644 index 00000000..05bd0470 Binary files /dev/null and b/test-app/runtime/src/main/libs/jsc/arm64-v8a/libJavaScriptCore_old.so differ diff --git a/test-app/runtime/src/main/libs/jsc/arm64-v8a/libjsc.so b/test-app/runtime/src/main/libs/jsc/arm64-v8a/libjsc.so deleted file mode 100644 index 22d4a093..00000000 Binary files a/test-app/runtime/src/main/libs/jsc/arm64-v8a/libjsc.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/jsc/armeabi-v7a/libJavaScriptCore.so b/test-app/runtime/src/main/libs/jsc/armeabi-v7a/libJavaScriptCore.so new file mode 100755 index 00000000..6e320efb Binary files /dev/null and b/test-app/runtime/src/main/libs/jsc/armeabi-v7a/libJavaScriptCore.so differ diff --git a/test-app/runtime/src/main/libs/jsc/armeabi-v7a/libjsc.so b/test-app/runtime/src/main/libs/jsc/armeabi-v7a/libjsc.so deleted file mode 100644 index 3f1f5005..00000000 Binary files a/test-app/runtime/src/main/libs/jsc/armeabi-v7a/libjsc.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/jsc/x86/libJavaScriptCore.so b/test-app/runtime/src/main/libs/jsc/x86/libJavaScriptCore.so new file mode 100755 index 00000000..e8edc176 Binary files /dev/null and b/test-app/runtime/src/main/libs/jsc/x86/libJavaScriptCore.so differ diff --git a/test-app/runtime/src/main/libs/jsc/x86/libjsc.so b/test-app/runtime/src/main/libs/jsc/x86/libjsc.so deleted file mode 100644 index 53ba1ad8..00000000 Binary files a/test-app/runtime/src/main/libs/jsc/x86/libjsc.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/jsc/x86_64/libJavaScriptCore.so b/test-app/runtime/src/main/libs/jsc/x86_64/libJavaScriptCore.so new file mode 100644 index 00000000..4d53f40d Binary files /dev/null and b/test-app/runtime/src/main/libs/jsc/x86_64/libJavaScriptCore.so differ diff --git a/test-app/runtime/src/main/libs/jsc/x86_64/libjsc.so b/test-app/runtime/src/main/libs/jsc/x86_64/libjsc.so deleted file mode 100644 index 14492c19..00000000 Binary files a/test-app/runtime/src/main/libs/jsc/x86_64/libjsc.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/primjs/arm64-v8a/libc++_shared.so b/test-app/runtime/src/main/libs/primjs/arm64-v8a/libc++_shared.so deleted file mode 100644 index c0944e8f..00000000 Binary files a/test-app/runtime/src/main/libs/primjs/arm64-v8a/libc++_shared.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/primjs/arm64-v8a/libnapi.so b/test-app/runtime/src/main/libs/primjs/arm64-v8a/libnapi.so new file mode 100644 index 00000000..96765e4c Binary files /dev/null and b/test-app/runtime/src/main/libs/primjs/arm64-v8a/libnapi.so differ diff --git a/test-app/runtime/src/main/libs/primjs/arm64-v8a/libquick.so b/test-app/runtime/src/main/libs/primjs/arm64-v8a/libquick.so index 02902516..a3b9ccbb 100644 Binary files a/test-app/runtime/src/main/libs/primjs/arm64-v8a/libquick.so and b/test-app/runtime/src/main/libs/primjs/arm64-v8a/libquick.so differ diff --git a/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libc++_shared.so b/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libc++_shared.so deleted file mode 100644 index eff4210f..00000000 Binary files a/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libc++_shared.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libnapi.so b/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libnapi.so new file mode 100644 index 00000000..779411b8 Binary files /dev/null and b/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libnapi.so differ diff --git a/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libquick.so b/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libquick.so index 72bf7618..5221fc02 100644 Binary files a/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libquick.so and b/test-app/runtime/src/main/libs/primjs/armeabi-v7a/libquick.so differ diff --git a/test-app/runtime/src/main/libs/primjs/x86/libc++_shared.so b/test-app/runtime/src/main/libs/primjs/x86/libc++_shared.so deleted file mode 100644 index e0034a4c..00000000 Binary files a/test-app/runtime/src/main/libs/primjs/x86/libc++_shared.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/primjs/x86/libnapi.so b/test-app/runtime/src/main/libs/primjs/x86/libnapi.so new file mode 100644 index 00000000..f6547f05 Binary files /dev/null and b/test-app/runtime/src/main/libs/primjs/x86/libnapi.so differ diff --git a/test-app/runtime/src/main/libs/primjs/x86/libquick.so b/test-app/runtime/src/main/libs/primjs/x86/libquick.so index 6f1bdd28..54832ab4 100644 Binary files a/test-app/runtime/src/main/libs/primjs/x86/libquick.so and b/test-app/runtime/src/main/libs/primjs/x86/libquick.so differ diff --git a/test-app/runtime/src/main/libs/primjs/x86_64/libc++_shared.so b/test-app/runtime/src/main/libs/primjs/x86_64/libc++_shared.so deleted file mode 100644 index 8f0fc9bb..00000000 Binary files a/test-app/runtime/src/main/libs/primjs/x86_64/libc++_shared.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/primjs/x86_64/libnapi.so b/test-app/runtime/src/main/libs/primjs/x86_64/libnapi.so new file mode 100644 index 00000000..b2d6747a Binary files /dev/null and b/test-app/runtime/src/main/libs/primjs/x86_64/libnapi.so differ diff --git a/test-app/runtime/src/main/libs/primjs/x86_64/libquick.so b/test-app/runtime/src/main/libs/primjs/x86_64/libquick.so index 072bfd41..4c976683 100644 Binary files a/test-app/runtime/src/main/libs/primjs/x86_64/libquick.so and b/test-app/runtime/src/main/libs/primjs/x86_64/libquick.so differ diff --git a/test-app/runtime/src/main/libs/shermes/arm64-v8a/libfbjni.so b/test-app/runtime/src/main/libs/shermes/arm64-v8a/libfbjni.so deleted file mode 100644 index 790bfd56..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/arm64-v8a/libfbjni.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/arm64-v8a/libhermesvm.so b/test-app/runtime/src/main/libs/shermes/arm64-v8a/libhermesvm.so deleted file mode 100644 index 9eb0df11..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/arm64-v8a/libhermesvm.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/arm64-v8a/libjsi.so b/test-app/runtime/src/main/libs/shermes/arm64-v8a/libjsi.so deleted file mode 100644 index df500ebd..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/arm64-v8a/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libfbjni.so b/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libfbjni.so deleted file mode 100644 index f856e989..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libfbjni.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libhermesvm.so b/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libhermesvm.so deleted file mode 100644 index 184c1b41..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libhermesvm.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libjsi.so b/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libjsi.so deleted file mode 100644 index 501b0ca7..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/armeabi-v7a/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/x86/libfbjni.so b/test-app/runtime/src/main/libs/shermes/x86/libfbjni.so deleted file mode 100644 index 7cb12f31..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/x86/libfbjni.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/x86/libhermesvm.so b/test-app/runtime/src/main/libs/shermes/x86/libhermesvm.so deleted file mode 100644 index d8c78140..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/x86/libhermesvm.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/x86/libjsi.so b/test-app/runtime/src/main/libs/shermes/x86/libjsi.so deleted file mode 100644 index 8d421d1a..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/x86/libjsi.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/x86_64/libfbjni.so b/test-app/runtime/src/main/libs/shermes/x86_64/libfbjni.so deleted file mode 100644 index 682db5a6..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/x86_64/libfbjni.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/x86_64/libhermesvm.so b/test-app/runtime/src/main/libs/shermes/x86_64/libhermesvm.so deleted file mode 100644 index 8e7a2095..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/x86_64/libhermesvm.so and /dev/null differ diff --git a/test-app/runtime/src/main/libs/shermes/x86_64/libjsi.so b/test-app/runtime/src/main/libs/shermes/x86_64/libjsi.so deleted file mode 100644 index 5fd37fb7..00000000 Binary files a/test-app/runtime/src/main/libs/shermes/x86_64/libjsi.so and /dev/null differ diff --git a/tools/bytecode-compiler/README.md b/tools/bytecode-compiler/README.md new file mode 100644 index 00000000..db86ff65 --- /dev/null +++ b/tools/bytecode-compiler/README.md @@ -0,0 +1,92 @@ +# Compile-time bytecode compiler + +Engine-generic tool that turns the app's plain-JS bundle into pre-compiled engine +bytecode at build time, so the runtime skips parsing/compiling on cold start. +Used by the `compileBytecode` Gradle task for **release** builds. See +[`docs/bytecode.md`](../../docs/bytecode.md) for the end-to-end design. + +## Layout + +``` +bytecode-compiler/ + compile-bytecode.js generic driver (walk, wrap, idempotency, source maps, host resolution) + lib/ + index.js engine registry + host resolution + hermes.js Hermes adapter (wired end-to-end today) + quickjs.js wired; gated off (ready:false) until runtime read-support lands + quickjs-ng.js + primjs.js + native/ + qjs-compile.c blob-emitting shim for QuickJS + QuickJS-NG (JS_* API) + primjs-compile.c blob-emitting shim for PrimJS (LEPUS_* API) + bin/ + // host-specific compiler binaries (produced by CI) +``` + +## Building the compiler binaries + +The host binaries are built by [`.github/workflows/bytecode-compilers.yml`](../../.github/workflows/bytecode-compilers.yml) +(manual `workflow_dispatch`). It clones each engine's **upstream** repo +(facebook/hermes, bellard/quickjs, quickjs-ng/quickjs, lynx-family/primjs), builds +the CLI for every host, and uploads one artifact per `(engine, host)` named +`bytecode-compiler--`. Download and drop them into +`bin///`. + +`hermesc` already emits a loadable HBC blob. QuickJS's `qjsc` does **not** (it +emits a C array), so for the QuickJS family we build a tiny shim from `native/` +that links the engine and emits a **NativeScript bytecode container**: + +``` +[8-byte magic][4-byte format version, little-endian][engine JS_WriteObject payload] +``` + +The magic (`NSBCQJS\0`, `NSBCNGS\0`, `NSBCPJS\0`) must match the adapter's `magic` +and the runtime's detection. Hermes uses its native HBC magic instead (no container). + +> Compatibility: the CLI must be built from the engine ref that matches what the +> runtime bundles (prebuilt `.so` for Hermes/PrimJS, vendored source for +> QuickJS/QuickJS-NG). Pin the workflow inputs to exact commits once validated. + +Only the compiler binary and its command line are engine-specific; that lives in +each adapter. Everything else (module wrapping, the raw-file list, idempotency, +source maps, picking the host binary) is generic. + +## Host binaries + +Compilers are native executables, so there is one per build host. `` is +`-`: + +| Host | Directory | +| ---------------- | ---------------- | +| macOS (Apple Si) | `darwin-arm64` | +| macOS (Intel) | `darwin-x64` | +| Linux (x64) | `linux-x64` | +| Linux (arm64) | `linux-arm64` | +| Windows (x64) | `win32-x64` | + +Slots we don't have a binary for yet contain a placeholder `README.md`. The driver +treats any slot without a real (>1 KB) executable as "no compiler for this host" +and leaves the app as plain JS source (which the runtime runs fine). + +## Usage + +``` +node compile-bytecode.js --app --engine \ + [--source-maps] [--compiler ] [--bin-dir ] [--raw a,b] [--strict] [--verbose] +``` + +Engines without an adapter (V8*, JSC) and not-yet-ready adapters are a no-op. + +## Adding an engine + +1. Add `lib/.js` implementing the adapter contract (see `lib/index.js`) + and register it in `lib/index.js`. +2. Provide a compiler: either the engine ships a blob-emitting CLI (like + `hermesc`), or add a shim under `native/` and a build job in the workflow. Get + the host binaries into `bin///`. +3. Implement the runtime execution side in `napi//jsr.cpp` + (`js_run_bytecode_file`) so it detects this engine's `magic` and runs the + payload. +4. Flip the adapter's `ready` flag to `true` **only** once both the compiler and + the runtime support exist — otherwise the build would ship bytecode the + runtime can't run. diff --git a/tools/bytecode-compiler/bin/hermes/darwin-arm64/hermesc b/tools/bytecode-compiler/bin/hermes/darwin-arm64/hermesc new file mode 100755 index 00000000..1e4810f7 Binary files /dev/null and b/tools/bytecode-compiler/bin/hermes/darwin-arm64/hermesc differ diff --git a/tools/bytecode-compiler/bin/hermes/darwin-x64/hermesc b/tools/bytecode-compiler/bin/hermes/darwin-x64/hermesc new file mode 100755 index 00000000..e1ca9fed Binary files /dev/null and b/tools/bytecode-compiler/bin/hermes/darwin-x64/hermesc differ diff --git a/tools/bytecode-compiler/bin/hermes/linux-arm64/hermesc b/tools/bytecode-compiler/bin/hermes/linux-arm64/hermesc new file mode 100755 index 00000000..600304b3 Binary files /dev/null and b/tools/bytecode-compiler/bin/hermes/linux-arm64/hermesc differ diff --git a/tools/bytecode-compiler/bin/hermes/linux-x64/hermesc b/tools/bytecode-compiler/bin/hermes/linux-x64/hermesc new file mode 100755 index 00000000..ff648ac9 Binary files /dev/null and b/tools/bytecode-compiler/bin/hermes/linux-x64/hermesc differ diff --git a/tools/bytecode-compiler/bin/hermes/win32-x64/hermesc.exe b/tools/bytecode-compiler/bin/hermes/win32-x64/hermesc.exe new file mode 100644 index 00000000..193b250c Binary files /dev/null and b/tools/bytecode-compiler/bin/hermes/win32-x64/hermesc.exe differ diff --git a/tools/bytecode-compiler/bin/primjs/darwin-arm64/nsbc-primjs b/tools/bytecode-compiler/bin/primjs/darwin-arm64/nsbc-primjs new file mode 100755 index 00000000..d9c23491 Binary files /dev/null and b/tools/bytecode-compiler/bin/primjs/darwin-arm64/nsbc-primjs differ diff --git a/tools/bytecode-compiler/bin/primjs/darwin-x64/nsbc-primjs b/tools/bytecode-compiler/bin/primjs/darwin-x64/nsbc-primjs new file mode 100755 index 00000000..4fe98d7b Binary files /dev/null and b/tools/bytecode-compiler/bin/primjs/darwin-x64/nsbc-primjs differ diff --git a/tools/bytecode-compiler/bin/primjs/linux-x64/nsbc-primjs b/tools/bytecode-compiler/bin/primjs/linux-x64/nsbc-primjs new file mode 100755 index 00000000..1d8198c9 Binary files /dev/null and b/tools/bytecode-compiler/bin/primjs/linux-x64/nsbc-primjs differ diff --git a/tools/bytecode-compiler/bin/quickjs-ng/darwin-arm64/nsbc-quickjs-ng b/tools/bytecode-compiler/bin/quickjs-ng/darwin-arm64/nsbc-quickjs-ng new file mode 100755 index 00000000..cc19d916 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs-ng/darwin-arm64/nsbc-quickjs-ng differ diff --git a/tools/bytecode-compiler/bin/quickjs-ng/darwin-x64/nsbc-quickjs-ng b/tools/bytecode-compiler/bin/quickjs-ng/darwin-x64/nsbc-quickjs-ng new file mode 100755 index 00000000..5345c4ec Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs-ng/darwin-x64/nsbc-quickjs-ng differ diff --git a/tools/bytecode-compiler/bin/quickjs-ng/linux-arm64/nsbc-quickjs-ng b/tools/bytecode-compiler/bin/quickjs-ng/linux-arm64/nsbc-quickjs-ng new file mode 100755 index 00000000..10738b91 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs-ng/linux-arm64/nsbc-quickjs-ng differ diff --git a/tools/bytecode-compiler/bin/quickjs-ng/linux-x64/nsbc-quickjs-ng b/tools/bytecode-compiler/bin/quickjs-ng/linux-x64/nsbc-quickjs-ng new file mode 100755 index 00000000..8a6aa6e3 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs-ng/linux-x64/nsbc-quickjs-ng differ diff --git a/tools/bytecode-compiler/bin/quickjs-ng/win32-x64/nsbc-quickjs-ng.exe b/tools/bytecode-compiler/bin/quickjs-ng/win32-x64/nsbc-quickjs-ng.exe new file mode 100644 index 00000000..0d857290 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs-ng/win32-x64/nsbc-quickjs-ng.exe differ diff --git a/tools/bytecode-compiler/bin/quickjs/darwin-arm64/nsbc-quickjs b/tools/bytecode-compiler/bin/quickjs/darwin-arm64/nsbc-quickjs new file mode 100755 index 00000000..c51f953f Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs/darwin-arm64/nsbc-quickjs differ diff --git a/tools/bytecode-compiler/bin/quickjs/darwin-x64/nsbc-quickjs b/tools/bytecode-compiler/bin/quickjs/darwin-x64/nsbc-quickjs new file mode 100755 index 00000000..c3100916 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs/darwin-x64/nsbc-quickjs differ diff --git a/tools/bytecode-compiler/bin/quickjs/linux-arm64/nsbc-quickjs b/tools/bytecode-compiler/bin/quickjs/linux-arm64/nsbc-quickjs new file mode 100755 index 00000000..ec89bd4f Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs/linux-arm64/nsbc-quickjs differ diff --git a/tools/bytecode-compiler/bin/quickjs/linux-x64/nsbc-quickjs b/tools/bytecode-compiler/bin/quickjs/linux-x64/nsbc-quickjs new file mode 100755 index 00000000..f50ae842 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs/linux-x64/nsbc-quickjs differ diff --git a/tools/bytecode-compiler/bin/quickjs/win32-x64/nsbc-quickjs.exe b/tools/bytecode-compiler/bin/quickjs/win32-x64/nsbc-quickjs.exe new file mode 100644 index 00000000..63b8d712 Binary files /dev/null and b/tools/bytecode-compiler/bin/quickjs/win32-x64/nsbc-quickjs.exe differ diff --git a/tools/bytecode-compiler/compile-bytecode.js b/tools/bytecode-compiler/compile-bytecode.js new file mode 100644 index 00000000..f404f647 --- /dev/null +++ b/tools/bytecode-compiler/compile-bytecode.js @@ -0,0 +1,280 @@ +#!/usr/bin/env node +/** + * Compile-time bytecode generator for NativeScript Android (engine-agnostic). + * + * Walks a directory of app JavaScript (normally the `app/` folder inside the + * merged Android assets) and replaces every `.js` file with pre-compiled engine + * bytecode for the selected JS engine. The files keep their `.js` names so the + * runtime's module resolver is unaffected; the runtime detects the engine's + * bytecode magic at load time and runs it directly instead of compiling source, + * which dramatically improves time-to-interactive. + * + * This driver is generic. Everything engine-specific — which compiler binary to + * run, how to build its command line, and what its bytecode looks like — lives + * in a small adapter under ./lib (see lib/index.js for the contract). The only + * Hermes-specific thing anywhere is running `hermesc`, encapsulated in + * lib/hermes.js. + * + * IMPORTANT — module wrapping must match the runtime exactly. + * The runtime wraps every `require`d module in a function before executing it + * (see ModuleInternal::MODULE_PROLOGUE / MODULE_EPILOGUE in + * runtime/src/main/cpp/runtime/module/ModuleInternal.cpp). Running a module's + * bytecode must therefore yield that same wrapper function, so we wrap the + * source with the identical prologue/epilogue before compiling. Files the + * runtime runs unwrapped (raw scripts executed via Runtime::RunScript, e.g. + * `internal/ts_helpers.js`) are compiled as-is. Keep the two constants below + * byte-for-byte identical to ModuleInternal.cpp. + * + * Usage: + * node compile-bytecode.js --app --engine [options] + * + * Options: + * --app Directory to process recursively (required). + * --engine ns_engine value, e.g. HERMES, QUICKJS, QUICKJS_NG, PRIMJS, + * V8-13, JSC (required). Engines without a bytecode compiler + * are a no-op. + * --bin-dir Root of the host-specific compiler binaries + * (default: /bin). Resolved as + * ///. + * --compiler Explicit compiler binary path (overrides --bin-dir lookup). + * --raw Comma-separated app-relative paths compiled unwrapped + * (default: "internal/ts_helpers.js"). + * --source-maps Emit source maps next to each file (.map) when the + * engine's compiler supports them. + * --optimize Override the engine's default optimization flag. + * --strict Fail if the compiler binary is missing (default: warn and + * leave the app as source). + * --keep-going Leave a file as source if its compile fails, instead of + * aborting the build (default: abort). + * --verbose Log every file processed. + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); +const { spawnSync } = require('child_process'); + +const { adapterForEngine, hostKey } = require('./lib'); + +// MUST stay identical to ModuleInternal::MODULE_PROLOGUE / MODULE_EPILOGUE. +const MODULE_PROLOGUE = '(function(module, exports, require, __filename, __dirname){ '; +const MODULE_EPILOGUE = '\n})'; + +function parseArgs(argv) { + const opts = { + app: null, + engine: null, + binDir: path.join(__dirname, 'bin'), + compiler: null, + raw: ['internal/ts_helpers.js'], + sourceMaps: false, + optimize: undefined, + strict: false, + keepGoing: false, + verbose: false, + }; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + switch (a) { + case '--app': opts.app = argv[++i]; break; + case '--engine': opts.engine = argv[++i]; break; + case '--bin-dir': opts.binDir = argv[++i]; break; + case '--compiler': opts.compiler = argv[++i]; break; + case '--raw': opts.raw = argv[++i].split(',').map((s) => s.trim()).filter(Boolean); break; + case '--source-maps': opts.sourceMaps = true; break; + case '--optimize': opts.optimize = argv[++i]; break; + case '--strict': opts.strict = true; break; + case '--keep-going': opts.keepGoing = true; break; + case '--verbose': opts.verbose = true; break; + default: throw new Error(`Unknown argument: ${a}`); + } + } + if (!opts.app) throw new Error('--app is required'); + if (!opts.engine) throw new Error('--engine is required'); + return opts; +} + +function listJsFiles(dir) { + const out = []; + const walk = (d) => { + for (const entry of fs.readdirSync(d, { withFileTypes: true })) { + const full = path.join(d, entry.name); + if (entry.isDirectory()) walk(full); + else if (entry.isFile() && entry.name.endsWith('.js')) out.push(full); + } + }; + walk(dir); + return out; +} + +function readHead(file, n) { + const fd = fs.openSync(file, 'r'); + try { + const head = Buffer.alloc(n); + const read = fs.readSync(fd, head, 0, n, 0); + return head.subarray(0, read); + } finally { + fs.closeSync(fd); + } +} + +/** Normalize to a forward-slashed app-relative path for raw-file matching. */ +function relKey(appDir, file) { + return path.relative(appDir, file).split(path.sep).join('/'); +} + +function resolveCompiler(opts, adapter, host) { + if (opts.compiler) return opts.compiler; + return path.join(opts.binDir, adapter.key, host, adapter.binName(host)); +} + +function isPlaceholder(file) { + // Dummy host slots are laid out as small text (README/.gitkeep), never a real + // executable. Treat anything tiny as "not a real compiler". + try { + return fs.statSync(file).size < 1024; + } catch (_) { + return true; + } +} + +function compileFile(opts, adapter, compiler, file, isRaw, appDir) { + const source = fs.readFileSync(file, 'utf8'); + const wrapped = isRaw ? source : MODULE_PROLOGUE + source + MODULE_EPILOGUE; + + // Embed the app-relative path as the module's source name so runtime stack + // traces point at the app's file (e.g. "shared/index.js"), not a throwaway + // temp path. Every engine derives the embedded name from the *input path we + // pass*: hermesc bakes in its argv path verbatim (it never absolutizes a + // cwd-relative path), and the qjs/primjs shims hand their input path straight + // to JS_Eval/LEPUS_Eval as the filename. So we mirror the wrapped source under + // a temp root at its app-relative path and run the compiler from that root with + // the relative path as input — the embedded name becomes exactly `rel`. + // + // The device-absolute "file:///data/data//files/app/..." form can't be + // used here: hermesc must be given a real, openable host file (no URL scheme / + // device path), and that prefix is device-specific anyway. The app-relative + // path is the identity all engines can embed consistently. + const rel = relKey(appDir, file); + const relParts = rel.split('/'); + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nsbc-')); + const tmpSrc = path.join(tmpDir, ...relParts); + const tmpOut = path.join(tmpDir, path.basename(file) + '.bc'); + const wantMap = opts.sourceMaps && adapter.supportsSourceMaps; + try { + fs.mkdirSync(path.dirname(tmpSrc), { recursive: true }); + fs.writeFileSync(tmpSrc, wrapped); + const optimize = opts.optimize !== undefined ? opts.optimize : adapter.defaultOptimize; + const args = adapter.buildArgs({ + // Pass the app-relative path (forward-slashed); with cwd = tmpDir the + // compiler opens tmpDir/ and embeds "" as the source name. + input: rel, + output: tmpOut, + sourceMap: wantMap ? adapter.sourceMapOutput(tmpOut) : null, + optimize, + }); + const res = spawnSync(compiler, args, { cwd: tmpDir, encoding: 'utf8' }); + if (res.status !== 0) { + const detail = (res.stderr || res.stdout || (res.error && res.error.message) || '').toString().trim(); + throw new Error(`compiler failed for ${file} (exit ${res.status}):\n${detail}`); + } + const bytecode = fs.readFileSync(tmpOut); + if (adapter.magic && !adapter.isBytecode(bytecode.subarray(0, adapter.magic.length))) { + throw new Error(`compiler produced non-bytecode output for ${file}`); + } + // Overwrite in place: the file keeps its .js name; the runtime detects the magic. + fs.writeFileSync(file, bytecode); + if (wantMap) { + const mapSrc = adapter.sourceMapOutput(tmpOut); + if (fs.existsSync(mapSrc)) fs.copyFileSync(mapSrc, file + '.map'); + } + return bytecode.length; + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } +} + +function main() { + const opts = parseArgs(process.argv); + const appDir = path.resolve(opts.app); + const host = hostKey(); + + const adapter = adapterForEngine(opts.engine); + if (!adapter) { + console.log(`[bytecode] engine ${opts.engine} has no bytecode compiler — leaving app as source.`); + return; + } + if (!adapter.ready) { + console.log(`[bytecode] ${adapter.key} bytecode is not enabled yet — leaving app as source.`); + return; + } + if (!fs.existsSync(appDir)) { + console.log(`[bytecode] app dir not found, nothing to do: ${appDir}`); + return; + } + + const compiler = resolveCompiler(opts, adapter, host); + if (!fs.existsSync(compiler) || (!opts.compiler && isPlaceholder(compiler))) { + const msg = `[bytecode] no ${adapter.key} compiler for host ${host} at ${compiler}`; + if (opts.strict) throw new Error(msg + ' (--strict)'); + console.warn(`${msg} — leaving app as source.`); + return; + } + + // Ensure the compiler is executable. Binaries shipped via CI artifact zips, + // npm packages, or restored without the exec bit lose it, which surfaces as a + // spawn EACCES. (No-op on Windows.) + if (process.platform !== 'win32') { + try { + fs.chmodSync(compiler, 0o755); + } catch (err) { + console.warn(`[bytecode] could not chmod +x ${compiler}: ${err.message}`); + } + } + + const rawSet = new Set(opts.raw); + const magicLen = adapter.magic ? adapter.magic.length : 0; + const files = listJsFiles(appDir); + let compiled = 0; + let skipped = 0; + let rawCount = 0; + const start = Date.now(); + + for (const file of files) { + if (magicLen && adapter.isBytecode(readHead(file, magicLen))) { + // Idempotent: safe to re-run on already-processed (e.g. cached) assets. + skipped++; + continue; + } + const isRaw = rawSet.has(relKey(appDir, file)); + try { + compileFile(opts, adapter, compiler, file, isRaw, appDir); + compiled++; + if (isRaw) rawCount++; + if (opts.verbose) { + console.log(`[bytecode] compiled${isRaw ? ' (raw)' : ''}: ${relKey(appDir, file)}`); + } + } catch (err) { + if (opts.keepGoing) { + console.warn(`[bytecode] WARNING: leaving as source: ${err.message}`); + } else { + throw err; + } + } + } + + const ms = Date.now() - start; + console.log( + `[bytecode] ${adapter.key}/${host}: compiled ${compiled} file(s) ` + + `(${rawCount} raw), skipped ${skipped} already-compiled, in ${ms}ms.` + ); +} + +try { + main(); +} catch (err) { + console.error(`[bytecode] ERROR: ${err.message}`); + process.exit(1); +} diff --git a/tools/bytecode-compiler/lib/hermes.js b/tools/bytecode-compiler/lib/hermes.js new file mode 100644 index 00000000..71e2b81b --- /dev/null +++ b/tools/bytecode-compiler/lib/hermes.js @@ -0,0 +1,40 @@ +'use strict'; + +// Hermes (HBC) adapter. The only engine-specific logic is: which binary to run +// (hermesc), how to build its command line, and what its bytecode looks like. +// +// Bytecode magic: first 8 bytes (little-endian) of every HBC file. +// See hermes BytecodeFileFormat.h (MAGIC = 0x1F1903C103BC1FC6). Must match the +// runtime detection in napi/hermes/jsr.cpp (js_run_bytecode_file). +const MAGIC = Buffer.from([0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f]); + +module.exports = { + key: 'hermes', + engineKeys: ['HERMES'], + ready: true, + magic: MAGIC, + supportsSourceMaps: true, + defaultOptimize: '-O', + + binName(hostKey) { + return hostKey.startsWith('win32') ? 'hermesc.exe' : 'hermesc'; + }, + + isBytecode(head) { + return head.length >= MAGIC.length && head.subarray(0, MAGIC.length).equals(MAGIC); + }, + + buildArgs({ input, output, sourceMap, optimize }) { + const args = []; + if (optimize) args.push(optimize); + args.push('-emit-binary'); + if (sourceMap) args.push('-output-source-map'); + args.push('-out', output, input); + return args; + }, + + // hermesc writes the source map to ".map". + sourceMapOutput(output) { + return output + '.map'; + }, +}; diff --git a/tools/bytecode-compiler/lib/index.js b/tools/bytecode-compiler/lib/index.js new file mode 100644 index 00000000..ae917f11 --- /dev/null +++ b/tools/bytecode-compiler/lib/index.js @@ -0,0 +1,49 @@ +'use strict'; + +/** + * Engine registry + host resolution for the generic bytecode compiler. + * + * The driver (compile-bytecode.js) is engine-agnostic: it walks the app JS, + * wraps modules, handles idempotency and source maps, and shells out to a + * compiler binary. Everything engine-specific — which binary to run, how to + * build its command line, what its bytecode looks like — lives in an adapter + * under this folder and is registered below. + * + * Adapter contract — see hermes.js for a fully-implemented example: + * key : string adapter id (also the bin/ dir) + * engineKeys : string[] ns_engine values that select it (case-insensitive) + * ready : boolean false => driver skips (leaves source) even if a binary exists. + * Gate this on BOTH a real compiler AND runtime support for + * the produced bytecode, so we never ship a bytecode file the + * runtime can't execute. + * binName(hostKey) : string compiler filename for the host (e.g. 'hermesc'/'hermesc.exe') + * magic : Buffer leading bytes every bytecode file for this engine starts with + * supportsSourceMaps : boolean + * defaultOptimize : string|null optimization flag passed as {optimize} + * isBytecode(head) : boolean true if `head` (>= magic.length bytes) is this engine's bytecode + * buildArgs(ctx) : string[] argv (after the binary) to compile ctx.input -> ctx.output. + * ctx = { input, output, sourceMap|null, optimize } + * sourceMapOutput(out): string path the compiler writes the map to (moved to .map) + */ + +const HOST_KEYS = ['darwin-arm64', 'darwin-x64', 'linux-x64', 'linux-arm64', 'win32-x64']; + +function hostKey() { + return `${process.platform}-${process.arch}`; +} + +const adapters = [ + require('./hermes'), + require('./quickjs'), + require('./quickjs-ng'), + require('./primjs'), +]; + +/** Resolve an adapter from an ns_engine value (e.g. "HERMES", "QUICKJS_NG"). */ +function adapterForEngine(engine) { + if (!engine) return null; + const e = String(engine).toUpperCase(); + return adapters.find((a) => a.engineKeys.some((k) => k.toUpperCase() === e)) || null; +} + +module.exports = { adapters, adapterForEngine, hostKey, HOST_KEYS }; diff --git a/tools/bytecode-compiler/lib/primjs.js b/tools/bytecode-compiler/lib/primjs.js new file mode 100644 index 00000000..6577c437 --- /dev/null +++ b/tools/bytecode-compiler/lib/primjs.js @@ -0,0 +1,32 @@ +'use strict'; + +// PrimJS adapter. PrimJS is QuickJS-derived with a LEPUS_* API, so it uses its +// own shim (native/primjs-compile.c) but the same container format. The runtime +// reads this container in napi/primjs js_execute_script (strip the 12-byte +// header, LEPUS_ReadObject + LEPUS_EvalFunction); see quickjs.js. +const MAGIC = Buffer.from('NSBCPJS\0', 'latin1'); // 8 bytes, must match the shim + runtime + +module.exports = { + key: 'primjs', + engineKeys: ['PRIMJS'], + ready: true, + magic: MAGIC, + supportsSourceMaps: false, + defaultOptimize: null, + + binName(hostKey) { + return hostKey.startsWith('win32') ? 'nsbc-primjs.exe' : 'nsbc-primjs'; + }, + + isBytecode(head) { + return head.length >= MAGIC.length && head.subarray(0, MAGIC.length).equals(MAGIC); + }, + + buildArgs({ input, output }) { + return [input, output]; + }, + + sourceMapOutput(output) { + return output + '.map'; + }, +}; diff --git a/tools/bytecode-compiler/lib/quickjs-ng.js b/tools/bytecode-compiler/lib/quickjs-ng.js new file mode 100644 index 00000000..2bb6123f --- /dev/null +++ b/tools/bytecode-compiler/lib/quickjs-ng.js @@ -0,0 +1,32 @@ +'use strict'; + +// QuickJS-NG adapter. Same JS_* API and shim as QuickJS (native/qjs-compile.c), +// but a distinct engine whose bytecode is NOT interchangeable — hence its own +// magic and bin/quickjs-ng/ directory. The runtime reads this container in +// napi/quickjs js_execute_script (the __QUICKJS_NG__ build); see quickjs.js. +const MAGIC = Buffer.from('NSBCNGS\0', 'latin1'); // 8 bytes, must match the shim + runtime + +module.exports = { + key: 'quickjs-ng', + engineKeys: ['QUICKJS_NG'], + ready: true, + magic: MAGIC, + supportsSourceMaps: false, + defaultOptimize: null, + + binName(hostKey) { + return hostKey.startsWith('win32') ? 'nsbc-quickjs-ng.exe' : 'nsbc-quickjs-ng'; + }, + + isBytecode(head) { + return head.length >= MAGIC.length && head.subarray(0, MAGIC.length).equals(MAGIC); + }, + + buildArgs({ input, output }) { + return [input, output]; + }, + + sourceMapOutput(output) { + return output + '.map'; + }, +}; diff --git a/tools/bytecode-compiler/lib/quickjs.js b/tools/bytecode-compiler/lib/quickjs.js new file mode 100644 index 00000000..91d73bc0 --- /dev/null +++ b/tools/bytecode-compiler/lib/quickjs.js @@ -0,0 +1,38 @@ +'use strict'; + +// QuickJS (bellard) adapter. +// +// The compiler is our blob-emitting shim (native/qjs-compile.c), built per host +// by .github/workflows/bytecode-compilers.yml. It writes a NativeScript bytecode +// container: 8-byte magic + 4-byte LE format version + JS_WriteObject payload. +// +// The runtime side reads this container in napi/quickjs js_execute_script (strips +// the 12-byte header, JS_ReadObject(JS_READ_OBJ_BYTECODE) + JS_EvalFunction). +// Keep the CLI's engine ref in step with the runtime's vendored QuickJS. +const MAGIC = Buffer.from('NSBCQJS\0', 'latin1'); // 8 bytes, must match the shim + runtime + +module.exports = { + key: 'quickjs', + engineKeys: ['QUICKJS'], + ready: true, + magic: MAGIC, + supportsSourceMaps: false, + defaultOptimize: null, + + binName(hostKey) { + return hostKey.startsWith('win32') ? 'nsbc-quickjs.exe' : 'nsbc-quickjs'; + }, + + isBytecode(head) { + return head.length >= MAGIC.length && head.subarray(0, MAGIC.length).equals(MAGIC); + }, + + // Our shim takes: + buildArgs({ input, output }) { + return [input, output]; + }, + + sourceMapOutput(output) { + return output + '.map'; + }, +}; diff --git a/tools/bytecode-compiler/native/primjs-compile.c b/tools/bytecode-compiler/native/primjs-compile.c new file mode 100644 index 00000000..3be86174 --- /dev/null +++ b/tools/bytecode-compiler/native/primjs-compile.c @@ -0,0 +1,99 @@ +/* + * NativeScript PrimJS bytecode compiler shim. + * + * PrimJS is QuickJS-derived but renames the public API to the LEPUS_* prefix. + * This is the LEPUS_* twin of qjs-compile.c: read a JS file, compile it with + * LEPUS_EVAL_FLAG_COMPILE_ONLY, serialize via LEPUS_WriteObject(LEPUS_WRITE_OBJ_BYTECODE), + * and write the NativeScript bytecode container: + * + * [8 bytes magic "NSBCPJS\0"][4 bytes format version, little-endian][payload] + * + * The runtime (napi/primjs `js_run_bytecode_file`) checks the magic, skips the + * 12-byte header, and hands the payload to LEPUS_ReadObject + LEPUS_EvalFunction. + * The magic MUST match the `magic` in lib/primjs.js and the runtime. + * + * Build against the cloned PrimJS engine, e.g.: + * cc -O2 -I/include -o nsbc-primjs primjs-compile.c -lm -lpthread + * (PrimJS's exact include paths / link targets come from its own build; see the + * workflow's build-primjs step.) + */ +#include +#include +#include +#include + +#include "quickjs.h" + +#ifndef NSBC_MAGIC +#define NSBC_MAGIC "NSBCPJS" +#endif +#define NSBC_FORMAT_VERSION 1u + +static uint8_t *read_file(const char *path, size_t *out_len) { + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } + long n = ftell(f); + if (n < 0 || fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } + uint8_t *buf = (uint8_t *)malloc((size_t)n + 1); + if (!buf) { fclose(f); return NULL; } + size_t rd = fread(buf, 1, (size_t)n, f); + fclose(f); + if (rd != (size_t)n) { free(buf); return NULL; } + buf[n] = '\0'; + *out_len = (size_t)n; + return buf; +} + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 2; + } + const char *in_path = argv[1]; + const char *out_path = argv[2]; + + size_t src_len = 0; + uint8_t *src = read_file(in_path, &src_len); + if (!src) { fprintf(stderr, "cannot read %s\n", in_path); return 1; } + + LEPUSRuntime *rt = LEPUS_NewRuntime(); + LEPUSContext *ctx = rt ? LEPUS_NewContext(rt) : NULL; + if (!ctx) { fprintf(stderr, "primjs init failed\n"); free(src); return 1; } + + LEPUSValue obj = LEPUS_Eval(ctx, (const char *)src, src_len, in_path, + LEPUS_EVAL_TYPE_GLOBAL | LEPUS_EVAL_FLAG_COMPILE_ONLY); + free(src); + if (LEPUS_IsException(obj)) { + LEPUSValue exc = LEPUS_GetException(ctx); + const char *msg = LEPUS_ToCString(ctx, exc); + fprintf(stderr, "compile error in %s: %s\n", in_path, msg ? msg : "(unknown)"); + if (msg) LEPUS_FreeCString(ctx, msg); + LEPUS_FreeValue(ctx, exc); + return 1; + } + + size_t bc_len = 0; + uint8_t *bc = LEPUS_WriteObject(ctx, &bc_len, obj, LEPUS_WRITE_OBJ_BYTECODE); + LEPUS_FreeValue(ctx, obj); + if (!bc) { fprintf(stderr, "LEPUS_WriteObject failed for %s\n", in_path); return 1; } + + FILE *out = fopen(out_path, "wb"); + if (!out) { fprintf(stderr, "cannot write %s\n", out_path); lepus_free(ctx, bc); return 1; } + + unsigned char header[12]; + memcpy(header, NSBC_MAGIC, 7); + header[7] = 0; + uint32_t ver = NSBC_FORMAT_VERSION; + header[8] = (unsigned char)(ver & 0xff); + header[9] = (unsigned char)((ver >> 8) & 0xff); + header[10] = (unsigned char)((ver >> 16) & 0xff); + header[11] = (unsigned char)((ver >> 24) & 0xff); + + int ok = (fwrite(header, 1, sizeof(header), out) == sizeof(header)) && + (bc_len == 0 || fwrite(bc, 1, bc_len, out) == bc_len); + fclose(out); + lepus_free(ctx, bc); + if (!ok) { fprintf(stderr, "write failed for %s\n", out_path); return 1; } + return 0; +} diff --git a/tools/bytecode-compiler/native/qjs-compile.c b/tools/bytecode-compiler/native/qjs-compile.c new file mode 100644 index 00000000..de224259 --- /dev/null +++ b/tools/bytecode-compiler/native/qjs-compile.c @@ -0,0 +1,110 @@ +/* + * NativeScript QuickJS / QuickJS-NG bytecode compiler shim. + * + * The stock `qjsc` emits a C source array, not a loadable blob, so it can't feed + * our runtime. This tiny tool instead reads a JS file, compiles it with + * JS_EVAL_FLAG_COMPILE_ONLY, serializes it via JS_WriteObject(JS_WRITE_OBJ_BYTECODE), + * and writes a small NativeScript bytecode *container*: + * + * [8 bytes magic][4 bytes format version, little-endian][JS_WriteObject payload] + * + * The runtime (napi/quickjs `js_run_bytecode_file`) checks the magic, skips the + * 12-byte header, and hands the payload to JS_ReadObject + JS_EvalFunction. The + * container gives robust detection (QuickJS's own serialized output has no strong + * leading magic) and lets us version the format independently of the engine. + * + * One source serves both engines; the magic is chosen at build time so a QuickJS + * blob is never mistaken for a QuickJS-NG blob (their bytecode is incompatible): + * -DNSBC_MAGIC='"NSBCQJS"' QuickJS (bellard) + * -DNSBC_MAGIC='"NSBCNGS"' QuickJS-NG + * The 7-char string plus a trailing NUL byte is the 8-byte magic; it MUST match + * the `magic` in the corresponding lib/.js adapter and the runtime. + * + * Build (linked against the cloned upstream engine), e.g.: + * cc -O2 -DNSBC_MAGIC='"NSBCQJS"' -I -o nsbc-quickjs \ + * qjs-compile.c /libquickjs.a -lm -lpthread + */ +#include +#include +#include +#include + +#include "quickjs.h" + +#ifndef NSBC_MAGIC +#define NSBC_MAGIC "NSBCQJS" +#endif +#define NSBC_FORMAT_VERSION 1u + +static uint8_t *read_file(const char *path, size_t *out_len) { + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } + long n = ftell(f); + if (n < 0 || fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } + uint8_t *buf = (uint8_t *)malloc((size_t)n + 1); + if (!buf) { fclose(f); return NULL; } + size_t rd = fread(buf, 1, (size_t)n, f); + fclose(f); + if (rd != (size_t)n) { free(buf); return NULL; } + buf[n] = '\0'; + *out_len = (size_t)n; + return buf; +} + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 2; + } + const char *in_path = argv[1]; + const char *out_path = argv[2]; + + size_t src_len = 0; + uint8_t *src = read_file(in_path, &src_len); + if (!src) { fprintf(stderr, "cannot read %s\n", in_path); return 1; } + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = rt ? JS_NewContext(rt) : NULL; + if (!ctx) { fprintf(stderr, "quickjs init failed\n"); free(src); return 1; } + + /* Compile only — do not run. The completion value of the compiled top-level + * is the module wrapper function, exactly as when running the source. */ + JSValue obj = JS_Eval(ctx, (const char *)src, src_len, in_path, + JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_COMPILE_ONLY); + free(src); + if (JS_IsException(obj)) { + JSValue exc = JS_GetException(ctx); + const char *msg = JS_ToCString(ctx, exc); + fprintf(stderr, "compile error in %s: %s\n", in_path, msg ? msg : "(unknown)"); + if (msg) JS_FreeCString(ctx, msg); + JS_FreeValue(ctx, exc); + return 1; + } + + size_t bc_len = 0; + uint8_t *bc = JS_WriteObject(ctx, &bc_len, obj, JS_WRITE_OBJ_BYTECODE); + JS_FreeValue(ctx, obj); + if (!bc) { fprintf(stderr, "JS_WriteObject failed for %s\n", in_path); return 1; } + + FILE *out = fopen(out_path, "wb"); + if (!out) { fprintf(stderr, "cannot write %s\n", out_path); js_free(ctx, bc); return 1; } + + unsigned char header[12]; + memcpy(header, NSBC_MAGIC, 7); + header[7] = 0; + uint32_t ver = NSBC_FORMAT_VERSION; + header[8] = (unsigned char)(ver & 0xff); + header[9] = (unsigned char)((ver >> 8) & 0xff); + header[10] = (unsigned char)((ver >> 16) & 0xff); + header[11] = (unsigned char)((ver >> 24) & 0xff); + + int ok = (fwrite(header, 1, sizeof(header), out) == sizeof(header)) && + (bc_len == 0 || fwrite(bc, 1, bc_len, out) == bc_len); + fclose(out); + js_free(ctx, bc); + /* Process is about to exit; skip JS_FreeContext/JS_FreeRuntime to keep the + * shim simple and robust across engine versions. */ + if (!ok) { fprintf(stderr, "write failed for %s\n", out_path); return 1; } + return 0; +} diff --git a/tools/patches/README.md b/tools/patches/README.md new file mode 100644 index 00000000..4f4aada1 --- /dev/null +++ b/tools/patches/README.md @@ -0,0 +1,46 @@ +# Engine source patches + +The QuickJS / QuickJS-NG engine sources under +`test-app/runtime/src/main/cpp/napi/quickjs/{source,source_ng}` are git submodules +pinned to an upstream commit. Our hand-written NativeScript changes are kept here +as patches applied on top of that pristine checkout, rather than baked into a +vendored copy. + +``` +tools/patches/ + quickjs/ 0001-nativescript-local-changes.patch (bellard/quickjs) + quickjs_ng/ 0001-nativescript-local-changes.patch (quickjs-ng/quickjs) +``` + +Both patches touch only `quickjs.c` / `quickjs.h`, which is where all our edits +live: the `USE_HOST_OBJECT` host-object hooks in `JS_GetPropertyInternal` (host +objects act as a non-masking prototype fallback) and the functions after the +`/* CUSTOM */` marker at the bottom of each file. + +## Managing them + +`scripts/vendor-engines-as-submodules.sh` owns this setup: + +- **quickjs** patch is *auto-generated* by diffing the submodule against upstream + (the vendored base tracks bellard master closely, so the diff is exactly our + edits). +- **quickjs_ng** patch is *hand-migrated* and used as-is (`ENGINE_PATCH_MODE=manual`). + The old vendored source was v0.15.1 (BC_VERSION 26) while the submodule is pinned + to master `d950d55` (BC_VERSION 27), so a diff can't isolate our edits — the + patch was ported onto master by hand and verified to compile. + +On a fresh checkout / in CI: + +``` +git submodule update --init \ + test-app/runtime/src/main/cpp/napi/quickjs/source \ + test-app/runtime/src/main/cpp/napi/quickjs/source_ng +scripts/vendor-engines-as-submodules.sh --apply-only +``` + +## Compatibility + +The submodule commit sets the engine's `BC_VERSION`. The compile-time bytecode +CLI (see `.github/workflows/bytecode-compilers.yml`) MUST be built from the same +commit, or `JS_ReadObject` will reject the produced bytecode at runtime. Keep the +workflow refs and the submodule pins in lock-step. diff --git a/tools/patches/quickjs/0001-nativescript-local-changes.patch b/tools/patches/quickjs/0001-nativescript-local-changes.patch new file mode 100644 index 00000000..a58f53e9 --- /dev/null +++ b/tools/patches/quickjs/0001-nativescript-local-changes.patch @@ -0,0 +1,216 @@ +diff --git a/quickjs.c b/quickjs.c +index e3716a4..0493a81 100644 +--- a/quickjs.c ++++ b/quickjs.c +@@ -7223,7 +7223,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) + + void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) + { +- fprintf(fp, "QuickJS memory usage -- " CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", ++ fprintf(fp, "QuickJS memory usage -- version, %d-bit, malloc limit: %"PRId64"\n\n", + (int)sizeof(void *) * 8, s->malloc_limit); + #if 1 + if (rt) { +@@ -7632,7 +7632,9 @@ static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) + + JSValue JS_NewError(JSContext *ctx) + { +- return JS_NewObjectClass(ctx, JS_CLASS_ERROR); ++ JSValue error = JS_NewObjectClass(ctx, JS_CLASS_ERROR); ++ build_backtrace(ctx, error, NULL, 0, 0, 0); ++ return error; + } + + static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, +@@ -8207,6 +8209,13 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + return 0; + } + ++#ifdef USE_HOST_OBJECT ++/* NativeScript host-object exotic methods (defined in quickjs-api.c). Compared ++ by address below so the get_property handler can be treated as a non-masking ++ fallback for host objects only, leaving Proxy (js_proxy_get) authoritative. */ ++extern JSClassExoticMethods NapiHostObjectExoticMethods; ++#endif ++ + JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst this_obj, + BOOL throw_ref_error) +@@ -8215,6 +8224,11 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag; ++#ifdef USE_HOST_OBJECT ++ /* A host object whose get_property we deferred: the prototype chain is ++ consulted first, and this is called only if nothing was found. */ ++ JSObject *deferred_host = NULL; ++#endif + + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { +@@ -8319,14 +8333,27 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + 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_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); +- retval = em->get_property(ctx, obj1, prop, this_obj); +- JS_FreeValue(ctx, obj1); +- return retval; ++#ifdef USE_HOST_OBJECT ++ /* NativeScript host objects use get_property as a ++ NON-masking fallback: defer it so the prototype chain ++ (native methods / field accessors) is consulted first, ++ and call it only if nothing is found (see after loop). ++ Proxy and other exotics stay authoritative. */ ++ if (em == &NapiHostObjectExoticMethods) { ++ if (!deferred_host) ++ deferred_host = p; ++ } else ++#endif ++ { ++ 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_DupValue(ctx, 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; +@@ -8356,6 +8383,17 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + if (!p) + break; + } ++#ifdef USE_HOST_OBJECT ++ /* Nothing found on the prototype chain: now consult the deferred host ++ object's get_property (expandos, numeric indices, dynamic members). */ ++ if (deferred_host) { ++ JSValue obj1, retval; ++ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, deferred_host)); ++ retval = NapiHostObjectExoticMethods.get_property(ctx, obj1, prop, this_obj); ++ JS_FreeValue(ctx, obj1); ++ return retval; ++ } ++#endif + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { +@@ -61422,3 +61460,84 @@ int JS_AddIntrinsicWeakRef(JSContext *ctx) + JS_FreeValue(ctx, obj); + return 0; + } ++ ++/* CUSTOM */ ++ ++int JS_IsArrayBuffer2(JSContext *ctx, JSValueConst val) ++{ ++ JSObject *p; ++ if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { ++ p = JS_VALUE_GET_OBJ(val); ++ return p->class_id == JS_CLASS_ARRAY_BUFFER || p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER; ++ } else { ++ return false; ++ } ++} ++ ++JSValue JS_NewString16(JSContext *ctx, const uint16_t *buf, int len) ++{ ++ return js_new_string16_len(ctx, buf, len); ++} ++ ++JSValue JS_GetPropertyInt64_2(JSContext *ctx, JSValueConst obj, int64_t idx) { ++ return JS_GetPropertyInt64(ctx, obj, idx); ++} ++ ++JSValue JS_WeakRef_Deref(JSContext *ctx, JSValueConst this_val) { ++ return js_weakref_deref(ctx, this_val, 0, NULL); ++} ++ ++int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres) { ++ return js_get_length64(ctx, pres, obj); ++} ++ ++ ++static bool is_typed_array(JSClassID class_id) ++{ ++ return class_id >= JS_CLASS_UINT8C_ARRAY && class_id <= JS_CLASS_FLOAT64_ARRAY; ++} ++ ++int JS_GetTypedArrayType(JSValueConst obj) ++{ ++ JSClassID class_id = JS_GetClassID(obj); ++ if (is_typed_array(class_id)) ++ return class_id - JS_CLASS_UINT8C_ARRAY; ++ else ++ return -1; ++} ++ ++JS_BOOL JS_IsDataView(JSValueConst val) ++{ ++ return JS_CLASS_DATAVIEW == JS_GetClassID(val); ++} ++ ++int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val) ++{ ++ return JS_ToBigInt64Free(ctx, (int64_t *)pres, JS_DupValue(ctx, val)); ++} ++ ++JS_BOOL JS_IsStrictEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2) ++{ ++ return js_strict_eq2(ctx, JS_DupValue(ctx, op1), JS_DupValue(ctx,op2), JS_EQ_STRICT); ++} ++ ++int JS_SealObject(JSContext *ctx, JSValueConst obj) ++{ ++ JSValue value = js_object_seal(ctx, JS_UNDEFINED, 1, &obj, 0); ++ int result = JS_IsException(value) ? -1 : true; ++ JS_FreeValue(ctx, value); ++ return result; ++} ++ ++int JS_FreezeObject(JSContext *ctx, JSValueConst obj) ++{ ++ JSValue value = js_object_seal(ctx, JS_UNDEFINED, 1, &obj, 1); ++ int result = JS_IsException(value) ? -1 : true; ++ JS_FreeValue(ctx, value); ++ return result; ++} ++ ++size_t JS_GetGCThreshold(JSRuntime *rt) { ++ return rt->malloc_gc_threshold; ++} ++ +diff --git a/quickjs.h b/quickjs.h +index 476d735..ded9e30 100644 +--- a/quickjs.h ++++ b/quickjs.h +@@ -1170,6 +1170,22 @@ void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_o + void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque, + JSValueConst val, const JSPrintValueOptions *options); + ++/* CUSTOM */ ++ ++int JS_IsArrayBuffer2(JSContext *ctx, JSValueConst val); ++JSValue JS_NewString16(JSContext *ctx, const uint16_t *buf, int len); ++JSValue JS_GetPropertyInt64_2(JSContext *ctx, JSValueConst obj, int64_t idx); ++JSValue JS_WeakRef_Deref(JSContext *ctx, JSValueConst this_val); ++int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres); ++int JS_GetTypedArrayType(JSValueConst obj); ++JS_BOOL JS_IsDataView(JSValueConst val); ++int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val); ++JS_BOOL JS_IsStrictEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2); ++int JS_SealObject(JSContext *ctx, JSValueConst obj); ++int JS_FreezeObject(JSContext *ctx, JSValueConst obj); ++size_t JS_GetGCThreshold(JSRuntime *rt); ++ ++ + #undef js_unlikely + #undef js_force_inline + diff --git a/tools/patches/quickjs_ng/0001-nativescript-local-changes.patch b/tools/patches/quickjs_ng/0001-nativescript-local-changes.patch new file mode 100644 index 00000000..27182126 --- /dev/null +++ b/tools/patches/quickjs_ng/0001-nativescript-local-changes.patch @@ -0,0 +1,135 @@ +diff --git a/quickjs.c b/quickjs.c +index 9053c26..52fc3ac 100644 +--- a/quickjs.c ++++ b/quickjs.c +@@ -9062,6 +9062,13 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + return 0; + } + ++#ifdef USE_HOST_OBJECT ++/* NativeScript host-object exotic methods (defined in quickjs-api.c). Compared ++ by address below so the get_property handler can be treated as a non-masking ++ fallback for host objects only, leaving Proxy (js_proxy_get) authoritative. */ ++extern JSClassExoticMethods NapiHostObjectExoticMethods; ++#endif ++ + static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst this_obj, + bool throw_ref_error) +@@ -9070,6 +9077,11 @@ static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag; ++#ifdef USE_HOST_OBJECT ++ /* A host object whose get_property we deferred: the prototype chain is ++ consulted first, and this is called only if nothing was found. */ ++ JSObject *deferred_host = NULL; ++#endif + + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { +@@ -9174,14 +9186,27 @@ static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + 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; ++#ifdef USE_HOST_OBJECT ++ /* NativeScript host objects use get_property as a ++ NON-masking fallback: defer it so the prototype chain ++ (native methods / field accessors) is consulted first, ++ and call it only if nothing is found (see after loop). ++ Proxy and other exotics stay authoritative. */ ++ if (em == &NapiHostObjectExoticMethods) { ++ if (!deferred_host) ++ deferred_host = p; ++ } else ++#endif ++ { ++ 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; +@@ -9211,6 +9236,17 @@ static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + if (!p) + break; + } ++#ifdef USE_HOST_OBJECT ++ /* Nothing found on the prototype chain: now consult the deferred host ++ object's get_property (expandos, numeric indices, dynamic members). */ ++ if (deferred_host) { ++ JSValue obj1, retval; ++ obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, deferred_host)); ++ retval = NapiHostObjectExoticMethods.get_property(ctx, obj1, prop, this_obj); ++ JS_FreeValue(ctx, obj1); ++ return retval; ++ } ++#endif + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { +@@ -63992,6 +64028,32 @@ uintptr_t js_std_cmd(int cmd, ...) { + return rv; + } + ++/* CUSTOM */ ++ ++int JS_IsArrayBuffer2(JSContext *ctx, JSValueConst val) ++{ ++ JSObject *p; ++ if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { ++ p = JS_VALUE_GET_OBJ(val); ++ return p->class_id == JS_CLASS_ARRAY_BUFFER || p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER; ++ } else { ++ return false; ++ } ++} ++ ++JSValue JS_NewString16(JSContext *ctx, const uint16_t *buf, int len) ++{ ++ return js_new_string16_len(ctx, buf, len); ++} ++ ++JSValue JS_GetPropertyInt64_2(JSContext *ctx, JSValueConst obj, int64_t idx) { ++ return JS_GetPropertyInt64(ctx, obj, idx); ++} ++ ++JSValue JS_WeakRef_Deref(JSContext *ctx, JSValueConst this_val) { ++ return js_weakref_deref(ctx, this_val, 0, NULL); ++} ++ + #undef malloc + #undef free + #undef realloc +diff --git a/quickjs.h b/quickjs.h +index 6763f83..a8e0817 100644 +--- a/quickjs.h ++++ b/quickjs.h +@@ -1428,6 +1428,13 @@ JS_EXTERN int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *exp + JS_EXTERN int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); + ++/* CUSTOM */ ++ ++int JS_IsArrayBuffer2(JSContext *ctx, JSValueConst val); ++JSValue JS_NewString16(JSContext *ctx, const uint16_t *buf, int len); ++JSValue JS_GetPropertyInt64_2(JSContext *ctx, JSValueConst obj, int64_t idx); ++JSValue JS_WeakRef_Deref(JSContext *ctx, JSValueConst this_val); ++ + /* Version */ + + #define QJS_VERSION_MAJOR 0